killbill-memoizeit

Squashed commit of the following:

11/19/2012 11:12:19 PM

Changes

account/src/main/java/com/ning/billing/account/dao/AccountBinder.java 71(+0 -71)

account/src/main/java/com/ning/billing/account/dao/AccountEmailBinder.java 49(+0 -49)

account/src/main/java/com/ning/billing/account/dao/AccountEmailDao.java 43(+0 -43)

account/src/main/java/com/ning/billing/account/dao/AccountEmailHistoryBinder.java 54(+0 -54)

account/src/main/java/com/ning/billing/account/dao/AccountEmailMapper.java 39(+0 -39)

account/src/main/java/com/ning/billing/account/dao/AccountHistoryBinder.java 76(+0 -76)

account/src/main/java/com/ning/billing/account/dao/AccountMapper.java 75(+0 -75)

account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java 241(+0 -241)

account/src/main/java/com/ning/billing/account/dao/AuditedAccountEmailDao.java 164(+0 -164)

account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java 64(+0 -64)

account/src/test/java/com/ning/billing/account/dao/MockAccountEmailDao.java 103(+0 -103)

account/src/test/java/com/ning/billing/account/glue/AccountModuleWithMocks.java 42(+0 -42)

invoice/src/main/java/com/ning/billing/invoice/dao/AuditedInvoiceDao.java 976(+0 -976)

payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java 392(+0 -392)

payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptHistoryBinder.java 57(+0 -57)

payment/src/main/java/com/ning/billing/payment/dao/PaymentHistoryBinder.java 59(+0 -59)

payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodHistoryBinder.java 54(+0 -54)

payment/src/main/java/com/ning/billing/payment/dao/RefundHistoryBinder.java 59(+0 -59)

pom.xml 1(+0 -1)

tenant/src/main/java/com/ning/billing/tenant/dao/TenantBinder.java 51(+0 -51)

tenant/src/main/java/com/ning/billing/tenant/dao/TenantMapper.java 43(+0 -43)

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

util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java 119(+0 -119)

util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldBinder.java 49(+0 -49)

util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldHistoryBinder.java 52(+0 -52)

util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldMapper.java 38(+0 -38)

util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDao.java 40(+0 -40)

util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java 231(+0 -231)

util/src/main/java/com/ning/billing/util/dao/ChangeTypeBinder.java 45(+0 -45)

util/src/main/java/com/ning/billing/util/dao/CollectionHistorySqlDao.java 40(+0 -40)

util/src/main/java/com/ning/billing/util/dao/HistoryRecordIdMapper.java 33(+0 -33)

util/src/main/java/com/ning/billing/util/dao/ObjectTypeBinder.java 45(+0 -45)

util/src/main/java/com/ning/billing/util/dao/RecordIdMapper.java 34(+0 -34)

util/src/main/java/com/ning/billing/util/dao/TableNameBinder.java 45(+0 -45)

util/src/main/java/com/ning/billing/util/entity/collection/dao/EntityCollectionSqlDao.java 72(+0 -72)

util/src/main/java/com/ning/billing/util/entity/collection/dao/UpdatableEntityCollectionSqlDao.java 57(+0 -57)

util/src/main/java/com/ning/billing/util/entity/dao/UpdatableEntitySqlDao.java 37(+0 -37)

util/src/main/java/com/ning/billing/util/glue/TagDescriptionDaoProvider.java 39(+0 -39)

util/src/main/java/com/ning/billing/util/glue/TagStoreDaoProvider.java 39(+0 -39)

util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java 219(+0 -219)

util/src/main/java/com/ning/billing/util/tag/dao/TagBinder.java 48(+0 -48)

util/src/main/java/com/ning/billing/util/tag/dao/TagHistoryBinder.java 51(+0 -51)

util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java 55(+0 -55)

util/src/main/java/com/ning/billing/util/tag/DefaultTagStore.java 98(+0 -98)

util/src/main/resources/com/ning/billing/util/dao/AuditSqlDao.sql.stg 46(+0 -46)

util/src/test/java/com/ning/billing/util/tag/MockTagStoreModuleSql.java 39(+0 -39)

Details

diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java b/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
index cb2188f..7475d32 100644
--- a/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
+++ b/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
@@ -23,6 +23,7 @@ import javax.annotation.Nullable;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 
+import com.ning.billing.account.dao.AccountModelDao;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.junction.api.BlockingState;
 import com.ning.billing.util.entity.EntityBase;
@@ -63,10 +64,6 @@ public class DefaultAccount extends EntityBase implements Account {
     private final Boolean isMigrated;
     private final Boolean isNotifiedForInvoices;
 
-    public DefaultAccount(final AccountData data) {
-        this(UUID.randomUUID(), data);
-    }
-
     /**
      * This call is used to update an existing account
      *
@@ -129,6 +126,15 @@ public class DefaultAccount extends EntityBase implements Account {
         this.isNotifiedForInvoices = isNotifiedForInvoices;
     }
 
+    public DefaultAccount(final AccountModelDao accountModelDao) {
+        this(accountModelDao.getId(), accountModelDao.getCreatedDate(), accountModelDao.getUpdatedDate(), accountModelDao.getExternalKey(),
+             accountModelDao.getEmail(), accountModelDao.getName(), accountModelDao.getFirstNameLength(), accountModelDao.getCurrency(),
+             new DefaultBillCycleDay(accountModelDao.getBillingCycleDayLocal(), accountModelDao.getBillingCycleDayUtc()), accountModelDao.getPaymentMethodId(),
+             accountModelDao.getTimeZone(), accountModelDao.getLocale(), accountModelDao.getAddress1(), accountModelDao.getAddress2(),
+             accountModelDao.getCompanyName(), accountModelDao.getCity(), accountModelDao.getStateOrProvince(), accountModelDao.getCountry(),
+             accountModelDao.getPostalCode(), accountModelDao.getPhone(), accountModelDao.getMigrated(), accountModelDao.getIsNotifiedForInvoices());
+    }
+
     @Override
     public String getExternalKey() {
         return Objects.firstNonNull(externalKey, DEFAULT_STRING_VALUE);
diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultAccountEmail.java b/account/src/main/java/com/ning/billing/account/api/DefaultAccountEmail.java
index 4053a32..823d502 100644
--- a/account/src/main/java/com/ning/billing/account/api/DefaultAccountEmail.java
+++ b/account/src/main/java/com/ning/billing/account/api/DefaultAccountEmail.java
@@ -18,10 +18,11 @@ package com.ning.billing.account.api;
 
 import java.util.UUID;
 
-import com.ning.billing.util.entity.Entity;
+import com.ning.billing.account.dao.AccountEmailModelDao;
 import com.ning.billing.util.entity.EntityBase;
 
-public class DefaultAccountEmail extends EntityBase implements AccountEmail, Entity {
+public class DefaultAccountEmail extends EntityBase implements AccountEmail {
+
     private final UUID accountId;
     private final String email;
 
@@ -31,14 +32,10 @@ public class DefaultAccountEmail extends EntityBase implements AccountEmail, Ent
         this.email = email;
     }
 
-    public DefaultAccountEmail(final AccountEmail source, final String newEmail) {
-        this(source.getAccountId(), newEmail);
-    }
-
-    public DefaultAccountEmail(final UUID id, final UUID accountId, final String email) {
-        super(id);
-        this.accountId = accountId;
-        this.email = email;
+    public DefaultAccountEmail(final AccountEmailModelDao accountEmail) {
+        super(accountEmail.getId(), accountEmail.getCreatedDate(), accountEmail.getUpdatedDate());
+        this.accountId = accountEmail.getAccountId();
+        this.email = accountEmail.getEmail();
     }
 
     @Override
diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultAccountService.java b/account/src/main/java/com/ning/billing/account/api/DefaultAccountService.java
index 9b0cfc1..d630f70 100644
--- a/account/src/main/java/com/ning/billing/account/api/DefaultAccountService.java
+++ b/account/src/main/java/com/ning/billing/account/api/DefaultAccountService.java
@@ -16,7 +16,6 @@
 
 package com.ning.billing.account.api;
 
-
 public class DefaultAccountService implements AccountService {
 
     private static final String ACCOUNT_SERVICE_NAME = "account-service";
@@ -26,7 +25,7 @@ public class DefaultAccountService implements AccountService {
         return ACCOUNT_SERVICE_NAME;
     }
 
-//    @LifecycleHandlerType(LifecycleLevel.INIT_SERVICE)
-//    public void initialize() {
-//    }
+    //    @LifecycleHandlerType(LifecycleLevel.INIT_SERVICE)
+    //    public void initialize() {
+    //    }
 }
diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultBillCycleDay.java b/account/src/main/java/com/ning/billing/account/api/DefaultBillCycleDay.java
index e5587be..faa33e5 100644
--- a/account/src/main/java/com/ning/billing/account/api/DefaultBillCycleDay.java
+++ b/account/src/main/java/com/ning/billing/account/api/DefaultBillCycleDay.java
@@ -61,13 +61,21 @@ public class DefaultBillCycleDay implements BillCycleDay {
 
     @Override
     public boolean equals(final Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
 
         final DefaultBillCycleDay that = (DefaultBillCycleDay) o;
 
-        if (dayOfMonthLocal != that.dayOfMonthLocal) return false;
-        if (dayOfMonthUTC != that.dayOfMonthUTC) return false;
+        if (dayOfMonthLocal != that.dayOfMonthLocal) {
+            return false;
+        }
+        if (dayOfMonthUTC != that.dayOfMonthUTC) {
+            return false;
+        }
 
         return true;
     }
diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultChangedField.java b/account/src/main/java/com/ning/billing/account/api/DefaultChangedField.java
index 0b762fd..09a971e 100644
--- a/account/src/main/java/com/ning/billing/account/api/DefaultChangedField.java
+++ b/account/src/main/java/com/ning/billing/account/api/DefaultChangedField.java
@@ -47,7 +47,6 @@ public class DefaultChangedField implements ChangedField {
         this(fieldName, oldValue, newValue, new DateTime());
     }
 
-
     @Override
     public String getFieldName() {
         return fieldName;
@@ -73,13 +72,13 @@ public class DefaultChangedField implements ChangedField {
         final int prime = 31;
         int result = 1;
         result = prime * result
-                + ((changeDate == null) ? 0 : changeDate.hashCode());
+                 + ((changeDate == null) ? 0 : changeDate.hashCode());
         result = prime * result
-                + ((fieldName == null) ? 0 : fieldName.hashCode());
+                 + ((fieldName == null) ? 0 : fieldName.hashCode());
         result = prime * result
-                + ((newValue == null) ? 0 : newValue.hashCode());
+                 + ((newValue == null) ? 0 : newValue.hashCode());
         result = prime * result
-                + ((oldValue == null) ? 0 : oldValue.hashCode());
+                 + ((oldValue == null) ? 0 : oldValue.hashCode());
         return result;
     }
 
diff --git a/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java b/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java
index 8de582c..ed67f74 100644
--- a/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java
+++ b/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java
@@ -27,81 +27,90 @@ import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.AccountEmail;
 import com.ning.billing.account.api.DefaultAccount;
+import com.ning.billing.account.api.DefaultAccountEmail;
 import com.ning.billing.account.dao.AccountDao;
-import com.ning.billing.account.dao.AccountEmailDao;
+import com.ning.billing.account.dao.AccountEmailModelDao;
+import com.ning.billing.account.dao.AccountModelDao;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.entity.EntityPersistenceException;
 import com.ning.billing.util.svcapi.account.AccountInternalApi;
 
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+
 public class DefaultAccountInternalApi implements AccountInternalApi {
 
     private final AccountDao accountDao;
-    private final AccountEmailDao accountEmailDao;
 
     @Inject
-    public DefaultAccountInternalApi(final AccountDao accountDao, final AccountEmailDao accountEmailDao) {
+    public DefaultAccountInternalApi(final AccountDao accountDao) {
         this.accountDao = accountDao;
-        this.accountEmailDao = accountEmailDao;
     }
 
     @Override
-    public Account getAccountById(final UUID accountId, final InternalTenantContext context)
-            throws AccountApiException {
-        final Account account = accountDao.getById(accountId, context);
+    public Account getAccountById(final UUID accountId, final InternalTenantContext context) throws AccountApiException {
+        final AccountModelDao account = accountDao.getById(accountId, context);
         if (account == null) {
             throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, accountId);
         }
-        return account;
+        return new DefaultAccount(account);
     }
 
     @Override
-    public Account getAccountByRecordId(final Long recordId,
-                                        final InternalTenantContext context) throws AccountApiException {
-        return accountDao.getByRecordId(recordId, context);
+    public Account getAccountByRecordId(final Long recordId, final InternalTenantContext context) throws AccountApiException {
+        try {
+            final AccountModelDao account = accountDao.getByRecordId(recordId, context);
+            return new DefaultAccount(account);
+        } catch (NullPointerException e) {
+            return null;
+        }
     }
 
     @Override
     public void updateAccount(final String externalKey, final AccountData accountData,
                               final InternalCallContext context) throws AccountApiException {
-        try {
-            final Account account = getAccountByKey(externalKey, context);
-            final Account updatedAccount = new DefaultAccount(account.getId(), accountData);
-            accountDao.update(updatedAccount, context);
-        } catch (EntityPersistenceException e) {
-            throw new AccountApiException(e, ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID);
+        final Account currentAccount = getAccountByKey(externalKey, context);
+        if (currentAccount == null) {
+            throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_KEY, externalKey);
         }
+
+        // Set unspecified (null) fields to their current values
+        final Account updatedAccount = new DefaultAccount(currentAccount.getId(), accountData);
+        final AccountModelDao accountToUpdate = new AccountModelDao(currentAccount.getId(), updatedAccount.mergeWithDelegate(currentAccount));
+
+        accountDao.update(accountToUpdate, context);
     }
 
     @Override
     public List<AccountEmail> getEmails(final UUID accountId,
                                         final InternalTenantContext context) {
-        return accountEmailDao.getEmails(accountId, context);
+        return ImmutableList.<AccountEmail>copyOf(Collections2.transform(accountDao.getEmailsByAccountId(accountId, context),
+                                                                         new Function<AccountEmailModelDao, AccountEmail>() {
+                                                                             @Override
+                                                                             public AccountEmail apply(final AccountEmailModelDao input) {
+                                                                                 return new DefaultAccountEmail(input);
+                                                                             }
+                                                                         }));
     }
 
     @Override
-    public Account getAccountByKey(final String key, final InternalTenantContext context)
-            throws AccountApiException {
-        final Account account = accountDao.getAccountByKey(key, context);
-        if (account == null) {
+    public Account getAccountByKey(final String key, final InternalTenantContext context) throws AccountApiException {
+        final AccountModelDao accountModelDao = accountDao.getAccountByKey(key, context);
+        if (accountModelDao == null) {
             throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_KEY, key);
         }
-        return account;
+        return new DefaultAccount(accountModelDao);
     }
 
     @Override
-    public void removePaymentMethod(final UUID accountId, final InternalCallContext context)
-            throws AccountApiException {
+    public void removePaymentMethod(final UUID accountId, final InternalCallContext context) throws AccountApiException {
         updatePaymentMethod(accountId, null, context);
     }
 
     @Override
     public void updatePaymentMethod(final UUID accountId, final UUID paymentMethodId,
                                     final InternalCallContext context) throws AccountApiException {
-        try {
-            accountDao.updatePaymentMethod(accountId, paymentMethodId, context);
-        } catch (final EntityPersistenceException e) {
-            throw new AccountApiException(e, e.getCode(), e.getMessage());
-        }
+        accountDao.updatePaymentMethod(accountId, paymentMethodId, context);
     }
 }
diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeEvent.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeEvent.java
index 39cc93d..758b562 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeEvent.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeEvent.java
@@ -20,10 +20,10 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
-import com.ning.billing.account.api.Account;
-import com.ning.billing.util.events.ChangedField;
 import com.ning.billing.account.api.DefaultChangedField;
+import com.ning.billing.account.dao.AccountModelDao;
 import com.ning.billing.util.events.AccountChangeInternalEvent;
+import com.ning.billing.util.events.ChangedField;
 import com.ning.billing.util.events.DefaultBusInternalEvent;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
@@ -49,8 +49,8 @@ public class DefaultAccountChangeEvent extends DefaultBusInternalEvent implement
         this.changedFields = changedFields;
     }
 
-    public DefaultAccountChangeEvent(final UUID id, final UUID userToken, final Account oldData, final Account newData,
-            final Long accountRecordId, final Long tenantRecordId) {
+    public DefaultAccountChangeEvent(final UUID id, final UUID userToken, final AccountModelDao oldData, final AccountModelDao newData,
+                                     final Long accountRecordId, final Long tenantRecordId) {
         super(userToken, accountRecordId, tenantRecordId);
         this.accountId = id;
         this.userToken = userToken;
@@ -134,7 +134,7 @@ public class DefaultAccountChangeEvent extends DefaultBusInternalEvent implement
         return true;
     }
 
-    private List<ChangedField> calculateChangedFields(final Account oldData, final Account newData) {
+    private List<ChangedField> calculateChangedFields(final AccountModelDao oldData, final AccountModelDao newData) {
 
         final List<ChangedField> tmpChangedFields = new ArrayList<ChangedField>();
 
@@ -152,8 +152,12 @@ public class DefaultAccountChangeEvent extends DefaultBusInternalEvent implement
                           (newData.getCurrency() != null) ? newData.getCurrency().toString() : null);
 
         addIfValueChanged(tmpChangedFields,
-                          "billCycleDay",
-                          oldData.getBillCycleDay().toString(), newData.getBillCycleDay().toString());
+                          "billCycleDayLocal",
+                          String.valueOf(oldData.getBillingCycleDayLocal()), String.valueOf(newData.getBillingCycleDayLocal()));
+
+        addIfValueChanged(tmpChangedFields,
+                          "billCycleDayUTC",
+                          String.valueOf(oldData.getBillingCycleDayUtc()), String.valueOf(newData.getBillingCycleDayUtc()));
 
         addIfValueChanged(tmpChangedFields, "paymentMethodId",
                           (oldData.getPaymentMethodId() != null) ? oldData.getPaymentMethodId().toString() : null,
diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java
index ba2210f..6fddef1 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java
@@ -20,10 +20,10 @@ import java.util.UUID;
 
 import org.joda.time.DateTimeZone;
 
-import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.BillCycleDay;
 import com.ning.billing.account.api.DefaultBillCycleDay;
+import com.ning.billing.account.dao.AccountModelDao;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.util.events.AccountCreationInternalEvent;
 import com.ning.billing.util.events.DefaultBusInternalEvent;
@@ -32,7 +32,7 @@ import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
 
-public class DefaultAccountCreationEvent  extends DefaultBusInternalEvent implements AccountCreationInternalEvent {
+public class DefaultAccountCreationEvent extends DefaultBusInternalEvent implements AccountCreationInternalEvent {
 
     private final UUID id;
     private final AccountData data;
@@ -48,7 +48,7 @@ public class DefaultAccountCreationEvent  extends DefaultBusInternalEvent implem
         this.data = data;
     }
 
-    public DefaultAccountCreationEvent(final Account data, final UUID userToken, final Long accountRecordId, final Long tenantRecordId) {
+    public DefaultAccountCreationEvent(final AccountModelDao data, final UUID userToken, final Long accountRecordId, final Long tenantRecordId) {
         super(userToken, accountRecordId, tenantRecordId);
         this.id = data.getId();
         this.data = new DefaultAccountData(data);
@@ -70,7 +70,6 @@ public class DefaultAccountCreationEvent  extends DefaultBusInternalEvent implem
         return data;
     }
 
-
     @Override
     public int hashCode() {
         final int prime = 31;
@@ -109,8 +108,8 @@ public class DefaultAccountCreationEvent  extends DefaultBusInternalEvent implem
         return true;
     }
 
-
     public static class DefaultAccountData implements AccountData {
+
         private final String externalKey;
         private final String name;
         private final Integer firstNameLength;
@@ -131,12 +130,12 @@ public class DefaultAccountCreationEvent  extends DefaultBusInternalEvent implem
         private final boolean isMigrated;
         private final boolean isNotifiedForInvoices;
 
-        public DefaultAccountData(final Account d) {
+        public DefaultAccountData(final AccountModelDao d) {
             this(d.getExternalKey() != null ? d.getExternalKey() : null,
                  d.getName(),
                  d.getFirstNameLength(),
                  d.getEmail(),
-                 new DefaultBillCycleDay(d.getBillCycleDay()),
+                 new DefaultBillCycleDay(d.getBillingCycleDayLocal(), d.getBillingCycleDayUtc()),
                  d.getCurrency() != null ? d.getCurrency().name() : null,
                  d.getPaymentMethodId(),
                  d.getTimeZone() != null ? d.getTimeZone().getID() : null,
@@ -149,8 +148,8 @@ public class DefaultAccountCreationEvent  extends DefaultBusInternalEvent implem
                  d.getPostalCode(),
                  d.getCountry(),
                  d.getPhone(),
-                 d.isMigrated(),
-                 d.isNotifiedForInvoices());
+                 d.getMigrated(),
+                 d.getIsNotifiedForInvoices());
         }
 
         @JsonCreator
@@ -303,41 +302,41 @@ public class DefaultAccountCreationEvent  extends DefaultBusInternalEvent implem
             final int prime = 31;
             int result = 1;
             result = prime * result
-                    + ((address1 == null) ? 0 : address1.hashCode());
+                     + ((address1 == null) ? 0 : address1.hashCode());
             result = prime * result
-                    + ((address2 == null) ? 0 : address2.hashCode());
+                     + ((address2 == null) ? 0 : address2.hashCode());
             result = prime * result
-                    + ((billCycleDay == null) ? 0 : billCycleDay.hashCode());
+                     + ((billCycleDay == null) ? 0 : billCycleDay.hashCode());
             result = prime * result + ((city == null) ? 0 : city.hashCode());
             result = prime * result
-                    + ((companyName == null) ? 0 : companyName.hashCode());
+                     + ((companyName == null) ? 0 : companyName.hashCode());
             result = prime * result
-                    + ((country == null) ? 0 : country.hashCode());
+                     + ((country == null) ? 0 : country.hashCode());
             result = prime * result
-                    + ((currency == null) ? 0 : currency.hashCode());
+                     + ((currency == null) ? 0 : currency.hashCode());
             result = prime * result + ((email == null) ? 0 : email.hashCode());
             result = prime * result
-                    + ((externalKey == null) ? 0 : externalKey.hashCode());
+                     + ((externalKey == null) ? 0 : externalKey.hashCode());
             result = prime
-                    * result
-                    + ((firstNameLength == null) ? 0 : firstNameLength
+                     * result
+                     + ((firstNameLength == null) ? 0 : firstNameLength
                     .hashCode());
             result = prime * result
-                    + ((locale == null) ? 0 : locale.hashCode());
+                     + ((locale == null) ? 0 : locale.hashCode());
             result = prime * result + ((name == null) ? 0 : name.hashCode());
             result = prime
-                    * result
-                    + ((paymentMethodId == null) ? 0 : paymentMethodId
+                     * result
+                     + ((paymentMethodId == null) ? 0 : paymentMethodId
                     .hashCode());
             result = prime * result + ((phone == null) ? 0 : phone.hashCode());
             result = prime * result
-                    + ((postalCode == null) ? 0 : postalCode.hashCode());
+                     + ((postalCode == null) ? 0 : postalCode.hashCode());
             result = prime
-                    * result
-                    + ((stateOrProvince == null) ? 0 : stateOrProvince
+                     * result
+                     + ((stateOrProvince == null) ? 0 : stateOrProvince
                     .hashCode());
             result = prime * result
-                    + ((timeZone == null) ? 0 : timeZone.hashCode());
+                     + ((timeZone == null) ? 0 : timeZone.hashCode());
             return result;
         }
 
diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
index b1e3353..0403101 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
@@ -19,6 +19,8 @@ package com.ning.billing.account.api.user;
 import java.util.List;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
+
 import org.joda.time.DateTime;
 
 import com.ning.billing.ErrorCode;
@@ -31,13 +33,16 @@ import com.ning.billing.account.api.DefaultAccount;
 import com.ning.billing.account.api.DefaultAccountEmail;
 import com.ning.billing.account.api.MigrationAccountData;
 import com.ning.billing.account.dao.AccountDao;
-import com.ning.billing.account.dao.AccountEmailDao;
+import com.ning.billing.account.dao.AccountEmailModelDao;
+import com.ning.billing.account.dao.AccountModelDao;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextFactory;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.TenantContext;
-import com.ning.billing.util.entity.EntityPersistenceException;
 
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
 public class DefaultAccountUserApi implements AccountUserApi {
@@ -45,51 +50,57 @@ public class DefaultAccountUserApi implements AccountUserApi {
     private final CallContextFactory callContextFactory;
     private final InternalCallContextFactory internalCallContextFactory;
     private final AccountDao accountDao;
-    private final AccountEmailDao accountEmailDao;
 
     @Inject
     public DefaultAccountUserApi(final CallContextFactory callContextFactory, final InternalCallContextFactory internalCallContextFactory,
-                                 final AccountDao accountDao, final AccountEmailDao accountEmailDao) {
+                                 final AccountDao accountDao) {
         this.callContextFactory = callContextFactory;
         this.internalCallContextFactory = internalCallContextFactory;
         this.accountDao = accountDao;
-        this.accountEmailDao = accountEmailDao;
     }
 
     @Override
     public Account createAccount(final AccountData data, final CallContext context) throws AccountApiException {
-        final Account account = new DefaultAccount(data);
-
-        try {
-            accountDao.create(account, internalCallContextFactory.createInternalCallContext(account.getId(), context));
-        } catch (EntityPersistenceException e) {
-            throw new AccountApiException(e, ErrorCode.ACCOUNT_CREATION_FAILED);
+        // Not transactional, but there is a db constraint on that column
+        if (getIdFromKey(data.getExternalKey(), context) != null) {
+            throw new AccountApiException(ErrorCode.ACCOUNT_ALREADY_EXISTS, data.getExternalKey());
         }
 
-        return account;
+        final AccountModelDao account = new AccountModelDao(data);
+        accountDao.create(account, internalCallContextFactory.createInternalCallContext(account.getId(), context));
+
+        return new DefaultAccount(account);
     }
 
     @Override
     public Account getAccountByKey(final String key, final TenantContext context) throws AccountApiException {
-        final Account account = accountDao.getAccountByKey(key, internalCallContextFactory.createInternalTenantContext(context));
+        final AccountModelDao account = accountDao.getAccountByKey(key, internalCallContextFactory.createInternalTenantContext(context));
         if (account == null) {
             throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_KEY, key);
         }
-        return account;
+
+        return new DefaultAccount(account);
     }
 
     @Override
     public Account getAccountById(final UUID id, final TenantContext context) throws AccountApiException {
-        final Account account = accountDao.getById(id, internalCallContextFactory.createInternalTenantContext(context));
+        final AccountModelDao account = accountDao.getById(id, internalCallContextFactory.createInternalTenantContext(context));
         if (account == null) {
             throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, id);
         }
-        return account;
+
+        return new DefaultAccount(account);
     }
 
     @Override
     public List<Account> getAccounts(final TenantContext context) {
-        return accountDao.get(internalCallContextFactory.createInternalTenantContext(context));
+        final List<AccountModelDao> accountModelDaos = accountDao.get(internalCallContextFactory.createInternalTenantContext(context));
+        return ImmutableList.<Account>copyOf(Collections2.transform(accountModelDaos, new Function<AccountModelDao, Account>() {
+            @Override
+            public Account apply(@Nullable final AccountModelDao input) {
+                return new DefaultAccount(input);
+            }
+        }));
     }
 
     @Override
@@ -99,51 +110,53 @@ public class DefaultAccountUserApi implements AccountUserApi {
 
     @Override
     public void updateAccount(final Account account, final CallContext context) throws AccountApiException {
-        try {
-            accountDao.update(account, internalCallContextFactory.createInternalCallContext(account.getId(), context));
-        } catch (EntityPersistenceException e) {
-            throw new AccountApiException(e, ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, account.getId());
-        }
-
+        updateAccount(account.getId(), account, context);
     }
 
     @Override
-    public void updateAccount(final UUID accountId, final AccountData accountData, final CallContext context)
-            throws AccountApiException {
-        try {
-            final Account account = new DefaultAccount(accountId, accountData);
-            accountDao.update(account, internalCallContextFactory.createInternalCallContext(account.getId(), context));
-        } catch (EntityPersistenceException e) {
-            throw new AccountApiException(e, ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID);
-        } catch (RuntimeException e /* EntityPersistenceException */) {
-            throw new AccountApiException(e, ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, accountId);
+    public void updateAccount(final UUID accountId, final AccountData accountData, final CallContext context) throws AccountApiException {
+        final Account currentAccount = getAccountById(accountId, context);
+        if (currentAccount == null) {
+            throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, accountId);
         }
+
+        updateAccount(currentAccount, accountData, context);
     }
 
     @Override
     public void updateAccount(final String externalKey, final AccountData accountData, final CallContext context) throws AccountApiException {
-        final UUID accountId = getIdFromKey(externalKey, context);
-        if (accountId == null) {
+        final Account currentAccount = getAccountByKey(externalKey, context);
+        if (currentAccount == null) {
             throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_KEY, externalKey);
         }
-        updateAccount(accountId, accountData, context);
+
+        updateAccount(currentAccount, accountData, context);
+    }
+
+    private void updateAccount(final Account currentAccount, final AccountData accountData, final CallContext context) throws AccountApiException {
+        // Set unspecified (null) fields to their current values
+        final Account updatedAccount = new DefaultAccount(currentAccount.getId(), accountData);
+        final AccountModelDao accountToUpdate = new AccountModelDao(currentAccount.getId(), updatedAccount.mergeWithDelegate(currentAccount));
+
+        accountDao.update(accountToUpdate, internalCallContextFactory.createInternalCallContext(accountToUpdate.getId(), context));
     }
 
     @Override
-    public Account migrateAccount(final MigrationAccountData data, final CallContext context)
-            throws AccountApiException {
+    public Account migrateAccount(final MigrationAccountData data, final CallContext context) throws AccountApiException {
+        // Create a special (migration) context
         final DateTime createdDate = data.getCreatedDate() == null ? context.getCreatedDate() : data.getCreatedDate();
         final DateTime updatedDate = data.getUpdatedDate() == null ? context.getUpdatedDate() : data.getUpdatedDate();
         final CallContext migrationContext = callContextFactory.toMigrationCallContext(context, createdDate, updatedDate);
-        final Account account = new DefaultAccount(data);
 
-        try {
-            accountDao.create(account, internalCallContextFactory.createInternalCallContext(account.getId(), migrationContext));
+        // Create the account
+        final Account account = createAccount(data, migrationContext);
+
+        // Add associated contact emails
+        // In Killbill, we never return null for empty lists, but MigrationAccountData is implemented outside of Killbill
+        if (data.getAdditionalContactEmails() != null) {
             for (final String cur : data.getAdditionalContactEmails()) {
                 addEmail(account.getId(), new DefaultAccountEmail(account.getId(), cur), migrationContext);
             }
-        } catch (EntityPersistenceException e) {
-            throw new AccountApiException(e, ErrorCode.ACCOUNT_CREATION_FAILED);
         }
 
         return account;
@@ -151,16 +164,22 @@ public class DefaultAccountUserApi implements AccountUserApi {
 
     @Override
     public List<AccountEmail> getEmails(final UUID accountId, final TenantContext context) {
-        return accountEmailDao.getEmails(accountId, internalCallContextFactory.createInternalTenantContext(context));
+        return ImmutableList.<AccountEmail>copyOf(Collections2.transform(accountDao.getEmailsByAccountId(accountId, internalCallContextFactory.createInternalTenantContext(context)),
+                                                                         new Function<AccountEmailModelDao, AccountEmail>() {
+                                                                             @Override
+                                                                             public AccountEmail apply(final AccountEmailModelDao input) {
+                                                                                 return new DefaultAccountEmail(input);
+                                                                             }
+                                                                         }));
     }
 
     @Override
-    public void addEmail(final UUID accountId, final AccountEmail email, final CallContext context) {
-        accountEmailDao.addEmail(accountId, email, internalCallContextFactory.createInternalCallContext(accountId, context));
+    public void addEmail(final UUID accountId, final AccountEmail email, final CallContext context) throws AccountApiException {
+        accountDao.addEmail(new AccountEmailModelDao(email), internalCallContextFactory.createInternalCallContext(accountId, context));
     }
 
     @Override
     public void removeEmail(final UUID accountId, final AccountEmail email, final CallContext context) {
-        accountEmailDao.removeEmail(accountId, email, internalCallContextFactory.createInternalCallContext(accountId, context));
+        accountDao.removeEmail(new AccountEmailModelDao(email, false), internalCallContextFactory.createInternalCallContext(accountId, context));
     }
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
index ee9edb0..8ab5f98 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
@@ -16,19 +16,18 @@
 
 package com.ning.billing.account.dao;
 
+import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.entity.EntityPersistenceException;
 import com.ning.billing.util.entity.dao.EntityDao;
-import com.ning.billing.util.entity.dao.UpdatableEntityDao;
 
-public interface AccountDao extends EntityDao<Account> {
+public interface AccountDao extends EntityDao<AccountModelDao, Account, AccountApiException> {
 
-    public Account getAccountByKey(String key, InternalTenantContext context);
+    public AccountModelDao getAccountByKey(String key, InternalTenantContext context);
 
     /**
      * @throws AccountApiException when externalKey is null
@@ -39,7 +38,13 @@ public interface AccountDao extends EntityDao<Account> {
      * @param accountId       the id of the account
      * @param paymentMethodId the is of the current default paymentMethod
      */
-    public void updatePaymentMethod(UUID accountId, UUID paymentMethodId, InternalCallContext context) throws EntityPersistenceException;
+    public void updatePaymentMethod(UUID accountId, UUID paymentMethodId, InternalCallContext context) throws AccountApiException;
 
-    public void update(Account account, InternalCallContext context) throws EntityPersistenceException;
+    public void update(AccountModelDao account, InternalCallContext context) throws AccountApiException;
+
+    public void addEmail(AccountEmailModelDao email, InternalCallContext context) throws AccountApiException;
+
+    public void removeEmail(AccountEmailModelDao email, InternalCallContext context);
+
+    public List<AccountEmailModelDao> getEmailsByAccountId(UUID accountId, InternalTenantContext context);
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountEmailModelDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountEmailModelDao.java
new file mode 100644
index 0000000..375ea7f
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountEmailModelDao.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2010-2012 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.account.dao;
+
+import java.util.UUID;
+
+import com.ning.billing.account.api.AccountEmail;
+import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.dao.EntityModelDao;
+
+public class AccountEmailModelDao extends EntityBase implements EntityModelDao<AccountEmail> {
+
+    private UUID accountId;
+    private String email;
+    private Boolean isActive;
+
+    public AccountEmailModelDao() { /* For the DAO mapper */ }
+
+    public AccountEmailModelDao(final AccountEmail email) {
+        this(email, true);
+    }
+
+    public AccountEmailModelDao(final AccountEmail email, final boolean isActive) {
+        super(email.getId(), email.getCreatedDate(), email.getUpdatedDate());
+        this.accountId = email.getAccountId();
+        this.email = email.getEmail();
+        this.isActive = isActive;
+    }
+
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public Boolean getIsActive() {
+        return isActive;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("AccountEmailModelDao");
+        sb.append("{accountId=").append(accountId);
+        sb.append(", email='").append(email).append('\'');
+        sb.append(", isActive=").append(isActive);
+        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 AccountEmailModelDao that = (AccountEmailModelDao) o;
+
+        if (isActive != that.isActive) {
+            return false;
+        }
+        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
+            return false;
+        }
+        if (email != null ? !email.equals(that.email) : that.email != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (accountId != null ? accountId.hashCode() : 0);
+        result = 31 * result + (email != null ? email.hashCode() : 0);
+        result = 31 * result + (isActive ? 1 : 0);
+        return result;
+    }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.ACCOUNT_EMAIL;
+    }
+}
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountEmailSqlDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountEmailSqlDao.java
index d001633..90aa7c0 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountEmailSqlDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountEmailSqlDao.java
@@ -16,53 +16,31 @@
 
 package com.ning.billing.account.dao;
 
-import java.util.Collection;
 import java.util.List;
+import java.util.UUID;
 
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.SqlBatch;
-import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+import org.skife.jdbi.v2.sqlobject.BindBean;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 
-import com.ning.billing.ObjectType;
 import com.ning.billing.account.api.AccountEmail;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
-import com.ning.billing.util.dao.EntityHistory;
-import com.ning.billing.util.dao.ObjectTypeBinder;
-import com.ning.billing.util.entity.collection.dao.UpdatableEntityCollectionSqlDao;
-
-@ExternalizedSqlViaStringTemplate3
-@RegisterMapper(AccountEmailMapper.class)
-public interface AccountEmailSqlDao extends UpdatableEntityCollectionSqlDao<AccountEmail>, Transactional<AccountEmailSqlDao>, Transmogrifier {
-
-    @Override
-    @SqlBatch
-    public void insertFromTransaction(@Bind("objectId") final String objectId,
-                                      @ObjectTypeBinder final ObjectType objectType,
-                                      @AccountEmailBinder final Collection<AccountEmail> entities,
-                                      @InternalTenantContextBinder final InternalCallContext context);
-
-    @Override
-    @SqlBatch
-    public void updateFromTransaction(@Bind("objectId") final String objectId,
-                                      @ObjectTypeBinder final ObjectType objectType,
-                                      @AccountEmailBinder final Collection<AccountEmail> entities,
-                                      @InternalTenantContextBinder final InternalCallContext context);
-
-    @Override
-    @SqlBatch
-    public void deleteFromTransaction(@Bind("objectId") final String objectId,
-                                      @ObjectTypeBinder final ObjectType objectType,
-                                      @AccountEmailBinder final Collection<AccountEmail> entities,
-                                      @InternalTenantContextBinder final InternalCallContext context);
-
-    @Override
-    @SqlBatch
-    public void addHistoryFromTransaction(@Bind("objectId") final String objectId,
-                                          @ObjectTypeBinder final ObjectType objectType,
-                                          @AccountEmailHistoryBinder final List<EntityHistory<AccountEmail>> entities,
-                                          @InternalTenantContextBinder final InternalCallContext context);
+import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.entity.dao.Audited;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
+
+@EntitySqlDaoStringTemplate
+public interface AccountEmailSqlDao extends EntitySqlDao<AccountEmailModelDao, AccountEmail> {
+
+    @SqlUpdate
+    @Audited(ChangeType.DELETE)
+    public void markEmailAsDeleted(@BindBean final AccountEmailModelDao accountEmail,
+                                   @BindBean final InternalCallContext context);
+
+    @SqlQuery
+    public List<AccountEmailModelDao> getEmailByAccountId(@Bind("accountId") final UUID accountId,
+                                                          @BindBean final InternalTenantContext context);
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountModelDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountModelDao.java
new file mode 100644
index 0000000..95e6aaa
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountModelDao.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2010-2012 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.account.dao;
+
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountData;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.dao.EntityModelDao;
+
+public class AccountModelDao extends EntityBase implements EntityModelDao<Account> {
+
+    private String externalKey;
+    private String email;
+    private String name;
+    private Integer firstNameLength;
+    private Currency currency;
+    private int billingCycleDayLocal;
+    private int billingCycleDayUtc;
+    private UUID paymentMethodId;
+    private DateTimeZone 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 isNotifiedForInvoices;
+
+    public AccountModelDao() { /* For the DAO mapper */ }
+
+    public AccountModelDao(final UUID id, final DateTime createdDate, final DateTime updatedDate, final String externalKey,
+                           final String email, final String name, final Integer firstNameLength, final Currency currency,
+                           final int billingCycleDayLocal, final int billingCycleDayUtc, final UUID paymentMethodId, final DateTimeZone 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 migrated, final Boolean notifiedForInvoices) {
+        super(id, createdDate, updatedDate);
+        this.externalKey = externalKey;
+        this.email = email;
+        this.name = name;
+        this.firstNameLength = firstNameLength;
+        this.currency = currency;
+        this.billingCycleDayLocal = billingCycleDayLocal;
+        this.billingCycleDayUtc = billingCycleDayUtc;
+        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 = migrated;
+        this.isNotifiedForInvoices = notifiedForInvoices;
+    }
+
+    public AccountModelDao(final UUID id, @Nullable final DateTime createdDate, final DateTime updatedDate, final AccountData account) {
+        this(id, createdDate, updatedDate, account.getExternalKey(),
+             account.getEmail(), account.getName(), account.getFirstNameLength(), account.getCurrency(), account.getBillCycleDay() == null ? 0 : account.getBillCycleDay().getDayOfMonthLocal(),
+             account.getBillCycleDay() == null ? 0 : account.getBillCycleDay().getDayOfMonthUTC(), account.getPaymentMethodId(), account.getTimeZone(), account.getLocale(), account.getAddress1(), account.getAddress2(),
+             account.getCompanyName(), account.getCity(), account.getStateOrProvince(), account.getCountry(), account.getPostalCode(),
+             account.getPhone(), account.isMigrated(), account.isNotifiedForInvoices());
+    }
+
+    public AccountModelDao(final UUID id, final AccountData account) {
+        this(id, null, null, account);
+    }
+
+    public AccountModelDao(final AccountData account) {
+        this(UUID.randomUUID(), account);
+    }
+
+    public String getExternalKey() {
+        return externalKey;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Integer getFirstNameLength() {
+        return firstNameLength;
+    }
+
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    public int getBillingCycleDayLocal() {
+        return billingCycleDayLocal;
+    }
+
+    public int getBillingCycleDayUtc() {
+        return billingCycleDayUtc;
+    }
+
+    public UUID getPaymentMethodId() {
+        return paymentMethodId;
+    }
+
+    public DateTimeZone 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;
+    }
+
+    // TODO Required for making the BindBeanFactory with Introspector work
+    // see Introspector line 571; they look at public method.
+    public Boolean getIsNotifiedForInvoices() {
+        return isNotifiedForInvoices;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("AccountModelDao");
+        sb.append("{externalKey='").append(externalKey).append('\'');
+        sb.append(", email='").append(email).append('\'');
+        sb.append(", name='").append(name).append('\'');
+        sb.append(", firstNameLength=").append(firstNameLength);
+        sb.append(", currency=").append(currency);
+        sb.append(", billingCycleDayLocal=").append(billingCycleDayLocal);
+        sb.append(", billingCycleDayUTC=").append(billingCycleDayUtc);
+        sb.append(", paymentMethodId=").append(paymentMethodId);
+        sb.append(", timeZone=").append(timeZone);
+        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(", isNotifiedForInvoices=").append(isNotifiedForInvoices);
+        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 AccountModelDao that = (AccountModelDao) o;
+
+        if (billingCycleDayLocal != that.billingCycleDayLocal) {
+            return false;
+        }
+        if (billingCycleDayUtc != that.billingCycleDayUtc) {
+            return false;
+        }
+        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 (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 != that.currency) {
+            return false;
+        }
+        if (email != null ? !email.equals(that.email) : that.email != null) {
+            return false;
+        }
+        if (externalKey != null ? !externalKey.equals(that.externalKey) : that.externalKey != null) {
+            return false;
+        }
+        if (firstNameLength != null ? !firstNameLength.equals(that.firstNameLength) : that.firstNameLength != null) {
+            return false;
+        }
+        if (migrated != null ? !migrated.equals(that.migrated) : that.migrated != null) {
+            return false;
+        }
+        if (isNotifiedForInvoices != null ? !isNotifiedForInvoices.equals(that.isNotifiedForInvoices) : that.isNotifiedForInvoices != null) {
+            return false;
+        }
+        if (locale != null ? !locale.equals(that.locale) : that.locale != null) {
+            return false;
+        }
+        if (name != null ? !name.equals(that.name) : that.name != 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;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (externalKey != null ? externalKey.hashCode() : 0);
+        result = 31 * result + (email != null ? email.hashCode() : 0);
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        result = 31 * result + (firstNameLength != null ? firstNameLength.hashCode() : 0);
+        result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + billingCycleDayLocal;
+        result = 31 * result + billingCycleDayUtc;
+        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 + (isNotifiedForInvoices != null ? isNotifiedForInvoices.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.ACCOUNT;
+    }
+}
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
index aa08ee5..1ee1c71 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
@@ -19,50 +19,37 @@ package com.ning.billing.account.dao;
 import java.util.UUID;
 
 import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 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.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 
 import com.ning.billing.account.api.Account;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.EntityHistory;
-import com.ning.billing.util.dao.UuidMapper;
-import com.ning.billing.util.entity.dao.UpdatableEntitySqlDao;
+import com.ning.billing.util.entity.dao.Audited;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 
-@ExternalizedSqlViaStringTemplate3
-@RegisterMapper({UuidMapper.class, AccountMapper.class})
-public interface AccountSqlDao extends UpdatableEntitySqlDao<Account>, Transactional<AccountSqlDao>, Transmogrifier {
+@EntitySqlDaoStringTemplate
+public interface AccountSqlDao extends EntitySqlDao<AccountModelDao, Account> {
 
     @SqlQuery
-    public Account getAccountByKey(@Bind("externalKey") final String key,
-                                   @InternalTenantContextBinder final InternalTenantContext context);
+    public AccountModelDao getAccountByKey(@Bind("externalKey") final String key,
+                                           @BindBean final InternalTenantContext context);
 
     @SqlQuery
     public UUID getIdFromKey(@Bind("externalKey") final String key,
-                             @InternalTenantContextBinder final InternalTenantContext context);
+                             @BindBean final InternalTenantContext context);
 
-    @Override
     @SqlUpdate
-    public void create(@AccountBinder Account account,
-                       @InternalTenantContextBinder final InternalCallContext context);
-
-    @Override
-    @SqlUpdate
-    public void update(@AccountBinder Account account,
-                       @InternalTenantContextBinder final InternalCallContext context);
+    @Audited(ChangeType.UPDATE)
+    public void update(@BindBean final AccountModelDao account,
+                       @BindBean final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     public void updatePaymentMethod(@Bind("id") String accountId,
                                     @Bind("paymentMethodId") String paymentMethodId,
-                                    @InternalTenantContextBinder final InternalCallContext context);
-
-    @Override
-    @SqlUpdate
-    public void insertHistoryFromTransaction(@AccountHistoryBinder final EntityHistory<Account> account,
-                                             @InternalTenantContextBinder final InternalCallContext context);
+                                    @BindBean final InternalCallContext context);
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java b/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
new file mode 100644
index 0000000..a5d6fd4
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2010-2012 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.account.dao;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.skife.jdbi.v2.IDBI;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.account.api.user.DefaultAccountChangeEvent;
+import com.ning.billing.account.api.user.DefaultAccountCreationEvent;
+import com.ning.billing.util.audit.ChangeType;
+import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.entity.EntityPersistenceException;
+import com.ning.billing.util.entity.dao.EntityDaoBase;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+import com.ning.billing.util.events.AccountChangeInternalEvent;
+import com.ning.billing.util.events.AccountCreationInternalEvent;
+import com.ning.billing.util.svcsapi.bus.InternalBus;
+import com.ning.billing.util.svcsapi.bus.InternalBus.EventBusException;
+
+import com.google.inject.Inject;
+
+public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, AccountApiException> implements AccountDao {
+
+    private static final Logger log = LoggerFactory.getLogger(DefaultAccountDao.class);
+
+    private final InternalBus eventBus;
+    private final InternalCallContextFactory internalCallContextFactory;
+
+    @Inject
+    public DefaultAccountDao(final IDBI dbi, final InternalBus eventBus, final InternalCallContextFactory internalCallContextFactory) {
+        super(new EntitySqlDaoTransactionalJdbiWrapper(dbi), AccountSqlDao.class);
+        this.eventBus = eventBus;
+        this.internalCallContextFactory = internalCallContextFactory;
+    }
+
+    @Override
+    protected AccountApiException generateAlreadyExistsException(final AccountModelDao account, final InternalCallContext context) {
+        return new AccountApiException(ErrorCode.ACCOUNT_ALREADY_EXISTS, account.getExternalKey());
+    }
+
+    @Override
+    protected void postBusEventFromTransaction(final AccountModelDao account, final AccountModelDao savedAccount, final ChangeType changeType,
+                                               final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalCallContext context) throws BillingExceptionBase {
+        // This is only called for the create call (see update below)
+        switch (changeType) {
+            case INSERT:
+                break;
+            default:
+                return;
+        }
+
+        final Long recordId = entitySqlDaoWrapperFactory.become(AccountSqlDao.class).getRecordId(savedAccount.getId().toString(), context);
+        // We need to re-hydrate the context with the account record id
+        final InternalCallContext rehydratedContext = internalCallContextFactory.createInternalCallContext(recordId, context);
+        final AccountCreationInternalEvent creationEvent = new DefaultAccountCreationEvent(savedAccount,
+                                                                                           rehydratedContext.getUserToken(),
+                                                                                           context.getAccountRecordId(),
+                                                                                           context.getTenantRecordId());
+        try {
+            eventBus.postFromTransaction(creationEvent, entitySqlDaoWrapperFactory, rehydratedContext);
+        } catch (final EventBusException e) {
+            log.warn("Failed to post account creation event for account " + savedAccount.getId(), e);
+        }
+    }
+
+    @Override
+    public AccountModelDao getAccountByKey(final String key, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<AccountModelDao>() {
+            @Override
+            public AccountModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(AccountSqlDao.class).getAccountByKey(key, context);
+            }
+        });
+    }
+
+    @Override
+    public UUID getIdFromKey(final String externalKey, final InternalTenantContext context) throws AccountApiException {
+        if (externalKey == null) {
+            throw new AccountApiException(ErrorCode.ACCOUNT_CANNOT_MAP_NULL_KEY, "");
+        }
+
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<UUID>() {
+            @Override
+            public UUID inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(AccountSqlDao.class).getIdFromKey(externalKey, context);
+            }
+        });
+    }
+
+    @Override
+    public void update(final AccountModelDao specifiedAccount, final InternalCallContext context) throws AccountApiException {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws EventBusException, AccountApiException {
+                final AccountSqlDao transactional = entitySqlDaoWrapperFactory.become(AccountSqlDao.class);
+
+                final UUID accountId = specifiedAccount.getId();
+                final AccountModelDao currentAccount = transactional.getById(accountId.toString(), context);
+                if (currentAccount == null) {
+                    throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, accountId);
+                }
+
+                transactional.update(specifiedAccount, context);
+
+                final AccountChangeInternalEvent changeEvent = new DefaultAccountChangeEvent(accountId,
+                                                                                             context.getUserToken(),
+                                                                                             currentAccount,
+                                                                                             specifiedAccount,
+                                                                                             context.getAccountRecordId(),
+                                                                                             context.getTenantRecordId());
+                if (changeEvent.hasChanges()) {
+                    try {
+                        eventBus.postFromTransaction(changeEvent, entitySqlDaoWrapperFactory, context);
+                    } catch (final EventBusException e) {
+                        log.warn("Failed to post account change event for account " + accountId, e);
+                    }
+                }
+
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public void updatePaymentMethod(final UUID accountId, final UUID paymentMethodId, final InternalCallContext context) throws AccountApiException {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws EntityPersistenceException, EventBusException {
+                final AccountSqlDao transactional = entitySqlDaoWrapperFactory.become(AccountSqlDao.class);
+
+                final AccountModelDao currentAccount = transactional.getById(accountId.toString(), context);
+                if (currentAccount == null) {
+                    throw new EntityPersistenceException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, accountId);
+                }
+                final String thePaymentMethodId = paymentMethodId != null ? paymentMethodId.toString() : null;
+                transactional.updatePaymentMethod(accountId.toString(), thePaymentMethodId, context);
+
+                final AccountModelDao account = transactional.getById(accountId.toString(), context);
+                final AccountChangeInternalEvent changeEvent = new DefaultAccountChangeEvent(accountId, context.getUserToken(), currentAccount, account,
+                                                                                             context.getAccountRecordId(), context.getTenantRecordId());
+
+                if (changeEvent.hasChanges()) {
+                    try {
+                        eventBus.postFromTransaction(changeEvent, entitySqlDaoWrapperFactory, context);
+                    } catch (final EventBusException e) {
+                        log.warn("Failed to post account change event for account " + accountId, e);
+                    }
+                }
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public void addEmail(final AccountEmailModelDao email, final InternalCallContext context) throws AccountApiException {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final AccountEmailSqlDao transactional = entitySqlDaoWrapperFactory.become(AccountEmailSqlDao.class);
+
+                if (transactional.getById(email.getId().toString(), context) != null) {
+                    throw new AccountApiException(ErrorCode.ACCOUNT_EMAIL_ALREADY_EXISTS, email.getId());
+                }
+
+                transactional.create(email, context);
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public void removeEmail(final AccountEmailModelDao email, final InternalCallContext context) {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                entitySqlDaoWrapperFactory.become(AccountEmailSqlDao.class).markEmailAsDeleted(email, context);
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public List<AccountEmailModelDao> getEmailsByAccountId(final UUID accountId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<AccountEmailModelDao>>() {
+            @Override
+            public List<AccountEmailModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(AccountEmailSqlDao.class).getEmailByAccountId(accountId, context);
+            }
+        });
+    }
+}
diff --git a/account/src/main/java/com/ning/billing/account/glue/DefaultAccountModule.java b/account/src/main/java/com/ning/billing/account/glue/DefaultAccountModule.java
index b419725..20c88d0 100644
--- a/account/src/main/java/com/ning/billing/account/glue/DefaultAccountModule.java
+++ b/account/src/main/java/com/ning/billing/account/glue/DefaultAccountModule.java
@@ -22,9 +22,7 @@ import com.ning.billing.account.api.DefaultAccountService;
 import com.ning.billing.account.api.svcs.DefaultAccountInternalApi;
 import com.ning.billing.account.api.user.DefaultAccountUserApi;
 import com.ning.billing.account.dao.AccountDao;
-import com.ning.billing.account.dao.AccountEmailDao;
-import com.ning.billing.account.dao.AuditedAccountDao;
-import com.ning.billing.account.dao.AuditedAccountEmailDao;
+import com.ning.billing.account.dao.DefaultAccountDao;
 import com.ning.billing.glue.AccountModule;
 import com.ning.billing.util.glue.RealImplementation;
 import com.ning.billing.util.svcapi.account.AccountInternalApi;
@@ -32,12 +30,12 @@ import com.ning.billing.util.svcapi.account.AccountInternalApi;
 import com.google.inject.AbstractModule;
 
 public class DefaultAccountModule extends AbstractModule implements AccountModule {
+
     private void installConfig() {
     }
 
     protected void installAccountDao() {
-        bind(AccountEmailDao.class).to(AuditedAccountEmailDao.class).asEagerSingleton();
-        bind(AccountDao.class).to(AuditedAccountDao.class).asEagerSingleton();
+        bind(AccountDao.class).to(DefaultAccountDao.class).asEagerSingleton();
     }
 
     @Override
@@ -49,6 +47,7 @@ public class DefaultAccountModule extends AbstractModule implements AccountModul
     public void installInternalApi() {
         bind(AccountInternalApi.class).to(DefaultAccountInternalApi.class).asEagerSingleton();
     }
+
     private void installAccountService() {
         bind(AccountService.class).to(DefaultAccountService.class).asEagerSingleton();
     }
diff --git a/account/src/main/resources/com/ning/billing/account/dao/AccountEmailSqlDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/AccountEmailSqlDao.sql.stg
index 6ded4ea..49c37e0 100644
--- a/account/src/main/resources/com/ning/billing/account/dao/AccountEmailSqlDao.sql.stg
+++ b/account/src/main/resources/com/ning/billing/account/dao/AccountEmailSqlDao.sql.stg
@@ -1,93 +1,46 @@
-group account_emails;
-
-CHECK_TENANT() ::= "tenant_record_id = :tenantRecordId"
-AND_CHECK_TENANT() ::= "AND <CHECK_TENANT()>"
-
-fields(prefix) ::= <<
-    <prefix>id,
-    <prefix>account_id,
-    <prefix>email,
-    <prefix>created_by,
-    <prefix>created_date,
-    <prefix>updated_by,
-    <prefix>updated_date,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
-
-insertFromTransaction() ::= <<
-    INSERT INTO account_emails(<fields()>)
-    VALUES (:id, :accountId, :email, :userName, :createdDate, :userName, :updatedDate, :accountRecordId, :tenantRecordId);
->>
-
-updateFromTransaction() ::= <<
-    UPDATE account_emails
-    SET email = :email, updated_by = :userName, updated_date = :updatedDate
-    WHERE id = :id <AND_CHECK_TENANT()>;
->>
-
-deleteFromTransaction() ::= <<
-    DELETE FROM account_emails
-    WHERE id = :id <AND_CHECK_TENANT()>;
->>
-
-addHistoryFromTransaction() ::= <<
-    INSERT INTO account_email_history(record_id, id, account_id, email, change_type, updated_by, date, account_record_id, tenant_record_id)
-    VALUES (:recordId, :id, :accountId, :email, :changeType, :userName, :updatedDate, :accountRecordId, :tenantRecordId);
->>
-
-load() ::= <<
-    SELECT <fields()> FROM account_emails WHERE account_id = :objectId <AND_CHECK_TENANT()>;
->>
-
-getRecordIds() ::= <<
-    SELECT record_id, id
-    FROM account_emails
-    WHERE account_id = :objectId <AND_CHECK_TENANT()>;
->>
-
-getMaxHistoryRecordId() ::= <<
-    SELECT MAX(history_record_id)
-    FROM account_email_history
-    WHERE <CHECK_TENANT()>;
->>
-
-getHistoryRecordIds() ::= <<
-    SELECT history_record_id, record_id
-    FROM account_email_history
-    WHERE history_record_id > :maxHistoryRecordId <AND_CHECK_TENANT()>;
->>
-
-getById() ::= <<
-    SELECT <fields()> FROM account_emails WHERE id = :id <AND_CHECK_TENANT()>;
->>
-
-get() ::= <<
-    SELECT <fields()> FROM account_emails WHERE <CHECK_TENANT()>;
->>
-
-getByAccountId() ::= <<
-    SELECT <fields()> FROM account_emails WHERE account_id = :accountId <AND_CHECK_TENANT()>;
->>
-
-auditFields(prefix) ::= <<
-    <prefix>table_name,
-    <prefix>record_id,
-    <prefix>change_type,
-    <prefix>change_date,
-    <prefix>changed_by,
-    <prefix>reason_code,
-    <prefix>comments,
-    <prefix>user_token,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
-
-insertAuditFromTransaction() ::= <<
-    INSERT INTO audit_log(<auditFields()>)
-    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken, :accountRecordId, :tenantRecordId);
->>
-
-test() ::= <<
-    SELECT 1 FROM account_emails where <CHECK_TENANT()>;
+group AccountEmailSqlDao: EntitySqlDao;
+
+tableName() ::= "account_emails"
+
+historyTableName() ::= "account_email_history"
+
+andCheckSoftDeletionWithComma(prefix) ::= "and <prefix>is_active"
+
+tableFields(prefix) ::= <<
+  account_id
+, email
+, is_active
+, created_by
+, created_date
+, updated_by
+, updated_date
+>>
+
+
+tableValues() ::= <<
+  :accountId
+, :email
+, :isActive
+, :createdBy
+, :createdDate
+, :updatedBy
+, :updatedDate
+ >>
+
+getEmailByAccountId() ::= <<
+select
+  <allTableFields("t.")>
+from <tableName()> t
+where t.account_id = :accountId
+and t.is_active
+<AND_CHECK_TENANT("t.")>
+;
+>>
+
+markEmailAsDeleted() ::= <<
+update <tableName()> t
+set t.is_active = 0
+where <idField("t.")> = :id
+<AND_CHECK_TENANT("t.")>
+;
 >>
diff --git a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
index 0329a2f..7a61009 100644
--- a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
+++ b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
@@ -1,55 +1,75 @@
-group AccountDaoSql;
-
-CHECK_TENANT() ::= "tenant_record_id = :tenantRecordId"
-AND_CHECK_TENANT() ::= "AND <CHECK_TENANT()>"
-
-accountFields(prefix) ::= <<
-    <prefix>id,
-    <prefix>external_key,
-    <prefix>email,
-    <prefix>name,
-    <prefix>first_name_length,
-    <prefix>currency,
-    <prefix>billing_cycle_day_local,
-    <prefix>billing_cycle_day_utc,
-    <prefix>payment_method_id,
-    <prefix>time_zone, 
-    <prefix>locale,
-    <prefix>address1, 
-    <prefix>address2, 
-    <prefix>company_name, 
-    <prefix>city, 
-	<prefix>state_or_province, 
-    <prefix>country, 
-    <prefix>postal_code,
-    <prefix>phone,
-    <prefix>migrated,
-    <prefix>is_notified_for_invoices,
-    <prefix>created_by,
-    <prefix>created_date,
-    <prefix>updated_by,
-    <prefix>updated_date,
-    <prefix>tenant_record_id
->>
-
-create() ::= <<
-    INSERT INTO accounts
-      (<accountFields()>)
-    VALUES
-      (:id, :externalKey, :email, :name, :firstNameLength, :currency, :billingCycleDayLocal,
-       :billingCycleDayUTC, :paymentMethodId, :timeZone, :locale,
-       :address1, :address2, :companyName, :city, :stateOrProvince, :country, :postalCode, :phone,
-       :migrated, :isNotifiedForInvoices, :userName, :createdDate, :userName, :updatedDate, :tenantRecordId);
->>
+group AccountSqlDao: EntitySqlDao;
+
+tableName() ::= "accounts"
+
+historyTableName() ::= "account_history"
+
+tableFields(prefix) ::= <<
+  <prefix>external_key
+, <prefix>email
+, <prefix>name
+, <prefix>first_name_length
+, <prefix>currency
+, <prefix>billing_cycle_day_local
+, <prefix>billing_cycle_day_utc
+, <prefix>payment_method_id
+, <prefix>time_zone
+, <prefix>locale
+, <prefix>address1
+, <prefix>address2
+, <prefix>company_name
+, <prefix>city
+, <prefix>state_or_province
+, <prefix>country
+, <prefix>postal_code
+, <prefix>phone
+, <prefix>migrated
+, <prefix>is_notified_for_invoices
+, <prefix>created_by
+, <prefix>created_date
+, <prefix>updated_by
+, <prefix>updated_date
+>>
+
+tableValues() ::= <<
+  :externalKey
+, :email
+, :name
+, :firstNameLength
+, :currency
+, :billingCycleDayLocal
+, :billingCycleDayUtc
+, :paymentMethodId
+, :timeZone
+, :locale
+, :address1
+, :address2
+, :companyName
+, :city
+, :stateOrProvince
+, :country
+, :postalCode
+, :phone
+, :migrated
+, :isNotifiedForInvoices
+, :createdBy
+, :createdDate
+, :updatedBy
+, :updatedDate
+>>
+
+/** The accounts table doesn't have an account_record_id column (it's the record_id) **/
+accountRecordIdFieldWithComma(prefix) ::= ""
+accountRecordIdValueWithComma(prefix) ::= ""
 
 update() ::= <<
     UPDATE accounts
     SET email = :email, name = :name, first_name_length = :firstNameLength,
-        currency = :currency, billing_cycle_day_local = :billingCycleDayLocal, billing_cycle_day_utc = :billingCycleDayUTC,
+        currency = :currency, billing_cycle_day_local = :billingCycleDayLocal, billing_cycle_day_utc = :billingCycleDayUtc,
         payment_method_id = :paymentMethodId, time_zone = :timeZone, locale = :locale,
         address1 = :address1, address2 = :address2, company_name = :companyName, city = :city, state_or_province = :stateOrProvince,
         country = :country, postal_code = :postalCode, phone = :phone,
-        is_notified_for_invoices = :isNotifiedForInvoices, updated_date = :updatedDate, updated_by = :userName
+        is_notified_for_invoices = :isNotifiedForInvoices, updated_date = :updatedDate, updated_by = :updatedBy
     WHERE id = :id <AND_CHECK_TENANT()>;
 >>
 
@@ -58,84 +78,18 @@ updatePaymentMethod() ::= <<
     UPDATE accounts
     SET payment_method_id = :paymentMethodId
     , updated_date = :updatedDate
-    , updated_by = :userName
-    WHERE id = :id <AND_CHECK_TENANT()>;
->>
-
-
-historyFields() ::= <<
-    record_id,
-    id,
-    external_key,
-    email,
-    name,
-    first_name_length,
-    currency,
-    billing_cycle_day_local,
-    billing_cycle_day_utc,
-    payment_method_id,
-    time_zone,
-    locale,
-    address1,
-    address2,
-    company_name,
-    city,
-    state_or_province,
-    country,
-    postal_code,
-    phone,
-    migrated,
-    is_notified_for_invoices,
-    change_type,
-    updated_by,
-    date,
-    tenant_record_id
->>
-
-getRecordId() ::= <<
-    SELECT record_id
-    FROM accounts
+    , updated_by = :updatedBy
     WHERE id = :id <AND_CHECK_TENANT()>;
 >>
 
-getByRecordId() ::= <<
-    SELECT <accountFields()>
-    FROM accounts
-    WHERE record_id = :recordId <AND_CHECK_TENANT()>;
->>
-
-getHistoryRecordId() ::= <<
-    SELECT MAX(history_record_id)
-    FROM account_history
-    WHERE record_id = :recordId <AND_CHECK_TENANT()>;
->>
 
-insertHistoryFromTransaction() ::= <<
-    INSERT INTO account_history(<historyFields()>)
-    VALUES
-    (:recordId, :id, :externalKey, :email, :name, :firstNameLength, :currency,
-     :billingCycleDayLocal, :billingCycleDayUTC, :paymentMethodId, :timeZone, :locale,
-     :address1, :address2, :companyName, :city, :stateOrProvince,
-     :country, :postalCode, :phone, :migrated, :isNotifiedForInvoices, :changeType, :userName, :createdDate, :tenantRecordId);
->>
 
 getAccountByKey() ::= <<
-    select <accountFields()>
+    select <allTableFields()>
     from accounts
     where external_key = :externalKey <AND_CHECK_TENANT()>;
 >>
 
-getById() ::= <<
-    SELECT <accountFields()>
-    FROM accounts
-    WHERE id = :id <AND_CHECK_TENANT()>;
->>
-
-get() ::= <<
-    SELECT <accountFields()>
-    FROM accounts
-    WHERE <CHECK_TENANT()>;
->>
 
 getIdFromKey() ::= <<
     SELECT id
@@ -143,25 +97,3 @@ getIdFromKey() ::= <<
     WHERE external_key = :externalKey <AND_CHECK_TENANT()>;
 >>
 
-auditFields(prefix) ::= <<
-    <prefix>table_name,
-    <prefix>record_id,
-    <prefix>change_type,
-    <prefix>change_date,
-    <prefix>changed_by,
-    <prefix>reason_code,
-    <prefix>comments,
-    <prefix>user_token,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
-
-insertAuditFromTransaction() ::= <<
-    INSERT INTO audit_log(<auditFields()>)
-    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken, :accountRecordId, :tenantRecordId);
->>
-
-test() ::= <<
-    SELECT 1 FROM accounts WHERE <CHECK_TENANT()>;
->>
-;
diff --git a/account/src/main/resources/com/ning/billing/account/ddl.sql b/account/src/main/resources/com/ning/billing/account/ddl.sql
index 47f2138..28dc6e1 100644
--- a/account/src/main/resources/com/ning/billing/account/ddl.sql
+++ b/account/src/main/resources/com/ning/billing/account/ddl.sql
@@ -35,9 +35,9 @@ CREATE INDEX accounts_tenant_record_id ON accounts(tenant_record_id);
 
 DROP TABLE IF EXISTS account_history;
 CREATE TABLE account_history (
-    history_record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
-    record_id int(11) unsigned NOT NULL,
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
     id char(36) NOT NULL,
+    target_record_id int(11) unsigned NOT NULL,
     external_key varchar(128) NULL,
     email varchar(128) NOT NULL,
     name varchar(100) NOT NULL,
@@ -59,12 +59,14 @@ CREATE TABLE account_history (
     migrated bool DEFAULT false,
     is_notified_for_invoices boolean NOT NULL,
     change_type char(6) NOT NULL,
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
     updated_by varchar(50) NOT NULL,
-    date datetime NOT NULL,
+    updated_date datetime NOT NULL,
     tenant_record_id int(11) unsigned default null,
-    PRIMARY KEY(history_record_id)
-) ENGINE=innodb;
-CREATE INDEX account_history_record_id ON account_history(record_id);
+    PRIMARY KEY(record_id)
+    ) ENGINE=innodb;
+CREATE INDEX account_history_target_record_id ON account_history(target_record_id);
 CREATE INDEX account_history_tenant_record_id ON account_history(tenant_record_id);
 
 DROP TABLE IF EXISTS account_emails;
@@ -73,6 +75,7 @@ CREATE TABLE account_emails (
     id char(36) NOT NULL,
     account_id char(36) NOT NULL,
     email varchar(128) NOT NULL,
+    is_active bool DEFAULT true,
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
     updated_by varchar(50) NOT NULL,
@@ -82,22 +85,25 @@ CREATE TABLE account_emails (
     PRIMARY KEY(record_id)
 ) ENGINE=innodb;
 CREATE UNIQUE INDEX account_email_id ON account_emails(id);
-CREATE UNIQUE INDEX account_email_account_id_email ON account_emails(account_id, email);
+CREATE INDEX account_email_account_id_email ON account_emails(account_id, email);
 CREATE INDEX account_emails_tenant_account_record_id ON account_emails(tenant_record_id, account_record_id);
 
 DROP TABLE IF EXISTS account_email_history;
 CREATE TABLE account_email_history (
-    history_record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
-    record_id int(11) unsigned NOT NULL,
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
     id char(36) NOT NULL,
+    target_record_id int(11) unsigned NOT NULL,
     account_id char(36) NOT NULL,
     email varchar(128) NOT NULL,
+    is_active bool DEFAULT true,
     change_type char(6) NOT NULL,
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
     updated_by varchar(50) NOT NULL,
-    date datetime NOT NULL,
+    updated_date datetime NOT NULL,
     account_record_id int(11) unsigned default null,
     tenant_record_id int(11) unsigned default null,
-    PRIMARY KEY(history_record_id)
+    PRIMARY KEY(record_id)
 ) ENGINE=innodb;
-CREATE INDEX account_email_record_id ON account_email_history(record_id);
+CREATE INDEX account_email_target_record_id ON account_email_history(target_record_id);
 CREATE INDEX account_email_history_tenant_account_record_id ON account_email_history(tenant_record_id, account_record_id);
diff --git a/account/src/test/java/com/ning/billing/account/AccountTestBase.java b/account/src/test/java/com/ning/billing/account/AccountTestBase.java
new file mode 100644
index 0000000..b75ec6f
--- /dev/null
+++ b/account/src/test/java/com/ning/billing/account/AccountTestBase.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2010-2012 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.account;
+
+import java.io.IOException;
+import java.util.Locale;
+import java.util.UUID;
+
+import org.joda.time.DateTimeZone;
+import org.skife.jdbi.v2.IDBI;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+
+import com.ning.billing.account.api.AccountData;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.account.api.BillCycleDay;
+import com.ning.billing.account.api.DefaultBillCycleDay;
+import com.ning.billing.account.api.DefaultMutableAccountData;
+import com.ning.billing.account.api.user.DefaultAccountUserApi;
+import com.ning.billing.account.dao.AccountDao;
+import com.ning.billing.account.dao.AccountModelDao;
+import com.ning.billing.account.dao.DefaultAccountDao;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.audit.dao.AuditDao;
+import com.ning.billing.util.audit.dao.DefaultAuditDao;
+import com.ning.billing.util.bus.DefaultBusService;
+import com.ning.billing.util.bus.InMemoryInternalBus;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.customfield.dao.CustomFieldDao;
+import com.ning.billing.util.customfield.dao.DefaultCustomFieldDao;
+import com.ning.billing.util.svcsapi.bus.BusService;
+import com.ning.billing.util.svcsapi.bus.InternalBus;
+import com.ning.billing.util.tag.api.user.TagEventBuilder;
+import com.ning.billing.util.tag.dao.DefaultTagDao;
+import com.ning.billing.util.tag.dao.DefaultTagDefinitionDao;
+import com.ning.billing.util.tag.dao.TagDao;
+import com.ning.billing.util.tag.dao.TagDefinitionDao;
+
+import static org.testng.Assert.fail;
+
+public abstract class AccountTestBase extends AccountTestSuiteWithEmbeddedDB {
+
+    protected final TagEventBuilder tagEventBuilder = new TagEventBuilder();
+    protected final Clock clock = new ClockMock();
+    protected final InternalBus bus = new InMemoryInternalBus();
+
+    protected AccountDao accountDao;
+    protected AuditDao auditDao;
+    protected CustomFieldDao customFieldDao;
+    protected TagDefinitionDao tagDefinitionDao;
+    protected TagDao tagDao;
+
+    protected AccountUserApi accountUserApi;
+
+    @BeforeClass(groups = "slow")
+    protected void setup() throws IOException {
+        try {
+            final IDBI dbi = getMysqlTestingHelper().getDBI();
+
+            final InternalCallContextFactory internalCallContextFactory = new InternalCallContextFactory(dbi, clock);
+            accountDao = new DefaultAccountDao(dbi, bus, internalCallContextFactory);
+            auditDao = new DefaultAuditDao(dbi);
+            customFieldDao = new DefaultCustomFieldDao(dbi);
+            tagDefinitionDao = new DefaultTagDefinitionDao(dbi, tagEventBuilder, bus);
+            tagDao = new DefaultTagDao(dbi, tagEventBuilder, bus);
+
+            // Health check test to make sure MySQL is setup properly
+            accountDao.test(internalCallContext);
+
+            final BusService busService = new DefaultBusService(bus);
+            ((DefaultBusService) busService).startBus();
+
+            final DefaultCallContextFactory callContextFactory = new DefaultCallContextFactory(clock);
+            accountUserApi = new DefaultAccountUserApi(callContextFactory, internalCallContextFactory, accountDao);
+        } catch (Throwable t) {
+            fail(t.toString());
+        }
+    }
+
+    protected void checkAccountsEqual(final AccountData retrievedAccount, final AccountData account) {
+        final UUID fakeId = UUID.randomUUID();
+        checkAccountsEqual(new AccountModelDao(fakeId, retrievedAccount), new AccountModelDao(fakeId, account));
+    }
+
+    protected void checkAccountsEqual(final AccountModelDao retrievedAccount, final AccountModelDao account) {
+        if (retrievedAccount == null || account == null) {
+            Assert.assertNull(retrievedAccount);
+            Assert.assertNull(account);
+            return;
+        }
+
+        // Check all fields but createdDate/updatedDate (comes from the context)
+        Assert.assertEquals(retrievedAccount.getId(), account.getId());
+        Assert.assertEquals(retrievedAccount.getExternalKey(), account.getExternalKey());
+        Assert.assertEquals(retrievedAccount.getEmail(), account.getEmail());
+        Assert.assertEquals(retrievedAccount.getName(), account.getName());
+        Assert.assertEquals(retrievedAccount.getFirstNameLength(), account.getFirstNameLength());
+        Assert.assertEquals(retrievedAccount.getCurrency(), account.getCurrency());
+        Assert.assertEquals(retrievedAccount.getBillingCycleDayLocal(), account.getBillingCycleDayLocal());
+        Assert.assertEquals(retrievedAccount.getBillingCycleDayUtc(), account.getBillingCycleDayUtc());
+        Assert.assertEquals(retrievedAccount.getPaymentMethodId(), account.getPaymentMethodId());
+        Assert.assertEquals(retrievedAccount.getTimeZone(), account.getTimeZone());
+        Assert.assertEquals(retrievedAccount.getLocale(), account.getLocale());
+        Assert.assertEquals(retrievedAccount.getAddress1(), account.getAddress1());
+        Assert.assertEquals(retrievedAccount.getAddress2(), account.getAddress2());
+        Assert.assertEquals(retrievedAccount.getCompanyName(), account.getCompanyName());
+        Assert.assertEquals(retrievedAccount.getCity(), account.getCity());
+        Assert.assertEquals(retrievedAccount.getStateOrProvince(), account.getStateOrProvince());
+        Assert.assertEquals(retrievedAccount.getCountry(), account.getCountry());
+        Assert.assertEquals(retrievedAccount.getPostalCode(), account.getPostalCode());
+        Assert.assertEquals(retrievedAccount.getPhone(), account.getPhone());
+        Assert.assertEquals(retrievedAccount.getIsNotifiedForInvoices(), account.getIsNotifiedForInvoices());
+        Assert.assertEquals(retrievedAccount.getMigrated(), account.getMigrated());
+    }
+
+    protected AccountModelDao createTestAccount() {
+        return createTestAccount(30, 31, UUID.randomUUID().toString().substring(0, 4));
+    }
+
+    protected AccountModelDao createTestAccount(final String phone) {
+        return createTestAccount(30, 31, phone);
+    }
+
+    protected AccountModelDao createTestAccount(final int billCycleDay) {
+        return createTestAccount(billCycleDay, billCycleDay, UUID.randomUUID().toString().substring(0, 4));
+    }
+
+    private AccountModelDao createTestAccount(final int billCycleDayUTC, final int billCycleDayLocal, final String phone) {
+        final AccountData accountData = createAccountData(billCycleDayUTC, billCycleDayLocal, phone);
+        return new AccountModelDao(UUID.randomUUID(), accountData);
+    }
+
+    protected AccountData createAccountData() {
+        return createAccountData(30, 31, UUID.randomUUID().toString().substring(0, 4));
+    }
+
+    private AccountData createAccountData(final int billCycleDayUTC, final int billCycleDayLocal, final String phone) {
+        final String externalKey = UUID.randomUUID().toString();
+        final String email = UUID.randomUUID().toString().substring(0, 4) + '@' + UUID.randomUUID().toString().substring(0, 4);
+        final String name = UUID.randomUUID().toString();
+        final String locale = Locale.GERMANY.toString();
+        final DateTimeZone timeZone = DateTimeZone.forID("America/Los_Angeles");
+        final int firstNameLength = name.length();
+        final Currency currency = Currency.MXN;
+        final BillCycleDay billCycleDay = new DefaultBillCycleDay(billCycleDayLocal, billCycleDayUTC);
+        final UUID paymentMethodId = UUID.randomUUID();
+        final String address1 = UUID.randomUUID().toString();
+        final String address2 = UUID.randomUUID().toString();
+        final String companyName = UUID.randomUUID().toString();
+        final String city = UUID.randomUUID().toString();
+        final String stateOrProvince = UUID.randomUUID().toString();
+        final String country = Locale.GERMANY.getCountry();
+        final String postalCode = UUID.randomUUID().toString().substring(0, 4);
+
+        return new DefaultMutableAccountData(externalKey, email, name, firstNameLength, currency,
+                                             billCycleDay, paymentMethodId, timeZone,
+                                             locale, address1, address2, companyName, city, stateOrProvince,
+                                             country, postalCode, phone, false, true);
+    }
+}
diff --git a/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApi.java b/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApi.java
index 01284ca..159b2de 100644
--- a/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApi.java
+++ b/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApi.java
@@ -16,119 +16,117 @@
 
 package com.ning.billing.account.api.user;
 
+import java.util.List;
 import java.util.UUID;
 
-import org.joda.time.DateTimeZone;
-import org.mockito.Mockito;
+import org.joda.time.DateTime;
 import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import com.ning.billing.account.AccountTestSuite;
+import com.ning.billing.account.AccountTestBase;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.BillCycleDay;
 import com.ning.billing.account.api.DefaultAccount;
-import com.ning.billing.account.api.DefaultAccountEmail;
-import com.ning.billing.account.api.DefaultBillCycleDay;
-import com.ning.billing.account.dao.AccountDao;
-import com.ning.billing.account.dao.AccountEmailDao;
-import com.ning.billing.account.dao.MockAccountDao;
-import com.ning.billing.account.dao.MockAccountEmailDao;
+import com.ning.billing.account.api.DefaultMutableAccountData;
+import com.ning.billing.account.api.MigrationAccountData;
+import com.ning.billing.account.api.MutableAccountData;
 import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.util.svcsapi.bus.InternalBus;
-import com.ning.billing.util.callcontext.CallContext;
-import com.ning.billing.util.callcontext.CallContextFactory;
-import com.ning.billing.util.callcontext.InternalCallContextFactory;
-import com.ning.billing.util.callcontext.InternalTenantContext;
-
-public class TestDefaultAccountUserApi extends AccountTestSuite {
-
-    private final CallContextFactory factory = Mockito.mock(CallContextFactory.class);
-    private final InternalCallContextFactory internalFactory = Mockito.mock(InternalCallContextFactory.class);
-    private final CallContext callContext = Mockito.mock(CallContext.class);
-    private final InternalTenantContext tenantContext = Mockito.mock(InternalTenantContext.class);
-
-    private AccountDao accountDao;
-    private AccountEmailDao accountEmailDao;
-    private DefaultAccountUserApi accountUserApi;
-
-    @BeforeMethod(groups = "fast")
-    public void setUp() throws Exception {
-        accountDao = new MockAccountDao(Mockito.mock(InternalBus.class));
-        accountEmailDao = new MockAccountEmailDao();
-        accountUserApi = new DefaultAccountUserApi(factory, internalFactory, accountDao, accountEmailDao);
+
+public class TestDefaultAccountUserApi extends AccountTestBase {
+
+    @Test(groups = "slow")
+    public void testMigrate() throws Exception {
+        final MigrationAccountData accountData = new TestMigrationAccountData(createAccountData());
+        final Account account = accountUserApi.migrateAccount(accountData, callContext);
+        checkAccountsEqual(account.toMutableAccountData(), accountData);
+
+        // Make sure we can retrieve the migrated account
+        final Account retrievedAccount = accountUserApi.getAccountById(account.getId(), callContext);
+        checkAccountsEqual(retrievedAccount, account);
+    }
+
+    @Test(groups = "slow")
+    public void testShouldBeAbleToPassNullForSomeFieldsToAvoidUpdate() throws Exception {
+        final Account account = accountUserApi.createAccount(new DefaultAccount(createTestAccount()), callContext);
+
+        // Update the address and leave other fields null
+        final MutableAccountData mutableAccountData = new DefaultMutableAccountData(null, null, null, 0, null, null, null,
+                                                                                    null, null, null, null, null, null,
+                                                                                    null, null, null, null, false, false);
+        final String newAddress1 = UUID.randomUUID().toString();
+        mutableAccountData.setAddress1(newAddress1);
+
+        accountUserApi.updateAccount(account.getId(), mutableAccountData, callContext);
+
+        final Account retrievedAccount = accountUserApi.getAccountById(account.getId(), callContext);
+        Assert.assertEquals(retrievedAccount.getAddress1(), newAddress1);
+        Assert.assertEquals(retrievedAccount.getAddress2(), account.getAddress2());
+        Assert.assertEquals(retrievedAccount.getCurrency(), account.getCurrency());
+        Assert.assertEquals(retrievedAccount.getExternalKey(), account.getExternalKey());
+        Assert.assertEquals(retrievedAccount.getBillCycleDay().getDayOfMonthLocal(), account.getBillCycleDay().getDayOfMonthLocal());
+        Assert.assertEquals(retrievedAccount.getBillCycleDay().getDayOfMonthUTC(), account.getBillCycleDay().getDayOfMonthUTC());
+    }
+
+    @Test(groups = "slow", expectedExceptions = IllegalArgumentException.class)
+    public void testShouldntBeAbleToUpdateBillCycleDay() throws Exception {
+        final Account account = accountUserApi.createAccount(new DefaultAccount(createTestAccount()), callContext);
+
+        final MutableAccountData otherAccount = new DefaultAccount(account.getId(), account).toMutableAccountData();
+        otherAccount.setBillCycleDay(new BillCycleDay() {
+            @Override
+            public int getDayOfMonthUTC() {
+                return account.getBillCycleDay().getDayOfMonthUTC() + 2;
+            }
+
+            @Override
+            public int getDayOfMonthLocal() {
+                return account.getBillCycleDay().getDayOfMonthLocal() + 2;
+            }
+        });
+
+        accountUserApi.updateAccount(new DefaultAccount(account.getId(), otherAccount), callContext);
+    }
+
+    @Test(groups = "slow", expectedExceptions = IllegalArgumentException.class)
+    public void testShouldntBeAbleToUpdateCurrency() throws Exception {
+        final Account account = accountUserApi.createAccount(new DefaultAccount(createTestAccount()), callContext);
+
+        final MutableAccountData otherAccount = new DefaultAccount(account.getId(), account).toMutableAccountData();
+        otherAccount.setCurrency(Currency.GBP);
+
+        accountUserApi.updateAccount(new DefaultAccount(account.getId(), otherAccount), callContext);
     }
 
-    @Test(groups = "fast")
-    public void testCreateAccount() throws Exception {
-        final UUID id = UUID.randomUUID();
-        final String externalKey = UUID.randomUUID().toString();
-        final String email = UUID.randomUUID().toString();
-        final String name = UUID.randomUUID().toString();
-        final Integer firstNameLength = Integer.MAX_VALUE;
-        final Currency currency = Currency.BRL;
-        final BillCycleDay billCycleDay = new DefaultBillCycleDay(Integer.MIN_VALUE, Integer.MAX_VALUE);
-        final UUID paymentMethodId = UUID.randomUUID();
-        final DateTimeZone timeZone = DateTimeZone.UTC;
-        final String locale = UUID.randomUUID().toString();
-        final String address1 = UUID.randomUUID().toString();
-        final String address2 = UUID.randomUUID().toString();
-        final String companyName = UUID.randomUUID().toString();
-        final String city = UUID.randomUUID().toString();
-        final String stateOrProvince = UUID.randomUUID().toString();
-        final String country = UUID.randomUUID().toString();
-        final String postalCode = UUID.randomUUID().toString();
-        final String phone = UUID.randomUUID().toString();
-        final Boolean isMigrated = true;
-        final Boolean isNotifiedForInvoices = false;
-        final AccountData data = new DefaultAccount(id, externalKey, email, name, firstNameLength, currency, billCycleDay,
-                                                    paymentMethodId, timeZone, locale, address1, address2, companyName,
-                                                    city, stateOrProvince, country, postalCode, phone, isMigrated, isNotifiedForInvoices);
-
-        accountUserApi.createAccount(data, callContext);
-
-        final Account account = accountDao.getAccountByKey(externalKey, tenantContext);
-        Assert.assertEquals(account.getExternalKey(), externalKey);
-        Assert.assertEquals(account.getEmail(), email);
-        Assert.assertEquals(account.getName(), name);
-        Assert.assertEquals(account.getFirstNameLength(), firstNameLength);
-        Assert.assertEquals(account.getCurrency(), currency);
-        Assert.assertEquals(account.getBillCycleDay(), billCycleDay);
-        Assert.assertEquals(account.getPaymentMethodId(), paymentMethodId);
-        Assert.assertEquals(account.getTimeZone(), timeZone);
-        Assert.assertEquals(account.getLocale(), locale);
-        Assert.assertEquals(account.getAddress1(), address1);
-        Assert.assertEquals(account.getAddress2(), address2);
-        Assert.assertEquals(account.getCompanyName(), companyName);
-        Assert.assertEquals(account.getCity(), city);
-        Assert.assertEquals(account.getStateOrProvince(), stateOrProvince);
-        Assert.assertEquals(account.getCountry(), country);
-        Assert.assertEquals(account.getPostalCode(), postalCode);
-        Assert.assertEquals(account.getPhone(), phone);
-        Assert.assertEquals(account.isMigrated(), isMigrated);
-        Assert.assertEquals(account.isNotifiedForInvoices(), isNotifiedForInvoices);
+    @Test(groups = "slow", expectedExceptions = IllegalArgumentException.class)
+    public void testShouldntBeAbleToUpdateExternalKey() throws Exception {
+        final Account account = accountUserApi.createAccount(new DefaultAccount(createTestAccount()), callContext);
+
+        final MutableAccountData otherAccount = new DefaultAccount(account.getId(), account).toMutableAccountData();
+        otherAccount.setExternalKey(UUID.randomUUID().toString());
+
+        accountUserApi.updateAccount(new DefaultAccount(account.getId(), otherAccount), callContext);
     }
 
-    @Test(groups = "fast")
-    public void testAddEmail() throws Exception {
-        final UUID accountId = UUID.randomUUID();
+    private class TestMigrationAccountData extends DefaultMutableAccountData implements MigrationAccountData {
 
-        // Verify the initial state
-        Assert.assertEquals(accountEmailDao.getEmails(accountId, tenantContext).size(), 0);
+        public TestMigrationAccountData(final AccountData accountData) {
+            super(accountData);
+        }
 
-        // Add the first email
-        final String email1 = UUID.randomUUID().toString();
-        accountUserApi.addEmail(accountId, new DefaultAccountEmail(accountId, email1), callContext);
-        Assert.assertEquals(accountEmailDao.getEmails(accountId, tenantContext).size(), 1);
+        @Override
+        public DateTime getCreatedDate() {
+            return null;
+        }
 
-        // Add a second one
-        final String email2 = UUID.randomUUID().toString();
-        accountUserApi.addEmail(accountId, new DefaultAccountEmail(accountId, email2), callContext);
-        Assert.assertEquals(accountEmailDao.getEmails(accountId, tenantContext).size(), 2);
+        @Override
+        public DateTime getUpdatedDate() {
+            return null;
+        }
 
-        // Remove the first second one
-        accountUserApi.removeEmail(accountId, new DefaultAccountEmail(accountId, email1), callContext);
-        Assert.assertEquals(accountEmailDao.getEmails(accountId, tenantContext).size(), 1);
+        @Override
+        public List<String> getAdditionalContactEmails() {
+            return null;
+        }
     }
 }
diff --git a/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java b/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java
new file mode 100644
index 0000000..72f04d8
--- /dev/null
+++ b/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2010-2012 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.account.api.user;
+
+import java.util.UUID;
+
+import org.joda.time.DateTimeZone;
+import org.mockito.Mockito;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.AccountTestSuite;
+import com.ning.billing.account.api.AccountData;
+import com.ning.billing.account.api.AccountEmail;
+import com.ning.billing.account.api.BillCycleDay;
+import com.ning.billing.account.api.DefaultAccount;
+import com.ning.billing.account.api.DefaultAccountEmail;
+import com.ning.billing.account.api.DefaultBillCycleDay;
+import com.ning.billing.account.dao.AccountDao;
+import com.ning.billing.account.dao.AccountModelDao;
+import com.ning.billing.account.dao.MockAccountDao;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextFactory;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.svcsapi.bus.InternalBus;
+
+public class TestDefaultAccountUserApiWithMocks extends AccountTestSuite {
+
+    private final CallContextFactory factory = Mockito.mock(CallContextFactory.class);
+    private final InternalCallContextFactory internalFactory = Mockito.mock(InternalCallContextFactory.class);
+    private final CallContext callContext = Mockito.mock(CallContext.class);
+    private final InternalTenantContext tenantContext = Mockito.mock(InternalTenantContext.class);
+
+    private AccountDao accountDao;
+    private DefaultAccountUserApi accountUserApi;
+
+    @BeforeMethod(groups = "fast")
+    public void setUp() throws Exception {
+        accountDao = new MockAccountDao(Mockito.mock(InternalBus.class));
+        accountUserApi = new DefaultAccountUserApi(factory, internalFactory, accountDao);
+    }
+
+    @Test(groups = "fast")
+    public void testCreateAccount() throws Exception {
+        final UUID id = UUID.randomUUID();
+        final String externalKey = UUID.randomUUID().toString();
+        final String email = UUID.randomUUID().toString();
+        final String name = UUID.randomUUID().toString();
+        final Integer firstNameLength = Integer.MAX_VALUE;
+        final Currency currency = Currency.BRL;
+        final BillCycleDay billCycleDay = new DefaultBillCycleDay(Integer.MIN_VALUE, Integer.MAX_VALUE);
+        final UUID paymentMethodId = UUID.randomUUID();
+        final DateTimeZone timeZone = DateTimeZone.UTC;
+        final String locale = UUID.randomUUID().toString();
+        final String address1 = UUID.randomUUID().toString();
+        final String address2 = UUID.randomUUID().toString();
+        final String companyName = UUID.randomUUID().toString();
+        final String city = UUID.randomUUID().toString();
+        final String stateOrProvince = UUID.randomUUID().toString();
+        final String country = UUID.randomUUID().toString();
+        final String postalCode = UUID.randomUUID().toString();
+        final String phone = UUID.randomUUID().toString();
+        final Boolean isMigrated = true;
+        final Boolean isNotifiedForInvoices = false;
+        final AccountData data = new DefaultAccount(id, externalKey, email, name, firstNameLength, currency, billCycleDay,
+                                                    paymentMethodId, timeZone, locale, address1, address2, companyName,
+                                                    city, stateOrProvince, country, postalCode, phone, isMigrated, isNotifiedForInvoices);
+
+        accountUserApi.createAccount(data, callContext);
+
+        final AccountModelDao account = accountDao.getAccountByKey(externalKey, tenantContext);
+        Assert.assertEquals(account.getExternalKey(), externalKey);
+        Assert.assertEquals(account.getEmail(), email);
+        Assert.assertEquals(account.getName(), name);
+        Assert.assertEquals(account.getFirstNameLength(), firstNameLength);
+        Assert.assertEquals(account.getCurrency(), currency);
+        Assert.assertEquals(account.getBillingCycleDayLocal(), billCycleDay.getDayOfMonthLocal());
+        Assert.assertEquals(account.getBillingCycleDayUtc(), billCycleDay.getDayOfMonthUTC());
+        Assert.assertEquals(account.getPaymentMethodId(), paymentMethodId);
+        Assert.assertEquals(account.getTimeZone(), timeZone);
+        Assert.assertEquals(account.getLocale(), locale);
+        Assert.assertEquals(account.getAddress1(), address1);
+        Assert.assertEquals(account.getAddress2(), address2);
+        Assert.assertEquals(account.getCompanyName(), companyName);
+        Assert.assertEquals(account.getCity(), city);
+        Assert.assertEquals(account.getStateOrProvince(), stateOrProvince);
+        Assert.assertEquals(account.getCountry(), country);
+        Assert.assertEquals(account.getPostalCode(), postalCode);
+        Assert.assertEquals(account.getPhone(), phone);
+        Assert.assertEquals(account.getMigrated(), isMigrated);
+        Assert.assertEquals(account.getIsNotifiedForInvoices(), isNotifiedForInvoices);
+    }
+
+    @Test(groups = "fast")
+    public void testAddEmail() throws Exception {
+        final UUID accountId = UUID.randomUUID();
+
+        // Verify the initial state
+        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, tenantContext).size(), 0);
+
+        // Add the first email
+        final String emailAddress1 = UUID.randomUUID().toString();
+        final AccountEmail email1 = new DefaultAccountEmail(accountId, emailAddress1);
+        accountUserApi.addEmail(accountId, email1, callContext);
+        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, tenantContext).size(), 1);
+
+        // Add a second one
+        final String emailAddress2 = UUID.randomUUID().toString();
+        final AccountEmail email2 = new DefaultAccountEmail(accountId, emailAddress2);
+        accountUserApi.addEmail(accountId, email2, callContext);
+        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, tenantContext).size(), 2);
+
+        // Remove the first second one
+        accountUserApi.removeEmail(accountId, email1, callContext);
+        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, tenantContext).size(), 1);
+    }
+}
diff --git a/account/src/test/java/com/ning/billing/account/api/user/TestEventJson.java b/account/src/test/java/com/ning/billing/account/api/user/TestEventJson.java
index 8dd87bb..f19d940 100644
--- a/account/src/test/java/com/ning/billing/account/api/user/TestEventJson.java
+++ b/account/src/test/java/com/ning/billing/account/api/user/TestEventJson.java
@@ -24,14 +24,15 @@ import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import com.ning.billing.account.AccountTestSuite;
-import com.ning.billing.util.events.ChangedField;
 import com.ning.billing.account.api.DefaultBillCycleDay;
 import com.ning.billing.account.api.DefaultChangedField;
 import com.ning.billing.account.api.user.DefaultAccountCreationEvent.DefaultAccountData;
 import com.ning.billing.util.events.AccountChangeInternalEvent;
+import com.ning.billing.util.events.ChangedField;
 import com.ning.billing.util.jackson.ObjectMapper;
 
 public class TestEventJson extends AccountTestSuite {
+
     private final ObjectMapper mapper = new ObjectMapper();
 
     @Test(groups = "fast")
@@ -52,7 +53,7 @@ public class TestEventJson extends AccountTestSuite {
     public void testAccountCreationEvent() throws Exception {
         final DefaultAccountData data = new DefaultAccountData("dsfdsf", "bobo", 3, "bobo@yahoo.com", new DefaultBillCycleDay(12), "USD", UUID.randomUUID(),
                                                                "UTC", "US", "21 avenue", "", "Gling", "San Franciso", "CA", "94110", "USA", "4126789887", false, false);
-        final DefaultAccountCreationEvent e = new DefaultAccountCreationEvent(data, UUID.randomUUID(), UUID.randomUUID(),  1L, 45L);
+        final DefaultAccountCreationEvent e = new DefaultAccountCreationEvent(data, UUID.randomUUID(), UUID.randomUUID(), 1L, 45L);
         final String json = mapper.writeValueAsString(e);
 
         final DefaultAccountCreationEvent obj = mapper.readValue(json, DefaultAccountCreationEvent.class);
diff --git a/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
index fddfbc1..e4ad71a 100644
--- a/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
@@ -16,100 +16,128 @@
 
 package com.ning.billing.account.dao;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
 
+import org.testng.Assert;
+
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.account.api.AccountEmail;
+import com.ning.billing.account.api.DefaultAccount;
+import com.ning.billing.account.api.DefaultMutableAccountData;
 import com.ning.billing.account.api.user.DefaultAccountChangeEvent;
 import com.ning.billing.account.api.user.DefaultAccountCreationEvent;
 import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.entity.EntityPersistenceException;
+import com.ning.billing.util.entity.dao.MockEntityDaoBase;
 import com.ning.billing.util.events.AccountChangeInternalEvent;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
 import com.ning.billing.util.svcsapi.bus.InternalBus.EventBusException;
 
-import com.google.inject.Inject;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 
-public class MockAccountDao implements AccountDao {
+public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account, AccountApiException> implements AccountDao {
 
+    private final MockEntityDaoBase<AccountEmailModelDao, AccountEmail, AccountApiException> accountEmailSqlDao = new MockEntityDaoBase<AccountEmailModelDao, AccountEmail, AccountApiException>();
     private final InternalBus eventBus;
-    private final Map<UUID, Account> accounts = new ConcurrentHashMap<UUID, Account>();
 
-    @Inject
     public MockAccountDao(final InternalBus eventBus) {
         this.eventBus = eventBus;
     }
 
     @Override
-    public Long getRecordId(final UUID id, final InternalTenantContext context) {
-        return 1L;
-    }
-
-    @Override
-    public void create(final Account account, final InternalCallContext context) {
-        accounts.put(account.getId(), account);
+    public void create(final AccountModelDao account, final InternalCallContext context) throws AccountApiException {
+        super.create(account, context);
 
         try {
-            eventBus.post(new DefaultAccountCreationEvent(account, null, 1L, 1L), context);
+            final Long accountRecordId = getRecordId(account.getId(), context);
+            final long tenantRecordId = context == null ? InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID
+                                                        : context.getTenantRecordId();
+            eventBus.post(new DefaultAccountCreationEvent(account, null, accountRecordId, tenantRecordId), context);
         } catch (final EventBusException ex) {
-            throw new RuntimeException(ex);
+            Assert.fail(ex.toString());
         }
     }
 
     @Override
-    public Account getById(final UUID id, final InternalTenantContext context) {
-        return accounts.get(id);
-    }
-
-    @Override
-    public List<Account> get(final InternalTenantContext context) {
-        return new ArrayList<Account>(accounts.values());
-    }
-
-    @Override
-    public void test(final InternalTenantContext context) {
+    public void update(final AccountModelDao account, final InternalCallContext context) {
+        super.update(account, context);
+
+        final AccountModelDao currentAccount = getById(account.getId(), context);
+        final Long accountRecordId = getRecordId(account.getId(), context);
+        final long tenantRecordId = context == null ? InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID
+                                                    : context.getTenantRecordId();
+        final AccountChangeInternalEvent changeEvent = new DefaultAccountChangeEvent(account.getId(), null, currentAccount, account,
+                                                                                     accountRecordId, tenantRecordId);
+        if (changeEvent.hasChanges()) {
+            try {
+                eventBus.post(changeEvent, context);
+            } catch (final EventBusException ex) {
+                Assert.fail(ex.toString());
+            }
+        }
     }
 
     @Override
-    public Account getAccountByKey(final String externalKey, final InternalTenantContext context) {
-        for (final Account account : accounts.values()) {
-            if (externalKey.equals(account.getExternalKey())) {
+    public AccountModelDao getAccountByKey(final String externalKey, final InternalTenantContext context) {
+        for (final Map<Long, AccountModelDao> accountRow : entities.values()) {
+            final AccountModelDao account = accountRow.values().iterator().next();
+            if (account.getExternalKey().equals(externalKey)) {
                 return account;
             }
         }
+
         return null;
     }
 
     @Override
     public UUID getIdFromKey(final String externalKey, final InternalTenantContext context) {
-        final Account account = getAccountByKey(externalKey, context);
+        final AccountModelDao account = getAccountByKey(externalKey, context);
         return account == null ? null : account.getId();
     }
 
     @Override
-    public void update(final Account account, final InternalCallContext context) {
-        final Account currentAccount = accounts.put(account.getId(), account);
+    public void updatePaymentMethod(final UUID accountId, final UUID paymentMethodId, final InternalCallContext context) throws AccountApiException {
+        final AccountModelDao currentAccountModelDao = getById(accountId, context);
+        if (currentAccountModelDao == null) {
+            throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, accountId);
+        }
 
-        final AccountChangeInternalEvent changeEvent = new DefaultAccountChangeEvent(account.getId(), null, currentAccount, account, 1L, 1L);
-        if (changeEvent.hasChanges()) {
-            try {
-                eventBus.post(changeEvent, context);
-            } catch (final EventBusException ex) {
-                throw new RuntimeException(ex);
-            }
+        final DefaultAccount currentAccount = new DefaultAccount(currentAccountModelDao);
+        final DefaultMutableAccountData updatedAccount = new DefaultMutableAccountData(currentAccount);
+        updatedAccount.setPaymentMethodId(paymentMethodId);
+
+        update(new AccountModelDao(accountId, updatedAccount), context);
+    }
+
+    @Override
+    public void addEmail(final AccountEmailModelDao email, final InternalCallContext context) {
+        try {
+            accountEmailSqlDao.create(email, context);
+        } catch (BillingExceptionBase billingExceptionBase) {
+            Assert.fail(billingExceptionBase.toString());
         }
     }
 
     @Override
-    public void updatePaymentMethod(final UUID accountId, final UUID paymentMethodId, final InternalCallContext context) throws EntityPersistenceException {
+    public void removeEmail(final AccountEmailModelDao email, final InternalCallContext context) {
+        accountEmailSqlDao.delete(email, context);
     }
 
     @Override
-    public Account getByRecordId(final Long recordId, final InternalTenantContext context) {
-        return null;
+    public List<AccountEmailModelDao> getEmailsByAccountId(final UUID accountId, final InternalTenantContext context) {
+        return ImmutableList.<AccountEmailModelDao>copyOf(Collections2.filter(accountEmailSqlDao.get(context), new Predicate<AccountEmailModelDao>() {
+            @Override
+            public boolean apply(final AccountEmailModelDao input) {
+                return input.getAccountId().equals(accountId);
+            }
+        }));
     }
 }
diff --git a/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
index 0a6b546..2e2f87d 100644
--- a/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
@@ -16,511 +16,316 @@
 
 package com.ning.billing.account.dao;
 
-import java.util.ArrayList;
+import java.sql.SQLException;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
 import org.joda.time.DateTimeZone;
-import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.exceptions.TransactionFailedException;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.ning.billing.ErrorCode;
 import com.ning.billing.ObjectType;
-import com.ning.billing.account.api.Account;
+import com.ning.billing.account.AccountTestBase;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.AccountEmail;
 import com.ning.billing.account.api.BillCycleDay;
 import com.ning.billing.account.api.DefaultAccount;
 import com.ning.billing.account.api.DefaultAccountEmail;
-import com.ning.billing.account.api.DefaultBillCycleDay;
-import com.ning.billing.account.api.DefaultMutableAccountData;
 import com.ning.billing.account.api.MutableAccountData;
-import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.mock.MockAccountBuilder;
+import com.ning.billing.util.api.AuditLevel;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.api.TagApiException;
+import com.ning.billing.util.api.TagDefinitionApiException;
+import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.customfield.CustomField;
 import com.ning.billing.util.customfield.StringCustomField;
-import com.ning.billing.util.customfield.dao.AuditedCustomFieldDao;
-import com.ning.billing.util.customfield.dao.CustomFieldDao;
-import com.ning.billing.util.entity.EntityPersistenceException;
-import com.ning.billing.util.tag.DefaultTagDefinition;
+import com.ning.billing.util.customfield.dao.CustomFieldModelDao;
+import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.tag.DescriptiveTag;
 import com.ning.billing.util.tag.Tag;
-import com.ning.billing.util.tag.TagDefinition;
-import com.ning.billing.util.tag.dao.AuditedTagDao;
-import com.ning.billing.util.tag.dao.TagDao;
-import com.ning.billing.util.tag.dao.TagDefinitionSqlDao;
+import com.ning.billing.util.tag.dao.TagDefinitionModelDao;
+import com.ning.billing.util.tag.dao.TagModelDao;
 
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
+public class TestAccountDao extends AccountTestBase {
 
-public class TestAccountDao extends AccountDaoTestBase {
+    @Test(groups = "slow")
+    public void testBasic() throws AccountApiException {
+        final AccountModelDao account = createTestAccount();
+        accountDao.create(account, internalCallContext);
 
-    private Account createTestAccount() {
-        return createTestAccount(5, UUID.randomUUID().toString().substring(0, 5));
-    }
+        // Retrieve by key
+        AccountModelDao retrievedAccount = accountDao.getAccountByKey(account.getExternalKey(), internalCallContext);
+        checkAccountsEqual(retrievedAccount, account);
 
-    private Account createTestAccount(final int billCycleDay) {
-        return createTestAccount(billCycleDay, "123-456-7890");
-    }
+        // Retrieve by id
+        retrievedAccount = accountDao.getById(retrievedAccount.getId(), internalCallContext);
+        checkAccountsEqual(retrievedAccount, account);
 
-    private Account createTestAccount(final int billCycleDay, final String phone) {
-        final String thisKey = "test" + UUID.randomUUID().toString();
-        final String lastName = UUID.randomUUID().toString();
-        final String thisEmail = "me@me.com" + " " + UUID.randomUUID();
-        final String firstName = "Bob";
-        final String name = firstName + " " + lastName;
-        final String locale = "EN-US";
-        final DateTimeZone timeZone = DateTimeZone.forID("America/Los_Angeles");
-        final int firstNameLength = firstName.length();
-
-        return new DefaultAccount(UUID.randomUUID(), thisKey, thisEmail, name, firstNameLength, Currency.USD,
-                                  new DefaultBillCycleDay(billCycleDay, billCycleDay), UUID.randomUUID(), timeZone, locale,
-                                  null, null, null, null, null, null, null, // add null address fields
-                                  phone, false, false);
-    }
+        // Retrieve all
+        final List<AccountModelDao> all = accountDao.get(internalCallContext);
+        Assert.assertNotNull(all);
+        Assert.assertEquals(all.size(), 1);
+        checkAccountsEqual(all.get(0), account);
 
-    @Test(groups = "slow")
-    public void testBasic() throws EntityPersistenceException {
-        final Account a = createTestAccount(5);
-        accountDao.create(a, internalCallContext);
-        final String key = a.getExternalKey();
-
-        Account r = accountDao.getAccountByKey(key, internalCallContext);
-        assertNotNull(r);
-        assertEquals(r.getExternalKey(), a.getExternalKey());
-
-        r = accountDao.getById(r.getId(), internalCallContext);
-        assertNotNull(r);
-        assertEquals(r.getExternalKey(), a.getExternalKey());
-
-        final List<Account> all = accountDao.get(internalCallContext);
-        assertNotNull(all);
-        assertTrue(all.size() >= 1);
+        // Verify audits
+        final List<AuditLog> auditLogsForAccount = auditDao.getAuditLogsForId(TableName.ACCOUNT, account.getId(), AuditLevel.FULL, internalCallContext);
+        Assert.assertEquals(auditLogsForAccount.size(), 1);
+        Assert.assertEquals(auditLogsForAccount.get(0).getChangeType(), ChangeType.INSERT);
     }
 
-    // simple test to ensure long phone numbers can be stored
+    // Simple test to ensure long phone numbers can be stored
     @Test(groups = "slow")
-    public void testLongPhoneNumber() throws EntityPersistenceException {
-        final Account account = createTestAccount(1, "123456789012345678901234");
+    public void testLongPhoneNumber() throws AccountApiException {
+        final AccountModelDao account = createTestAccount("123456789012345678901234");
         accountDao.create(account, internalCallContext);
 
-        final Account saved = accountDao.getAccountByKey(account.getExternalKey(), internalCallContext);
-        assertNotNull(saved);
-    }
-
-    // simple test to ensure excessively long phone numbers cannot be stored
-    @Test(groups = "slow", expectedExceptions = EntityPersistenceException.class)
-    public void testOverlyLongPhoneNumber() throws EntityPersistenceException {
-        final Account account = createTestAccount(1, "12345678901234567890123456");
-        accountDao.create(account, internalCallContext);
+        final AccountModelDao retrievedAccount = accountDao.getAccountByKey(account.getExternalKey(), internalCallContext);
+        checkAccountsEqual(retrievedAccount, account);
     }
 
+    // Simple test to ensure excessively long phone numbers cannot be stored
     @Test(groups = "slow")
-    public void testGetById() throws EntityPersistenceException {
-        Account account = createTestAccount(1);
-        final UUID id = account.getId();
-        final String key = account.getExternalKey();
-        final String name = account.getName();
-        final Integer firstNameLength = account.getFirstNameLength();
-
-        accountDao.create(account, internalCallContext);
-
-        account = accountDao.getById(id, internalCallContext);
-        assertNotNull(account);
-        assertEquals(account.getId(), id);
-        assertEquals(account.getExternalKey(), key);
-        assertEquals(account.getName(), name);
-        assertEquals(account.getFirstNameLength(), firstNameLength);
+    public void testOverlyLongPhoneNumber() throws AccountApiException {
+        final AccountModelDao account = createTestAccount("12345678901234567890123456");
+        try {
+            accountDao.create(account, internalCallContext);
+            Assert.fail();
+        } catch (RuntimeException e) {
+            Assert.assertTrue(e.getCause() instanceof SQLException);
+        }
     }
 
     @Test(groups = "slow")
-    public void testCustomFields() throws EntityPersistenceException {
-        final String fieldName = "testField1";
-        final String fieldValue = "testField1_value";
-
+    public void testCustomFields() throws CustomFieldApiException {
         final UUID accountId = UUID.randomUUID();
-        final List<CustomField> customFields = new ArrayList<CustomField>();
-        customFields.add(new StringCustomField(fieldName, fieldValue));
-        final CustomFieldDao customFieldDao = new AuditedCustomFieldDao(dbi);
-        customFieldDao.saveEntities(accountId, ObjectType.ACCOUNT, customFields, internalCallContext);
-
-        final Map<String, CustomField> customFieldMap = customFieldDao.loadEntities(accountId, ObjectType.ACCOUNT, internalCallContext);
-        assertEquals(customFieldMap.size(), 1);
-        final CustomField customField = customFieldMap.get(fieldName);
-        assertEquals(customField.getName(), fieldName);
-        assertEquals(customField.getValue(), fieldValue);
-    }
+        final String fieldName = UUID.randomUUID().toString().substring(0, 4);
+        final String fieldValue = UUID.randomUUID().toString();
 
-    @Test(groups = "slow")
-    public void testTags() throws EntityPersistenceException, TagApiException {
-        final Account account = createTestAccount(1);
-        final TagDefinition definition = new DefaultTagDefinition("Test Tag", "For testing only", false);
-        final TagDefinitionSqlDao tagDescriptionDao = dbi.onDemand(TagDefinitionSqlDao.class);
-        tagDescriptionDao.create(definition, internalCallContext);
+        final CustomField field = new StringCustomField(fieldName, fieldValue, ObjectType.ACCOUNT, accountId, internalCallContext.getCreatedDate());
+        customFieldDao.create(new CustomFieldModelDao(field), internalCallContext);
 
-        final TagDao tagDao = new AuditedTagDao(dbi, tagEventBuilder, bus);
-        tagDao.insertTag(account.getId(), ObjectType.ACCOUNT, definition.getId(), internalCallContext);
+        final List<CustomFieldModelDao> customFieldMap = customFieldDao.getCustomFields(accountId, ObjectType.ACCOUNT, internalCallContext);
+        Assert.assertEquals(customFieldMap.size(), 1);
 
-        final Map<String, Tag> tagMap = tagDao.loadEntities(account.getId(), ObjectType.ACCOUNT, internalCallContext);
-        assertEquals(tagMap.size(), 1);
+        final CustomFieldModelDao customField = customFieldMap.get(0);
+        Assert.assertEquals(customField.getFieldName(), fieldName);
+        Assert.assertEquals(customField.getFieldValue(), fieldValue);
+    }
 
-        assertEquals(tagMap.values().iterator().next().getTagDefinitionId(), definition.getId());
+    @Test(groups = "slow")
+    public void testTags() throws TagApiException, TagDefinitionApiException {
+        final AccountModelDao account = createTestAccount();
+        final TagDefinitionModelDao tagDefinition = tagDefinitionDao.create(UUID.randomUUID().toString().substring(0, 4), UUID.randomUUID().toString(), internalCallContext);
+        final Tag tag = new DescriptiveTag(tagDefinition.getId(), ObjectType.ACCOUNT, account.getId(), internalCallContext.getCreatedDate());
+        tagDao.create(new TagModelDao(tag), internalCallContext);
+
+        final List<TagModelDao> tags = tagDao.getTags(account.getId(), ObjectType.ACCOUNT, internalCallContext);
+        Assert.assertEquals(tags.size(), 1);
+        Assert.assertEquals(tags.get(0).getTagDefinitionId(), tagDefinition.getId());
+        Assert.assertEquals(tags.get(0).getObjectId(), account.getId());
+        Assert.assertEquals(tags.get(0).getObjectType(), ObjectType.ACCOUNT);
     }
 
     @Test(groups = "slow")
-    public void testGetIdFromKey() throws EntityPersistenceException {
-        final Account account = createTestAccount(1);
+    public void testGetIdFromKey() throws AccountApiException {
+        final AccountModelDao account = createTestAccount();
         accountDao.create(account, internalCallContext);
 
-        try {
-            final UUID accountId = accountDao.getIdFromKey(account.getExternalKey(), internalCallContext);
-            assertEquals(accountId, account.getId());
-        } catch (AccountApiException a) {
-            fail("Retrieving account failed.");
-        }
+        final UUID accountId = accountDao.getIdFromKey(account.getExternalKey(), internalCallContext);
+        Assert.assertEquals(accountId, account.getId());
     }
 
     @Test(groups = "slow", expectedExceptions = AccountApiException.class)
     public void testGetIdFromKeyForNullKey() throws AccountApiException {
-        final String key = null;
-        accountDao.getIdFromKey(key, internalCallContext);
+        accountDao.getIdFromKey(null, internalCallContext);
     }
 
     @Test(groups = "slow")
     public void testUpdate() throws Exception {
-        final Account account = createTestAccount(1);
+        final AccountModelDao account = createTestAccount();
         accountDao.create(account, internalCallContext);
 
-        final AccountData accountData = new MockAccountBuilder(account).migrated(false)
-                                                                       .isNotifiedForInvoices(false)
-                                                                       .timeZone(DateTimeZone.forID("Australia/Darwin"))
-                                                                       .locale("FR-CA")
-                                                                       .build();
-
-        final Account updatedAccount = new DefaultAccount(account.getId(), accountData);
+        final AccountData accountData = new MockAccountBuilder(new DefaultAccount(account)).migrated(false)
+                                                                                           .isNotifiedForInvoices(false)
+                                                                                           .timeZone(DateTimeZone.forID("Australia/Darwin"))
+                                                                                           .locale("FR-CA")
+                                                                                           .build();
+        final AccountModelDao updatedAccount = new AccountModelDao(account.getId(), accountData);
         accountDao.update(updatedAccount, internalCallContext);
 
-        final Account savedAccount = accountDao.getAccountByKey(account.getExternalKey(), internalCallContext);
-
-        assertNotNull(savedAccount);
-        assertEquals(savedAccount.getName(), updatedAccount.getName());
-        assertEquals(savedAccount.getEmail(), updatedAccount.getEmail());
-        assertEquals(savedAccount.getPaymentMethodId(), updatedAccount.getPaymentMethodId());
-        assertEquals(savedAccount.getBillCycleDay(), updatedAccount.getBillCycleDay());
-        assertEquals(savedAccount.getFirstNameLength(), updatedAccount.getFirstNameLength());
-        assertEquals(savedAccount.getTimeZone(), updatedAccount.getTimeZone());
-        assertEquals(savedAccount.getLocale(), updatedAccount.getLocale());
-        assertEquals(savedAccount.getAddress1(), updatedAccount.getAddress1());
-        assertEquals(savedAccount.getAddress2(), updatedAccount.getAddress2());
-        assertEquals(savedAccount.getCity(), updatedAccount.getCity());
-        assertEquals(savedAccount.getStateOrProvince(), updatedAccount.getStateOrProvince());
-        assertEquals(savedAccount.getCountry(), updatedAccount.getCountry());
-        assertEquals(savedAccount.getPostalCode(), updatedAccount.getPostalCode());
-        assertEquals(savedAccount.getPhone(), updatedAccount.getPhone());
+        final AccountModelDao retrievedAccount = accountDao.getAccountByKey(account.getExternalKey(), internalCallContext);
+        checkAccountsEqual(retrievedAccount, updatedAccount);
     }
 
     @Test(groups = "slow")
     public void testUpdatePaymentMethod() throws Exception {
-        final Account account = createTestAccount(1);
+        final AccountModelDao account = createTestAccount();
         accountDao.create(account, internalCallContext);
 
         final UUID newPaymentMethodId = UUID.randomUUID();
         accountDao.updatePaymentMethod(account.getId(), newPaymentMethodId, internalCallContext);
 
-        final Account newAccount = accountDao.getById(account.getId(), internalCallContext);
-        assertEquals(newAccount.getPaymentMethodId(), newPaymentMethodId);
+        final AccountModelDao newAccount = accountDao.getById(account.getId(), internalCallContext);
+        Assert.assertEquals(newAccount.getPaymentMethodId(), newPaymentMethodId);
 
-        // And then set it to null
+        // And then set it to null (delete the default payment method)
         accountDao.updatePaymentMethod(account.getId(), null, internalCallContext);
 
-        final Account newAccountWithPMNull = accountDao.getById(account.getId(), internalCallContext);
-        assertNull(newAccountWithPMNull.getPaymentMethodId());
-
-    }
-
-    @Test(groups = "slow")
-    public void testAddingContactInformation() throws Exception {
-        final UUID accountId = UUID.randomUUID();
-        final DefaultAccount account = new DefaultAccount(accountId, "extKey123456", "myemail123456@glam.com",
-                                                          "John Smith", 4, Currency.USD, new DefaultBillCycleDay(15), null,
-                                                          DateTimeZone.forID("America/Cambridge_Bay"), "EN-CA",
-                                                          null, null, null, null, null, null, null, null, false, false);
-        accountDao.create(account, internalCallContext);
-
-        final String address1 = "123 address 1";
-        final String address2 = "456 address 2";
-        final String companyName = "Some Company";
-        final String city = "Cambridge Bay";
-        final String stateOrProvince = "Nunavut";
-        final String country = "Canada";
-        final String postalCode = "X0B 0C0";
-        final String phone = "18001112222";
-
-        final DefaultAccount updatedAccount = new DefaultAccount(accountId, "extKey123456", "myemail123456@glam.com",
-                                                                 "John Smith", 4, Currency.USD, new DefaultBillCycleDay(15), null,
-                                                                 DateTimeZone.forID("America/Cambridge_Bay"), "EN-CA",
-                                                                 address1, address2, companyName, city, stateOrProvince, country,
-                                                                 postalCode, phone, false, false);
-
-        accountDao.update(updatedAccount, internalCallContext);
-
-        final Account savedAccount = accountDao.getById(accountId, internalCallContext);
-
-        assertNotNull(savedAccount);
-        assertEquals(savedAccount.getId(), accountId);
-        assertEquals(savedAccount.getAddress1(), address1);
-        assertEquals(savedAccount.getAddress2(), address2);
-        assertEquals(savedAccount.getCompanyName(), companyName);
-        assertEquals(savedAccount.getCity(), city);
-        assertEquals(savedAccount.getStateOrProvince(), stateOrProvince);
-        assertEquals(savedAccount.getCity(), city);
-        assertEquals(savedAccount.getPostalCode(), postalCode);
-        assertEquals(savedAccount.getPhone(), phone);
-    }
-
-    @Test(groups = "slow", expectedExceptions = IllegalArgumentException.class)
-    public void testShouldntBeAbleToUpdateExternalKey() throws Exception {
-        final Account account = createTestAccount();
-        accountDao.create(account, internalCallContext);
-
-        final MutableAccountData otherAccount = account.toMutableAccountData();
-        otherAccount.setExternalKey(UUID.randomUUID().toString());
-
-        accountDao.update(new DefaultAccount(account.getId(), otherAccount), internalCallContext);
-    }
-
-    @Test(groups = "slow", expectedExceptions = IllegalArgumentException.class)
-    public void testShouldntBeAbleToUpdateCurrency() throws Exception {
-        final Account account = createTestAccount();
-        accountDao.create(account, internalCallContext);
-
-        final MutableAccountData otherAccount = account.toMutableAccountData();
-        otherAccount.setCurrency(Currency.GBP);
-
-        accountDao.update(new DefaultAccount(account.getId(), otherAccount), internalCallContext);
-    }
-
-    @Test(groups = "slow", expectedExceptions = IllegalArgumentException.class)
-    public void testShouldntBeAbleToUpdateBillCycleDay() throws Exception {
-        final Account account = createTestAccount();
-        accountDao.create(account, internalCallContext);
-
-        final MutableAccountData otherAccount = account.toMutableAccountData();
-        otherAccount.setBillCycleDay(new BillCycleDay() {
-            @Override
-            public int getDayOfMonthUTC() {
-                return account.getBillCycleDay().getDayOfMonthUTC() + 2;
-            }
-
-            @Override
-            public int getDayOfMonthLocal() {
-                return account.getBillCycleDay().getDayOfMonthLocal() + 2;
-            }
-        });
-
-        accountDao.update(new DefaultAccount(account.getId(), otherAccount), internalCallContext);
+        final AccountModelDao newAccountWithPMNull = accountDao.getById(account.getId(), internalCallContext);
+        Assert.assertNull(newAccountWithPMNull.getPaymentMethodId());
     }
 
     @Test(groups = "slow")
     public void testShouldBeAbleToUpdateSomeFields() throws Exception {
-        final Account account = createTestAccount();
+        final AccountModelDao account = createTestAccount();
         accountDao.create(account, internalCallContext);
 
-        final MutableAccountData otherAccount = account.toMutableAccountData();
+        final MutableAccountData otherAccount = new DefaultAccount(account).toMutableAccountData();
         otherAccount.setAddress1(UUID.randomUUID().toString());
         otherAccount.setEmail(UUID.randomUUID().toString());
-
-        final DefaultAccount newAccount = new DefaultAccount(account.getId(), otherAccount);
-        accountDao.update(newAccount, internalCallContext);
-
-        Assert.assertEquals(accountDao.getById(account.getId(), internalCallContext), newAccount);
-    }
-
-    @Test(groups = "slow")
-    public void testShouldBeAbleToPassNullForSomeFieldsToAvoidUpdate() throws Exception {
-        final Account account = createTestAccount();
-        accountDao.create(account, internalCallContext);
-
-        // Update the address and leave other fields null
-        final MutableAccountData mutableAccountData = new DefaultMutableAccountData(null, null, null, 0, null, null, null,
-                                                                                    null, null, null, null, null, null, null,
-                                                                                    null, null, null, null, false, false);
-        final String newAddress1 = UUID.randomUUID().toString();
-        mutableAccountData.setAddress1(newAddress1);
-
-        final DefaultAccount newAccount = new DefaultAccount(account.getId(), mutableAccountData);
+        final AccountModelDao newAccount = new AccountModelDao(account.getId(), otherAccount);
         accountDao.update(newAccount, internalCallContext);
 
-        Assert.assertEquals(accountDao.getById(account.getId(), internalCallContext).getAddress1(), newAddress1);
-        Assert.assertEquals(accountDao.getById(account.getId(), internalCallContext).getAddress2(), account.getAddress2());
-        Assert.assertEquals(accountDao.getById(account.getId(), internalCallContext).getCurrency(), account.getCurrency());
-        Assert.assertEquals(accountDao.getById(account.getId(), internalCallContext).getExternalKey(), account.getExternalKey());
-        Assert.assertEquals(accountDao.getById(account.getId(), internalCallContext).getBillCycleDay(), account.getBillCycleDay());
+        final AccountModelDao retrievedAccount = accountDao.getById(account.getId(), internalCallContext);
+        checkAccountsEqual(retrievedAccount, newAccount);
     }
 
     @Test(groups = "slow")
     public void testShouldBeAbleToHandleOtherBCDClass() throws Exception {
-        final Account account = createTestAccount();
+        final AccountModelDao account = createTestAccount();
         accountDao.create(account, internalCallContext);
 
-        final MutableAccountData otherAccount = account.toMutableAccountData();
+        final MutableAccountData otherAccount = new DefaultAccount(account).toMutableAccountData();
         otherAccount.setAddress1(UUID.randomUUID().toString());
         otherAccount.setEmail(UUID.randomUUID().toString());
-        // Same BCD, but not .equals method
+        // Same BCD, but no .equals method
         otherAccount.setBillCycleDay(new BillCycleDay() {
             @Override
             public int getDayOfMonthUTC() {
-                return account.getBillCycleDay().getDayOfMonthUTC();
+                return account.getBillingCycleDayUtc();
             }
 
             @Override
             public int getDayOfMonthLocal() {
-                return account.getBillCycleDay().getDayOfMonthLocal();
+                return account.getBillingCycleDayLocal();
             }
         });
 
-        final DefaultAccount newAccount = new DefaultAccount(account.getId(), otherAccount);
+        final AccountModelDao newAccount = new AccountModelDao(account.getId(), otherAccount);
         accountDao.update(newAccount, internalCallContext);
 
-        final Account newFetchedAccount = accountDao.getById(account.getId(), internalCallContext);
-        Assert.assertEquals(newFetchedAccount.getAddress1(), newAccount.getAddress1());
-        Assert.assertEquals(newFetchedAccount.getEmail(), newAccount.getEmail());
-        // Same BCD
-        Assert.assertEquals(newFetchedAccount.getBillCycleDay(), account.getBillCycleDay());
+        final AccountModelDao newFetchedAccount = accountDao.getById(account.getId(), internalCallContext);
+        checkAccountsEqual(newFetchedAccount, newAccount);
+        // Verify it is the same BCD
+        Assert.assertEquals(newFetchedAccount.getBillingCycleDayUtc(), account.getBillingCycleDayUtc());
+        Assert.assertEquals(newFetchedAccount.getBillingCycleDayLocal(), account.getBillingCycleDayLocal());
     }
 
     @Test(groups = "slow")
     public void testShouldBeAbleToHandleBCDOfZeroZero() throws Exception {
-        final Account account = createTestAccount(0);
+        final AccountModelDao account = createTestAccount(0);
         accountDao.create(account, internalCallContext);
-        final Account fetchedAccount = accountDao.getById(account.getId(), internalCallContext);
 
-        final MutableAccountData otherAccount = account.toMutableAccountData();
+        final MutableAccountData otherAccount = new DefaultAccount(account).toMutableAccountData();
         // Set BCD to null
         otherAccount.setBillCycleDay(null);
 
-        final DefaultAccount newAccount = new DefaultAccount(account.getId(), otherAccount);
+        final AccountModelDao newAccount = new AccountModelDao(account.getId(), otherAccount);
         accountDao.update(newAccount, internalCallContext);
 
         // Same BCD (zero/zero)
-        Assert.assertEquals(accountDao.getById(account.getId(), internalCallContext), fetchedAccount);
+        final AccountModelDao retrievedAccount = accountDao.getById(account.getId(), internalCallContext);
+        checkAccountsEqual(retrievedAccount, account);
     }
 
     @Test(groups = "slow")
-    public void testHandleDuplicateEmails() {
+    public void testHandleDuplicateEmails() throws AccountApiException {
         final UUID accountId = UUID.randomUUID();
         final AccountEmail email = new DefaultAccountEmail(accountId, "test@gmail.com");
-        Assert.assertEquals(accountEmailDao.getEmails(accountId, internalCallContext).size(), 0);
+        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, internalCallContext).size(), 0);
 
-        accountEmailDao.addEmail(accountId, email, internalCallContext);
-        Assert.assertEquals(accountEmailDao.getEmails(accountId, internalCallContext).size(), 1);
+        final AccountEmailModelDao accountEmailModelDao = new AccountEmailModelDao(email);
+        accountDao.addEmail(accountEmailModelDao, internalCallContext);
+        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, internalCallContext).size(), 1);
 
-        accountEmailDao.addEmail(accountId, email, internalCallContext);
-        Assert.assertEquals(accountEmailDao.getEmails(accountId, internalCallContext).size(), 1);
+        try {
+            accountDao.addEmail(accountEmailModelDao, internalCallContext);
+            Assert.fail();
+        } catch (TransactionFailedException e) {
+            Assert.assertTrue(e.getCause() instanceof AccountApiException);
+            Assert.assertEquals(((AccountApiException) e.getCause()).getCode(), ErrorCode.ACCOUNT_EMAIL_ALREADY_EXISTS.getCode());
+        }
     }
 
     @Test(groups = "slow")
-    public void testAccountEmail() {
-        List<AccountEmail> emails = new ArrayList<AccountEmail>();
-
-        // generate random account id
+    public void testAddRemoveAccountEmail() throws AccountApiException {
         final UUID accountId = UUID.randomUUID();
 
-        // add a new e-mail
+        // Add a new e-mail
         final AccountEmail email = new DefaultAccountEmail(accountId, "test@gmail.com");
-        emails.add(email);
-        accountEmailDao.saveEmails(accountId, emails, internalCallContext);
-        emails = accountEmailDao.getEmails(accountId, internalCallContext);
-        assertEquals(emails.size(), 1);
-
-        // verify that history and audit contain one entry
-        verifyAccountEmailAuditAndHistoryCount(accountId, 1);
-
-        // update e-mail
-        final AccountEmail updatedEmail = new DefaultAccountEmail(email, "test2@gmail.com");
-        emails.clear();
-        emails.add(updatedEmail);
-        accountEmailDao.saveEmails(accountId, emails, internalCallContext);
-        emails = accountEmailDao.getEmails(accountId, internalCallContext);
-        assertEquals(emails.size(), 1);
-
-        // verify that history and audit contain three entries
-        // two inserts and one delete
-        verifyAccountEmailAuditAndHistoryCount(accountId, 3);
-
-        // delete e-mail
-        accountEmailDao.saveEmails(accountId, new ArrayList<AccountEmail>(), internalCallContext);
-        emails = accountEmailDao.getEmails(accountId, internalCallContext);
-        assertEquals(emails.size(), 0);
-
-        // verify that history and audit contain four entries
-        verifyAccountEmailAuditAndHistoryCount(accountId, 4);
+        accountDao.addEmail(new AccountEmailModelDao(email), internalCallContext);
+
+        final List<AccountEmailModelDao> accountEmails = accountDao.getEmailsByAccountId(accountId, internalCallContext);
+        Assert.assertEquals(accountEmails.size(), 1);
+        Assert.assertEquals(accountEmails.get(0).getAccountId(), accountId);
+        Assert.assertEquals(accountEmails.get(0).getEmail(), email.getEmail());
+
+        // Verify audits
+        final List<AuditLog> auditLogsForAccountEmail = auditDao.getAuditLogsForId(TableName.ACCOUNT_EMAIL, email.getId(), AuditLevel.FULL, internalCallContext);
+        Assert.assertEquals(auditLogsForAccountEmail.size(), 1);
+        Assert.assertEquals(auditLogsForAccountEmail.get(0).getChangeType(), ChangeType.INSERT);
+
+        // Delete the e-mail
+        accountDao.removeEmail(new AccountEmailModelDao(email), internalCallContext);
+        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, internalCallContext).size(), 0);
     }
 
     @Test(groups = "slow")
-    public void testAddAndRemoveAccountEmail() {
+    public void testAddAndRemoveMultipleAccountEmails() throws AccountApiException {
         final UUID accountId = UUID.randomUUID();
         final String email1 = UUID.randomUUID().toString();
         final String email2 = UUID.randomUUID().toString();
 
         // Verify the original state
-        assertEquals(accountEmailDao.getEmails(accountId, internalCallContext).size(), 0);
+        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, internalCallContext).size(), 0);
 
         // Add a new e-mail
         final AccountEmail accountEmail1 = new DefaultAccountEmail(accountId, email1);
-        accountEmailDao.addEmail(accountId, accountEmail1, internalCallContext);
-        final List<AccountEmail> firstEmails = accountEmailDao.getEmails(accountId, internalCallContext);
-        assertEquals(firstEmails.size(), 1);
-        assertEquals(firstEmails.get(0).getAccountId(), accountId);
-        assertEquals(firstEmails.get(0).getEmail(), email1);
+        accountDao.addEmail(new AccountEmailModelDao(accountEmail1), internalCallContext);
+        final List<AccountEmailModelDao> firstEmails = accountDao.getEmailsByAccountId(accountId, internalCallContext);
+        Assert.assertEquals(firstEmails.size(), 1);
+        Assert.assertEquals(firstEmails.get(0).getAccountId(), accountId);
+        Assert.assertEquals(firstEmails.get(0).getEmail(), email1);
 
         // Add a second e-mail
         final AccountEmail accountEmail2 = new DefaultAccountEmail(accountId, email2);
-        accountEmailDao.addEmail(accountId, accountEmail2, internalCallContext);
-        final List<AccountEmail> secondEmails = accountEmailDao.getEmails(accountId, internalCallContext);
-        assertEquals(secondEmails.size(), 2);
-        assertTrue(secondEmails.get(0).getAccountId().equals(accountId));
-        assertTrue(secondEmails.get(1).getAccountId().equals(accountId));
-        assertTrue(secondEmails.get(0).getEmail().equals(email1) || secondEmails.get(0).getEmail().equals(email2));
-        assertTrue(secondEmails.get(1).getEmail().equals(email1) || secondEmails.get(1).getEmail().equals(email2));
+        accountDao.addEmail(new AccountEmailModelDao(accountEmail2), internalCallContext);
+        final List<AccountEmailModelDao> secondEmails = accountDao.getEmailsByAccountId(accountId, internalCallContext);
+        Assert.assertEquals(secondEmails.size(), 2);
+        Assert.assertTrue(secondEmails.get(0).getAccountId().equals(accountId));
+        Assert.assertTrue(secondEmails.get(1).getAccountId().equals(accountId));
+        Assert.assertTrue(secondEmails.get(0).getEmail().equals(email1) || secondEmails.get(0).getEmail().equals(email2));
+        Assert.assertTrue(secondEmails.get(1).getEmail().equals(email1) || secondEmails.get(1).getEmail().equals(email2));
 
         // Delete the first e-mail
-        accountEmailDao.removeEmail(accountId, accountEmail1, internalCallContext);
-        final List<AccountEmail> thirdEmails = accountEmailDao.getEmails(accountId, internalCallContext);
-        assertEquals(thirdEmails.size(), 1);
-        assertEquals(thirdEmails.get(0).getAccountId(), accountId);
-        assertEquals(thirdEmails.get(0).getEmail(), email2);
-
-        // Verify that history and audit contain three entries (2 inserts and one delete)
-        verifyAccountEmailAuditAndHistoryCount(accountId, 3);
-    }
-
-    private void verifyAccountEmailAuditAndHistoryCount(final UUID accountId, final int expectedCount) {
-        final Handle handle = dbi.open();
-
-        // verify audit
-        StringBuilder sb = new StringBuilder();
-        sb.append("select * from audit_log a ");
-        sb.append("inner join account_email_history aeh on a.record_id = aeh.history_record_id ");
-        sb.append("where a.table_name = 'account_email_history' ");
-        sb.append(String.format("and aeh.account_id='%s'", accountId.toString()));
-        List<Map<String, Object>> result = handle.select(sb.toString());
-        assertEquals(result.size(), expectedCount);
-
-        // ***** NOT IDEAL
-        // ... but this works after the email record has been deleted; will likely fail when multiple emails exist for the same account
-        // verify history table
-        sb = new StringBuilder();
-        sb.append("select * from account_email_history aeh ");
-        sb.append(String.format("where aeh.account_id='%s'", accountId.toString()));
-        result = handle.select(sb.toString());
-        assertEquals(result.size(), expectedCount);
-
-        handle.close();
+        accountDao.removeEmail(new AccountEmailModelDao(accountEmail1), internalCallContext);
+        final List<AccountEmailModelDao> thirdEmails = accountDao.getEmailsByAccountId(accountId, internalCallContext);
+        Assert.assertEquals(thirdEmails.size(), 1);
+        Assert.assertEquals(thirdEmails.get(0).getAccountId(), accountId);
+        Assert.assertEquals(thirdEmails.get(0).getEmail(), email2);
+
+        // Verify audits
+        final List<AuditLog> auditLogsForAccountEmail1 = auditDao.getAuditLogsForId(TableName.ACCOUNT_EMAIL, accountEmail1.getId(), AuditLevel.FULL, internalCallContext);
+        Assert.assertEquals(auditLogsForAccountEmail1.size(), 2);
+        Assert.assertEquals(auditLogsForAccountEmail1.get(0).getChangeType(), ChangeType.INSERT);
+        Assert.assertEquals(auditLogsForAccountEmail1.get(1).getChangeType(), ChangeType.DELETE);
+        final List<AuditLog> auditLogsForAccountEmail2 = auditDao.getAuditLogsForId(TableName.ACCOUNT_EMAIL, accountEmail2.getId(), AuditLevel.FULL, internalCallContext);
+        Assert.assertEquals(auditLogsForAccountEmail2.size(), 1);
+        Assert.assertEquals(auditLogsForAccountEmail2.get(0).getChangeType(), ChangeType.INSERT);
     }
 }
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 6ceacf9..0aee461 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
@@ -16,8 +16,11 @@
 
 package com.ning.billing.analytics;
 
-import com.ning.billing.account.api.AccountApiException;
-import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+import java.util.concurrent.Callable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.ning.billing.util.callcontext.CallOrigin;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
@@ -51,6 +54,7 @@ import com.google.inject.Inject;
 
 public class AnalyticsListener {
 
+    private static final Logger log = LoggerFactory.getLogger(AnalyticsListener.class);
     private static final int NB_LOCK_TRY = 5;
 
     private final BusinessSubscriptionTransitionDao bstDao;
@@ -77,40 +81,55 @@ public class AnalyticsListener {
         this.bosDao = bosDao;
         this.bipDao = bipDao;
         this.tagDao = tagDao;
-        // TODO: use accountRecordId when switching to internal events and acquire the lock for all refreshes
         this.locker = locker;
         this.internalCallContextFactory = internalCallContextFactory;
     }
 
     @Subscribe
-    public void handleEffectiveSubscriptionTransitionChange(final EffectiveSubscriptionInternalEvent eventEffective) throws AccountApiException, EntitlementUserApiException {
-        // The event is used as a trigger to rebuild all transitions for this bundle
-        bstDao.rebuildTransitionsForBundle(eventEffective.getBundleId(), createCallContext(eventEffective));
+    public void handleEffectiveSubscriptionTransitionChange(final EffectiveSubscriptionInternalEvent eventEffective) {
+        updateWithAccountLock(eventEffective.getAccountRecordId(), new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                // The event is used as a trigger to rebuild all transitions for this bundle
+                bstDao.rebuildTransitionsForBundle(eventEffective.getBundleId(), createCallContext(eventEffective));
+                return null;
+            }
+        });
     }
 
     @Subscribe
-    public void handleRequestedSubscriptionTransitionChange(final RequestedSubscriptionInternalEvent eventRequested) throws AccountApiException, EntitlementUserApiException {
-        // The event is used as a trigger to rebuild all transitions for this bundle
-        bstDao.rebuildTransitionsForBundle(eventRequested.getBundleId(), createCallContext(eventRequested));
+    public void handleRequestedSubscriptionTransitionChange(final RequestedSubscriptionInternalEvent eventRequested) {
+        updateWithAccountLock(eventRequested.getAccountRecordId(), new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                // The event is used as a trigger to rebuild all transitions for this bundle
+                bstDao.rebuildTransitionsForBundle(eventRequested.getBundleId(), createCallContext(eventRequested));
+                return null;
+            }
+        });
     }
 
     @Subscribe
     public void handleRepairEntitlement(final RepairEntitlementInternalEvent event) {
-        // In case of repair, just rebuild all transitions
-        bstDao.rebuildTransitionsForBundle(event.getBundleId(), createCallContext(event));
+        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                // In case of repair, just rebuild all transitions
+                bstDao.rebuildTransitionsForBundle(event.getBundleId(), createCallContext(event));
+                return null;
+            }
+        });
     }
 
     @Subscribe
     public void handleAccountCreation(final AccountCreationInternalEvent event) {
-        GlobalLock lock = null;
-        try {
-            lock = locker.lockWithNumberOfTries(LockerType.ACCOUNT_FOR_ANALYTICS, event.getId().toString(), NB_LOCK_TRY);
-            bacDao.accountUpdated(event.getId(), createCallContext(event));
-        } finally {
-            if (lock != null) {
-                lock.release();
+        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                bacDao.accountUpdated(event.getId(), createCallContext(event));
+                return null;
             }
-        }
+        });
     }
 
     @Subscribe
@@ -119,21 +138,25 @@ public class AnalyticsListener {
             return;
         }
 
-        GlobalLock lock = null;
-        try {
-            lock = locker.lockWithNumberOfTries(LockerType.ACCOUNT_FOR_ANALYTICS, event.getAccountId().toString(), NB_LOCK_TRY);
-            bacDao.accountUpdated(event.getAccountId(), createCallContext(event));
-        } finally {
-            if (lock != null) {
-                lock.release();
+        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                bacDao.accountUpdated(event.getAccountId(), createCallContext(event));
+                return null;
             }
-        }
+        });
     }
 
     @Subscribe
     public void handleInvoiceCreation(final InvoiceCreationInternalEvent event) {
-        // The event is used as a trigger to rebuild all invoices and invoice items for this account
-        invoiceDao.rebuildInvoicesForAccount(event.getAccountId(), createCallContext(event));
+        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                // The event is used as a trigger to rebuild all invoices and invoice items for this account
+                invoiceDao.rebuildInvoicesForAccount(event.getAccountId(), createCallContext(event));
+                return null;
+            }
+        });
     }
 
     @Subscribe
@@ -143,53 +166,101 @@ public class AnalyticsListener {
 
     @Subscribe
     public void handleInvoiceAdjustment(final InvoiceAdjustmentInternalEvent event) {
-        // The event is used as a trigger to rebuild all invoices and invoice items for this account
-        invoiceDao.rebuildInvoicesForAccount(event.getAccountId(), createCallContext(event));
+        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                // The event is used as a trigger to rebuild all invoices and invoice items for this account
+                invoiceDao.rebuildInvoicesForAccount(event.getAccountId(), createCallContext(event));
+                return null;
+            }
+        });
     }
 
     @Subscribe
     public void handlePaymentInfo(final PaymentInfoInternalEvent paymentInfo) {
-        bipDao.invoicePaymentPosted(paymentInfo.getAccountId(),
-                                    paymentInfo.getPaymentId(),
-                                    paymentInfo.getExtFirstPaymentRefId(),
-                                    paymentInfo.getExtSecondPaymentRefId(),
-                                    paymentInfo.getStatus().toString(),
-                                    createCallContext(paymentInfo));
+        updateWithAccountLock(paymentInfo.getAccountRecordId(), new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                bipDao.invoicePaymentPosted(paymentInfo.getAccountId(),
+                                            paymentInfo.getPaymentId(),
+                                            paymentInfo.getExtFirstPaymentRefId(),
+                                            paymentInfo.getExtSecondPaymentRefId(),
+                                            paymentInfo.getStatus().toString(),
+                                            createCallContext(paymentInfo));
+                return null;
+            }
+        });
     }
 
     @Subscribe
     public void handlePaymentError(final PaymentErrorInternalEvent paymentError) {
-        bipDao.invoicePaymentPosted(paymentError.getAccountId(),
-                                    paymentError.getPaymentId(),
-                                    null,
-                                    null,
-                                    paymentError.getMessage(),
-                                    createCallContext(paymentError));
+        updateWithAccountLock(paymentError.getAccountRecordId(), new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                bipDao.invoicePaymentPosted(paymentError.getAccountId(),
+                                            paymentError.getPaymentId(),
+                                            null,
+                                            null,
+                                            paymentError.getMessage(),
+                                            createCallContext(paymentError));
+                return null;
+            }
+        });
     }
 
     @Subscribe
     public void handleOverdueChange(final OverdueChangeInternalEvent changeEvent) {
-        bosDao.overdueStatusChanged(changeEvent.getOverdueObjectType(), changeEvent.getOverdueObjectId(), createCallContext(changeEvent));
+        updateWithAccountLock(changeEvent.getAccountRecordId(), new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                bosDao.overdueStatusChanged(changeEvent.getOverdueObjectType(), changeEvent.getOverdueObjectId(), createCallContext(changeEvent));
+                return null;
+            }
+        });
     }
 
     @Subscribe
     public void handleControlTagCreation(final ControlTagCreationInternalEvent event) {
-        tagDao.tagAdded(event.getObjectType(), event.getObjectId(), event.getTagDefinition().getName(), createCallContext(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;
+            }
+        });
     }
 
     @Subscribe
     public void handleControlTagDeletion(final ControlTagDeletionInternalEvent event) {
-        tagDao.tagRemoved(event.getObjectType(), event.getObjectId(), event.getTagDefinition().getName(), createCallContext(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;
+            }
+        });
     }
 
     @Subscribe
     public void handleUserTagCreation(final UserTagCreationInternalEvent event) {
-        tagDao.tagAdded(event.getObjectType(), event.getObjectId(), event.getTagDefinition().getName(), createCallContext(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;
+            }
+        });
     }
 
     @Subscribe
     public void handleUserTagDeletion(final UserTagDeletionInternalEvent event) {
-        tagDao.tagRemoved(event.getObjectType(), event.getObjectId(), event.getTagDefinition().getName(), createCallContext(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;
+            }
+        });
     }
 
     @Subscribe
@@ -213,6 +284,24 @@ public class AnalyticsListener {
     }
 
     private InternalCallContext createCallContext(final BusInternalEvent event) {
-        return internalCallContextFactory.createInternalCallContext(event.getTenantRecordId(), event.getAccountRecordId(), "AnalyticsService", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
+        return internalCallContextFactory.createInternalCallContext(event.getTenantRecordId(), event.getAccountRecordId(),
+                                                                    "AnalyticsService", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
+    }
+
+    private <T> T updateWithAccountLock(final Long accountRecordId, 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);
+            return task.call();
+        } catch (Exception e) {
+            log.warn("Exception while refreshing analytics tables", e);
+        } finally {
+            if (lock != null) {
+                lock.release();
+            }
+        }
+
+        return null;
     }
 }
diff --git a/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java b/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java
index cc434c9..e0f43e2 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java
@@ -135,8 +135,8 @@ public class DefaultAnalyticsUserApi implements AnalyticsUserApi {
         final Collection<BusinessSubscriptionTransition> businessSubscriptionTransitions = new ArrayList<BusinessSubscriptionTransition>();
         final Collection<BusinessOverdueStatus> businessOverdueStatuses = new ArrayList<BusinessOverdueStatus>();
         for (final SubscriptionBundle bundle : bundles) {
-            businessSubscriptionTransitions.addAll(getTransitionsForBundle(bundle.getKey(), context));
-            businessOverdueStatuses.addAll(getOverdueStatusesForBundle(bundle.getKey(), context));
+            businessSubscriptionTransitions.addAll(getTransitionsForBundle(bundle.getExternalKey(), context));
+            businessOverdueStatuses.addAll(getOverdueStatusesForBundle(bundle.getExternalKey(), context));
         }
 
         // Find all invoices for that account
@@ -234,7 +234,7 @@ public class DefaultAnalyticsUserApi implements AnalyticsUserApi {
 
     @Override
     public void rebuildAnalyticsForAccount(final Account account, final CallContext context) {
-        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(context);
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), context);
 
         // Update the BAC row
         bacDao.accountUpdated(account.getId(), internalCallContext);
@@ -353,7 +353,7 @@ public class DefaultAnalyticsUserApi implements AnalyticsUserApi {
     private void updateTags(final Account account, final InternalCallContext internalCallContext) {
         // Find the current state of tags from util
         final List<TagDefinition> tagDefinitions = tagInternalApi.getTagDefinitions(internalCallContext);
-        final Collection<String> utilTags = Collections2.transform(tagInternalApi.getTags(account.getId(), ObjectType.ACCOUNT, internalCallContext).values(),
+        final Collection<String> utilTags = Collections2.transform(tagInternalApi.getTags(account.getId(), ObjectType.ACCOUNT, internalCallContext),
                                                                    new Function<Tag, String>() {
                                                                        @Override
                                                                        public String apply(@Nullable final Tag input) {
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceDao.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceDao.java
index 2f09a66..b04a9d1 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceDao.java
@@ -186,7 +186,7 @@ public class BusinessInvoiceDao {
         if (invoiceItem.getBundleId() != null) {
             try {
                 final SubscriptionBundle bundle = entitlementApi.getBundleFromId(invoiceItem.getBundleId(), context);
-                externalKey = bundle.getKey();
+                externalKey = bundle.getExternalKey();
             } catch (EntitlementUserApiException e) {
                 log.warn("Ignoring subscription fields for invoice item {} for bundle {} (bundle does not exist)",
                          invoiceItem.getId().toString(),
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessOverdueStatusDao.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessOverdueStatusDao.java
index b0b30fd..82f0459 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessOverdueStatusDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessOverdueStatusDao.java
@@ -89,7 +89,7 @@ public class BusinessOverdueStatusDao {
         }
 
         final String accountKey = account.getExternalKey();
-        final String externalKey = bundle.getKey();
+        final String externalKey = bundle.getExternalKey();
 
         overdueStatusSqlDao.inTransaction(new Transaction<Void, BusinessOverdueStatusSqlDao>() {
             @Override
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionDao.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionDao.java
index c8ebb65..0ae3076 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionDao.java
@@ -110,7 +110,7 @@ public class BusinessSubscriptionTransitionDao {
                         final BusinessSubscriptionTransitionModelDao transition = new BusinessSubscriptionTransitionModelDao(
                                 event.getTotalOrdering(),
                                 bundleId,
-                                bundle.getKey(),
+                                bundle.getExternalKey(),
                                 bundle.getAccountId(),
                                 account.getExternalKey(),
                                 subscription.getId(),
@@ -130,7 +130,7 @@ public class BusinessSubscriptionTransitionDao {
                             final BusinessSubscriptionTransitionModelDao systemCancelTransition = new BusinessSubscriptionTransitionModelDao(
                                     event.getTotalOrdering(),
                                     bundleId,
-                                    bundle.getKey(),
+                                    bundle.getExternalKey(),
                                     bundle.getAccountId(),
                                     account.getExternalKey(),
                                     subscription.getId(),
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessTagDao.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessTagDao.java
index dd83f5b..78fe387 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessTagDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessTagDao.java
@@ -128,7 +128,7 @@ public class BusinessTagDao {
          * Note: we store tags associated to bundles, not to subscriptions.
          */
         final String accountKey = account.getExternalKey();
-        final String externalKey = bundle.getKey();
+        final String externalKey = bundle.getExternalKey();
         subscriptionTransitionTagSqlDao.addTag(accountKey, bundleId.toString(), externalKey, name, context);
     }
 
diff --git a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
index ca7ad62..320d651 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
@@ -51,7 +51,6 @@ public class AnalyticsTestModule extends AnalyticsModule {
         install(new ClockModule());
         install(new CallContextModule());
         install(new CustomFieldModule());
-        install(new TagStoreModule());
         install(new DefaultAccountModule());
         install(new BusModule());
         install(new DefaultEntitlementModule());
diff --git a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
index 265631d..d1c1b5d 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
@@ -33,6 +33,7 @@ import org.testng.annotations.Test;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.account.api.user.DefaultAccountCreationEvent;
+import com.ning.billing.account.dao.AccountModelDao;
 import com.ning.billing.analytics.AnalyticsTestModule;
 import com.ning.billing.analytics.AnalyticsTestSuiteWithEmbeddedDB;
 import com.ning.billing.analytics.MockDuration;
@@ -62,9 +63,14 @@ import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.events.user.ApiEventType;
-import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.invoice.api.user.DefaultInvoiceCreationEvent;
 import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.invoice.dao.InvoiceItemModelDao;
+import com.ning.billing.invoice.dao.InvoiceModelDao;
+import com.ning.billing.invoice.dao.InvoiceModelDaoHelper;
+import com.ning.billing.invoice.dao.InvoicePaymentModelDao;
 import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
 import com.ning.billing.mock.MockAccountBuilder;
@@ -83,6 +89,9 @@ import com.ning.billing.util.events.InvoiceCreationInternalEvent;
 import com.ning.billing.util.events.PaymentInfoInternalEvent;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
 
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
 import static org.testng.Assert.fail;
@@ -177,7 +186,7 @@ public class TestAnalyticsService extends AnalyticsTestSuiteWithEmbeddedDB {
 
         // Verify we correctly initialized the account subsystem
         Assert.assertNotNull(bundle);
-        Assert.assertEquals(bundle.getKey(), BUNDLE_EXTERNAL_KEY);
+        Assert.assertEquals(bundle.getExternalKey(), BUNDLE_EXTERNAL_KEY);
 
         // Create a subscription transition event
         final UUID subscriptionId = UUID.randomUUID();
@@ -219,7 +228,7 @@ public class TestAnalyticsService extends AnalyticsTestSuiteWithEmbeddedDB {
     }
 
     private void createAccountCreationEvent(final Account account) {
-        accountCreationNotification = new DefaultAccountCreationEvent(account, null, 1L, 1L);
+        accountCreationNotification = new DefaultAccountCreationEvent(new AccountModelDao(account.getId(), account), null, 1L, 1L);
     }
 
     private void createInvoiceAndPaymentCreationEvents(final Account account) {
@@ -228,8 +237,25 @@ public class TestAnalyticsService extends AnalyticsTestSuiteWithEmbeddedDB {
                                                                             INVOICE_AMOUNT, ACCOUNT_CURRENCY);
         invoice.addInvoiceItem(invoiceItem);
 
-        invoiceDao.create(invoice, invoice.getTargetDate().getDayOfMonth(), true, internalCallContext);
-        final List<Invoice> invoices = invoiceDao.getInvoicesByAccount(account.getId(), internalCallContext);
+        final InvoiceModelDao invoiceModelDao = new InvoiceModelDao(invoice);
+        final List<InvoiceItemModelDao> invoiceItemModelDaos = ImmutableList.<InvoiceItemModelDao>copyOf(Collections2.transform(invoice.getInvoiceItems(),
+                                                                                                                                new Function<InvoiceItem, InvoiceItemModelDao>() {
+                                                                                                                                    @Override
+                                                                                                                                    public InvoiceItemModelDao apply(final InvoiceItem input) {
+                                                                                                                                        return new InvoiceItemModelDao(input);
+                                                                                                                                    }
+                                                                                                                                }));
+        // Not really needed, there shouldn't be any payment at this stage
+        final List<InvoicePaymentModelDao> invoicePaymentModelDaos = ImmutableList.<InvoicePaymentModelDao>copyOf(Collections2.transform(invoice.getPayments(),
+                                                                                                                                         new Function<InvoicePayment, InvoicePaymentModelDao>() {
+                                                                                                                                             @Override
+                                                                                                                                             public InvoicePaymentModelDao apply(final InvoicePayment input) {
+                                                                                                                                                 return new InvoicePaymentModelDao(input);
+                                                                                                                                             }
+                                                                                                                                         }));
+
+        invoiceDao.createInvoice(invoiceModelDao, invoiceItemModelDaos, invoicePaymentModelDaos, true, internalCallContext);
+        final List<InvoiceModelDao> invoices = invoiceDao.getInvoicesByAccount(account.getId(), internalCallContext);
         Assert.assertEquals(invoices.size(), 1);
         Assert.assertEquals(invoices.get(0).getInvoiceItems().size(), 1);
 
@@ -237,7 +263,7 @@ public class TestAnalyticsService extends AnalyticsTestSuiteWithEmbeddedDB {
         invoiceCreationNotification = new DefaultInvoiceCreationEvent(invoice.getId(), account.getId(),
                                                                       INVOICE_AMOUNT, ACCOUNT_CURRENCY, null, 1L, 1L);
 
-        paymentInfoNotification = new DefaultPaymentInfoEvent(account.getId(), invoices.get(0).getId(), null, invoices.get(0).getBalance(), -1,
+        paymentInfoNotification = new DefaultPaymentInfoEvent(account.getId(), invoices.get(0).getId(), null, InvoiceModelDaoHelper.getBalance(invoices.get(0)), -1,
                                                               PaymentStatus.UNKNOWN, null, null, null, clock.getUTCNow(), 1L, 1L);
 
         final PaymentModelDao paymentInfo = new PaymentModelDao(account.getId(), invoice.getId(), account.getPaymentMethodId(),
diff --git a/analytics/src/test/java/com/ning/billing/analytics/TestBusinessSubscriptionTransitionRecorder.java b/analytics/src/test/java/com/ning/billing/analytics/TestBusinessSubscriptionTransitionRecorder.java
index 05e27ec..73b2443 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/TestBusinessSubscriptionTransitionRecorder.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/TestBusinessSubscriptionTransitionRecorder.java
@@ -60,7 +60,7 @@ public class TestBusinessSubscriptionTransitionRecorder extends AnalyticsTestSui
         final SubscriptionBundle bundle = Mockito.mock(SubscriptionBundle.class);
         Mockito.when(bundle.getId()).thenReturn(bundleId);
         Mockito.when(bundle.getAccountId()).thenReturn(accountId);
-        Mockito.when(bundle.getKey()).thenReturn(externalKey.toString());
+        Mockito.when(bundle.getExternalKey()).thenReturn(externalKey.toString());
         final EntitlementInternalApi entitlementApi = Mockito.mock(EntitlementInternalApi.class);
         Mockito.when(entitlementApi.getBundleFromId(Mockito.<UUID>any(), Mockito.<InternalTenantContext>any())).thenReturn(bundle);
 
diff --git a/analytics/src/test/java/com/ning/billing/analytics/TestBusinessTagRecorder.java b/analytics/src/test/java/com/ning/billing/analytics/TestBusinessTagRecorder.java
index 7e2d9cf..7bd0ae1 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/TestBusinessTagRecorder.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/TestBusinessTagRecorder.java
@@ -31,9 +31,7 @@ import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.account.api.svcs.DefaultAccountInternalApi;
 import com.ning.billing.account.api.user.DefaultAccountUserApi;
 import com.ning.billing.account.dao.AccountDao;
-import com.ning.billing.account.dao.AccountEmailDao;
-import com.ning.billing.account.dao.AuditedAccountDao;
-import com.ning.billing.account.dao.AuditedAccountEmailDao;
+import com.ning.billing.account.dao.DefaultAccountDao;
 import com.ning.billing.analytics.dao.BusinessAccountTagSqlDao;
 import com.ning.billing.analytics.dao.BusinessInvoicePaymentTagSqlDao;
 import com.ning.billing.analytics.dao.BusinessInvoiceTagSqlDao;
@@ -42,7 +40,6 @@ import com.ning.billing.catalog.DefaultCatalogService;
 import com.ning.billing.catalog.api.CatalogService;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.io.VersionedCatalogLoader;
-import com.ning.billing.util.config.CatalogConfig;
 import com.ning.billing.entitlement.alignment.PlanAligner;
 import com.ning.billing.entitlement.api.svcs.DefaultEntitlementInternalApi;
 import com.ning.billing.entitlement.api.user.DefaultEntitlementUserApi;
@@ -51,14 +48,15 @@ import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.engine.addon.AddonUtils;
-import com.ning.billing.entitlement.engine.dao.AuditedEntitlementDao;
+import com.ning.billing.entitlement.engine.dao.DefaultEntitlementDao;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.mock.MockAccountBuilder;
 import com.ning.billing.util.bus.InMemoryInternalBus;
 import com.ning.billing.util.callcontext.DefaultCallContextFactory;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
-import com.ning.billing.util.clock.DefaultClock;
+import com.ning.billing.util.config.CatalogConfig;
 import com.ning.billing.util.notificationq.DefaultNotificationQueueService;
 import com.ning.billing.util.svcapi.account.AccountInternalApi;
 import com.ning.billing.util.svcapi.entitlement.EntitlementInternalApi;
@@ -77,23 +75,22 @@ public class TestBusinessTagRecorder extends AnalyticsTestSuiteWithEmbeddedDB {
 
     @BeforeMethod(groups = "slow")
     public void setUp() throws Exception {
+        final Clock clock = new ClockMock();
         final IDBI dbi = helper.getDBI();
         accountTagSqlDao = dbi.onDemand(BusinessAccountTagSqlDao.class);
         final BusinessInvoiceTagSqlDao invoiceTagSqlDao = dbi.onDemand(BusinessInvoiceTagSqlDao.class);
         final BusinessInvoicePaymentTagSqlDao invoicePaymentTagSqlDao = dbi.onDemand(BusinessInvoicePaymentTagSqlDao.class);
         subscriptionTransitionTagSqlDao = dbi.onDemand(BusinessSubscriptionTransitionTagSqlDao.class);
         eventBus = new InMemoryInternalBus();
-        final AccountDao accountDao = new AuditedAccountDao(dbi, eventBus, new InternalCallContextFactory(dbi, new ClockMock()));
-        final AccountEmailDao accountEmailDao = new AuditedAccountEmailDao(dbi);
-        final DefaultClock clock = new DefaultClock();
+        final AccountDao accountDao = new DefaultAccountDao(dbi, eventBus, new InternalCallContextFactory(dbi, new ClockMock()));
         callContextFactory = new DefaultCallContextFactory(clock);
         final InternalCallContextFactory internalCallContextFactory = new InternalCallContextFactory(dbi, clock);
-        accountApi = new DefaultAccountInternalApi(accountDao, accountEmailDao);
-        accountUserApi = new DefaultAccountUserApi(callContextFactory, internalCallContextFactory, accountDao, accountEmailDao);
+        accountApi = new DefaultAccountInternalApi(accountDao);
+        accountUserApi = new DefaultAccountUserApi(callContextFactory, internalCallContextFactory, accountDao);
         final CatalogService catalogService = new DefaultCatalogService(Mockito.mock(CatalogConfig.class), Mockito.mock(VersionedCatalogLoader.class));
         final AddonUtils addonUtils = new AddonUtils(catalogService);
         final DefaultNotificationQueueService notificationQueueService = new DefaultNotificationQueueService(dbi, clock, internalCallContextFactory);
-        final EntitlementDao entitlementDao = new AuditedEntitlementDao(dbi, clock, addonUtils, notificationQueueService, eventBus, catalogService);
+        final EntitlementDao entitlementDao = new DefaultEntitlementDao(dbi, clock, addonUtils, notificationQueueService, eventBus, catalogService);
         final PlanAligner planAligner = new PlanAligner(catalogService);
         final DefaultSubscriptionApiService apiService = new DefaultSubscriptionApiService(clock, entitlementDao, catalogService, planAligner, internalCallContextFactory);
         final DefaultSubscriptionFactory subscriptionFactory = new DefaultSubscriptionFactory(apiService, clock, catalogService);
diff --git a/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java b/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
index da18c28..d21e392 100644
--- a/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
+++ b/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
@@ -131,7 +131,7 @@ public interface AccountUserApi {
      * @param email     the email to be added
      * @param context   the user context
      */
-    public void addEmail(UUID accountId, AccountEmail email, CallContext context);
+    public void addEmail(UUID accountId, AccountEmail email, CallContext context) throws AccountApiException;
 
     /**
      *
diff --git a/api/src/main/java/com/ning/billing/account/api/DefaultMutableAccountData.java b/api/src/main/java/com/ning/billing/account/api/DefaultMutableAccountData.java
index 694297b..e61c803 100644
--- a/api/src/main/java/com/ning/billing/account/api/DefaultMutableAccountData.java
+++ b/api/src/main/java/com/ning/billing/account/api/DefaultMutableAccountData.java
@@ -21,7 +21,6 @@ import java.util.UUID;
 import org.joda.time.DateTimeZone;
 
 import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.util.tag.TagStore;
 
 public class DefaultMutableAccountData implements MutableAccountData {
 
@@ -47,7 +46,7 @@ public class DefaultMutableAccountData implements MutableAccountData {
 
     public DefaultMutableAccountData(final String externalKey, final String email, final String name,
                                      final int firstNameLength, final Currency currency, final BillCycleDay billCycleDay,
-                                     final UUID paymentMethodId, final TagStore tags, final DateTimeZone timeZone,
+                                     final UUID paymentMethodId, final DateTimeZone 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,
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundle.java b/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundle.java
index 479dbf5..2e4504d 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundle.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundle.java
@@ -25,7 +25,7 @@ import com.ning.billing.util.entity.Entity;
 public interface SubscriptionBundle extends Blockable, Entity {
     public UUID getAccountId();
 
-    public String getKey();
+    public String getExternalKey();
 
     public OverdueState<SubscriptionBundle> getOverdueState();
 }
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index 3f85b27..5b8e22f 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -156,6 +156,8 @@ public enum ErrorCode {
     ACCOUNT_CREATION_FAILED(3006, "Account creation failed."),
     ACCOUNT_UPDATE_FAILED(3007, "Account update failed."),
 
+    ACCOUNT_EMAIL_ALREADY_EXISTS(3500, "Account email already exists %s"),
+
     /*
     *
     * Range 3900: Tag definitions
@@ -175,6 +177,8 @@ public enum ErrorCode {
     */
     TAG_DOES_NOT_EXIST(3950, "The tag does not exist (name: %s)"),
     TAG_CANNOT_BE_REMOVED(3951, "The tag %s cannot be removed because %s"),
+    TAG_ALREADY_EXISTS(3952, "The tag %s already exists"),
+
 
     /*
     *
@@ -196,6 +200,7 @@ public enum ErrorCode {
     INVOICE_NO_SUCH_EXTERNAL_CHARGE(4014, "External charge item for id %s does not exist"),
     EXTERNAL_CHARGE_AMOUNT_INVALID(4015, "External charge amount %s should be strictly positive"),
     INVOICE_WOULD_BE_NEGATIVE(4016, "Cannot execute operation, the invoice balance would become negative"),
+    INVOICE_ALREADY_EXISTS(4017, "The invoice already exists %s"),
 
     /*
      *
@@ -287,6 +292,9 @@ public enum ErrorCode {
     TENANT_UPDATE_FAILED(10005, "Tenant update failed."),
     TENANT_NO_SUCH_KEY(10006, "Tenant %s does not have a key %s"),
 
+
+    CUSTOM_FIELD_ALREADY_EXISTS(11000, "The custom field %s already exists"),
+
     __UNKNOWN_ERROR_CODE(-1, "Unknown ErrorCode");
 
     private final int code;
diff --git a/api/src/main/java/com/ning/billing/invoice/api/Invoice.java b/api/src/main/java/com/ning/billing/invoice/api/Invoice.java
index b8dff70..bb31fd5 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/Invoice.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/Invoice.java
@@ -17,6 +17,7 @@
 package com.ning.billing.invoice.api;
 
 import java.math.BigDecimal;
+import java.util.Collection;
 import java.util.List;
 import java.util.UUID;
 
@@ -29,7 +30,7 @@ public interface Invoice extends Entity {
 
     boolean addInvoiceItem(InvoiceItem item);
 
-    boolean addInvoiceItems(List<InvoiceItem> items);
+    boolean addInvoiceItems(Collection<InvoiceItem> items);
 
     List<InvoiceItem> getInvoiceItems();
 
@@ -39,7 +40,7 @@ public interface Invoice extends Entity {
 
     boolean addPayment(InvoicePayment payment);
 
-    boolean addPayments(List<InvoicePayment> payments);
+    boolean addPayments(Collection<InvoicePayment> payments);
 
     List<InvoicePayment> getPayments();
 
diff --git a/api/src/main/java/com/ning/billing/junction/api/BlockingState.java b/api/src/main/java/com/ning/billing/junction/api/BlockingState.java
index a324506..d46e799 100644
--- a/api/src/main/java/com/ning/billing/junction/api/BlockingState.java
+++ b/api/src/main/java/com/ning/billing/junction/api/BlockingState.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.junction.api;
 
+import java.util.UUID;
+
 import org.joda.time.DateTime;
 
 import com.ning.billing.util.entity.Entity;
@@ -23,23 +25,27 @@ import com.ning.billing.util.entity.Entity;
 
 public interface BlockingState extends Entity, Comparable<BlockingState> {
 
-    public abstract String getStateName();
+    public UUID getBlockedId();
+
+    public String getStateName();
+
+    public Blockable.Type getType();
 
-    public abstract Blockable.Type getType();
+    public DateTime getTimestamp();
 
-    public abstract DateTime getTimestamp();
+    public boolean isBlockChange();
 
-    public abstract boolean isBlockChange();
+    public boolean isBlockEntitlement();
 
-    public abstract boolean isBlockEntitlement();
+    public boolean isBlockBilling();
 
-    public abstract boolean isBlockBilling();
+    public int compareTo(BlockingState arg0);
 
-    public abstract int compareTo(BlockingState arg0);
+    public int hashCode();
 
-    public abstract int hashCode();
+    public String getDescription();
 
-    public abstract String getDescription();
+    public String toString();
 
-    public abstract String toString();
+    public String getService();
 }
diff --git a/api/src/main/java/com/ning/billing/ObjectType.java b/api/src/main/java/com/ning/billing/ObjectType.java
index f310e0b..0b18aa9 100644
--- a/api/src/main/java/com/ning/billing/ObjectType.java
+++ b/api/src/main/java/com/ning/billing/ObjectType.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010-2011 Ning, Inc.
+ * Copyright 2010-2012 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
@@ -20,16 +20,20 @@ public enum ObjectType {
     ACCOUNT("account"),
     ACCOUNT_EMAIL("account email"),
     BUNDLE("subscription bundle"),
+    CUSTOM_FIELD("custom field"),
     INVOICE("invoice"),
     PAYMENT("payment"),
     INVOICE_ITEM("invoice item"),
     INVOICE_PAYMENT("invoice payment"),
     SUBSCRIPTION("subscription"),
     SUBSCRIPTION_EVENT("subscription event"),
+    PAYMENT_ATTEMPT("payment attempt"),
     PAYMENT_METHOD("payment method"),
     REFUND("refund"),
+    TAG("tag"),
     TAG_DEFINITION("tag definition"),
-    TENANT("tenant");
+    TENANT("tenant"),
+    TENANT_KVS("tenant kvs");
 
     private final String objectName;
 
diff --git a/api/src/main/java/com/ning/billing/payment/api/Payment.java b/api/src/main/java/com/ning/billing/payment/api/Payment.java
index 1d2db56..136890a 100644
--- a/api/src/main/java/com/ning/billing/payment/api/Payment.java
+++ b/api/src/main/java/com/ning/billing/payment/api/Payment.java
@@ -50,7 +50,7 @@ public interface Payment extends Entity {
 
     public String getExtSecondPaymentIdRef();
 
-    public interface PaymentAttempt {
+    public interface PaymentAttempt extends Entity {
 
         public UUID getId();
 
diff --git a/api/src/main/java/com/ning/billing/util/api/AuditUserApi.java b/api/src/main/java/com/ning/billing/util/api/AuditUserApi.java
index d6a7a4f..340420e 100644
--- a/api/src/main/java/com/ning/billing/util/api/AuditUserApi.java
+++ b/api/src/main/java/com/ning/billing/util/api/AuditUserApi.java
@@ -20,12 +20,15 @@ import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.ObjectType;
+import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.entitlement.api.timeline.BundleTimeline;
+import com.ning.billing.entitlement.api.timeline.EntitlementRepairException;
 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.Refund;
 import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.audit.AuditLogsForAccount;
 import com.ning.billing.util.audit.AuditLogsForBundles;
 import com.ning.billing.util.audit.AuditLogsForInvoicePayments;
 import com.ning.billing.util.audit.AuditLogsForInvoices;
@@ -35,6 +38,18 @@ import com.ning.billing.util.callcontext.TenantContext;
 
 public interface AuditUserApi {
 
+
+    public AuditLogsForAccount getAuditLogsForAccount(UUID accountId, AuditLevel auditLevel, TenantContext context);
+    /**
+     * Fetch all audit logs for a bundle.
+     *
+     * @param bundleId   the bundle id to lookup
+     * @param auditLevel audit level (verbosity)
+     * @param context    the tenant context
+     * @return all audit logs for these refunds
+     */
+    public AuditLogsForBundles getAuditLogsForBundle(UUID bundleId, AuditLevel auditLevel, TenantContext context) throws EntitlementRepairException;
+
     /**
      * Fetch all audit logs for bundles.
      *
diff --git a/api/src/main/java/com/ning/billing/util/api/CustomFieldUserApi.java b/api/src/main/java/com/ning/billing/util/api/CustomFieldUserApi.java
index c233a44..bb56fba 100644
--- a/api/src/main/java/com/ning/billing/util/api/CustomFieldUserApi.java
+++ b/api/src/main/java/com/ning/billing/util/api/CustomFieldUserApi.java
@@ -20,6 +20,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import com.ning.billing.BillingExceptionBase;
 import com.ning.billing.ObjectType;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.TenantContext;
@@ -27,7 +28,7 @@ import com.ning.billing.util.customfield.CustomField;
 
 public interface CustomFieldUserApi {
 
-    Map<String, CustomField> getCustomFields(UUID objectId, ObjectType objectType, TenantContext context);
+    List<CustomField> getCustomFields(UUID objectId, ObjectType objectType, TenantContext context);
 
-    void saveCustomFields(UUID objectId, ObjectType objectType, List<CustomField> fields, CallContext context);
+    void addCustomFields(List<CustomField> fields, CallContext context) throws CustomFieldApiException;
 }
diff --git a/api/src/main/java/com/ning/billing/util/api/TagUserApi.java b/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
index cde9535..99df733 100644
--- a/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
+++ b/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
@@ -71,5 +71,5 @@ public interface TagUserApi {
      * @param context    The tenant context
      * @return A map of tag, key being the tagId, and value the tag
      */
-    public Map<String, Tag> getTags(UUID objectId, ObjectType objectType, TenantContext context);
+    public List<Tag> getTags(UUID objectId, ObjectType objectType, TenantContext context);
 }
diff --git a/api/src/main/java/com/ning/billing/util/audit/AuditLog.java b/api/src/main/java/com/ning/billing/util/audit/AuditLog.java
index 1544931..44a39c7 100644
--- a/api/src/main/java/com/ning/billing/util/audit/AuditLog.java
+++ b/api/src/main/java/com/ning/billing/util/audit/AuditLog.java
@@ -18,8 +18,10 @@ package com.ning.billing.util.audit;
 
 import org.joda.time.DateTime;
 
+import com.ning.billing.util.entity.Entity;
 
-public interface AuditLog {
+
+public interface AuditLog extends Entity {
 
     /**
      * Get the type of change for this log entry
diff --git a/api/src/main/java/com/ning/billing/util/callcontext/CallContext.java b/api/src/main/java/com/ning/billing/util/callcontext/CallContext.java
index bb0270d..d14e062 100644
--- a/api/src/main/java/com/ning/billing/util/callcontext/CallContext.java
+++ b/api/src/main/java/com/ning/billing/util/callcontext/CallContext.java
@@ -35,7 +35,7 @@ public interface CallContext extends TenantContext {
 
     public String getReasonCode();
 
-    public String getComment();
+    public String getComments();
 
     public DateTime getCreatedDate();
 
diff --git a/api/src/main/java/com/ning/billing/util/customfield/CustomField.java b/api/src/main/java/com/ning/billing/util/customfield/CustomField.java
index 709be83..0831ee5 100644
--- a/api/src/main/java/com/ning/billing/util/customfield/CustomField.java
+++ b/api/src/main/java/com/ning/billing/util/customfield/CustomField.java
@@ -16,12 +16,19 @@
 
 package com.ning.billing.util.customfield;
 
+import java.util.UUID;
+
+import com.ning.billing.ObjectType;
 import com.ning.billing.util.entity.Entity;
 
 public interface CustomField extends Entity {
-    public String getName();
 
-    public String getValue();
+    public UUID getObjectId();
+
+    public ObjectType getObjectType();
+
+    public String getFieldName();
+
+    public String getFieldValue();
 
-    public void setValue(String value);
 }
diff --git a/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java b/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
index fe98ad6..dc32253 100644
--- a/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
+++ b/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
@@ -65,4 +65,13 @@ public enum ControlTagType {
     public List<ObjectType> getApplicableObjectTypes() {
         return applicableObjectTypes;
     }
+
+    public static ControlTagType getTypeFromId(final UUID targetId) {
+        for (ControlTagType cur : values()) {
+            if (cur.getId().equals(targetId)) {
+                return cur;
+            }
+        }
+        return null;
+    }
 }
diff --git a/api/src/main/java/com/ning/billing/util/tag/Tag.java b/api/src/main/java/com/ning/billing/util/tag/Tag.java
index 553ef5f..3c6f36d 100644
--- a/api/src/main/java/com/ning/billing/util/tag/Tag.java
+++ b/api/src/main/java/com/ning/billing/util/tag/Tag.java
@@ -18,8 +18,14 @@ package com.ning.billing.util.tag;
 
 import java.util.UUID;
 
+import com.ning.billing.ObjectType;
 import com.ning.billing.util.entity.Entity;
 
 public interface Tag extends Entity {
+
     UUID getTagDefinitionId();
+
+    ObjectType getObjectType();
+
+    UUID getObjectId();
 }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueBase.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueBase.java
index 0eef9b1..246076f 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueBase.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueBase.java
@@ -29,48 +29,16 @@ import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 
 import com.ning.billing.account.api.Account;
-import com.ning.billing.beatrix.integration.BeatrixModule;
+import com.ning.billing.beatrix.integration.BeatrixIntegrationModule;
 import com.ning.billing.beatrix.integration.TestIntegrationBase;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
-import com.ning.billing.invoice.api.InvoiceUserApi;
-import com.ning.billing.overdue.OverdueUserApi;
 import com.ning.billing.overdue.config.OverdueConfig;
-import com.ning.billing.overdue.wrapper.OverdueWrapperFactory;
-import com.ning.billing.payment.api.PaymentApi;
 import com.ning.billing.payment.api.PaymentMethodPlugin;
-import com.ning.billing.payment.provider.MockPaymentProviderPlugin;
-import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.config.catalog.XMLLoader;
-import com.ning.billing.util.svcapi.junction.BlockingInternalApi;
-
-import com.google.inject.Inject;
-import com.google.inject.name.Named;
 
 public abstract class TestOverdueBase extends TestIntegrationBase {
 
-    @Inject
-    protected ClockMock clock;
-
-    @Named("yoyo")
-    @Inject
-    protected
-    MockPaymentProviderPlugin paymentPlugin;
-
-    @Inject
-    protected BlockingInternalApi blockingApi;
-
-    @Inject
-    protected OverdueWrapperFactory overdueWrapperFactory;
-
-    @Inject
-    protected OverdueUserApi overdueApi;
-
-    @Inject
-    protected PaymentApi paymentApi;
-
-    @Inject
-    protected InvoiceUserApi invoiceApi;
 
     protected Account account;
     protected SubscriptionBundle bundle;
@@ -112,7 +80,7 @@ public abstract class TestOverdueBase extends TestIntegrationBase {
         account = createAccountWithPaymentMethod(getAccountData(0));
         assertNotNull(account);
 
-        paymentApi.addPaymentMethod(BeatrixModule.PLUGIN_NAME, account, true, paymentMethodPlugin, callContext);
+        paymentApi.addPaymentMethod(BeatrixIntegrationModule.PLUGIN_NAME, account, true, paymentMethodPlugin, callContext);
 
         bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", callContext);
 
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
index 4ce0c2f..38b3a50 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
@@ -16,9 +16,6 @@
 
 package com.ning.billing.beatrix.integration.overdue;
 
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
-
 import java.math.BigDecimal;
 import java.util.Collection;
 
@@ -28,8 +25,8 @@ import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.ning.billing.api.TestApiListener.NextEvent;
-import com.ning.billing.beatrix.integration.BeatrixModule;
-import com.ning.billing.beatrix.util.InvoiceChecker.ExpectedItemCheck;
+import com.ning.billing.beatrix.integration.BeatrixIntegrationModule;
+import com.ning.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.api.PriceListSet;
@@ -43,57 +40,60 @@ import com.ning.billing.junction.api.BlockingApiException;
 import com.ning.billing.payment.api.Payment;
 import com.ning.billing.util.svcapi.junction.DefaultBlockingState;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+
 @Test(groups = "slow")
-@Guice(modules = {BeatrixModule.class})
+@Guice(modules = {BeatrixIntegrationModule.class})
 public class TestOverdueIntegration extends TestOverdueBase {
 
 
-
     @Override
     public String getOverdueConfig() {
         final String configXml = "<overdueConfig>" +
-        "   <bundleOverdueStates>" +
-        "       <state name=\"OD3\">" +
-        "           <condition>" +
-        "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
-        "                   <unit>DAYS</unit><number>50</number>" +
-        "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
-        "           </condition>" +
-        "           <externalMessage>Reached OD3</externalMessage>" +
-        "           <blockChanges>true</blockChanges>" +
-        "           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
-        "           <autoReevaluationInterval>" +
-        "               <unit>DAYS</unit><number>5</number>" +
-        "           </autoReevaluationInterval>" +
-        "       </state>" +
-        "       <state name=\"OD2\">" +
-        "           <condition>" +
-        "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
-        "                   <unit>DAYS</unit><number>40</number>" +
-        "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
-        "           </condition>" +
-        "           <externalMessage>Reached OD2</externalMessage>" +
-        "           <blockChanges>true</blockChanges>" +
-        "           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
-        "           <autoReevaluationInterval>" +
-        "               <unit>DAYS</unit><number>5</number>" +
-        "           </autoReevaluationInterval>" +
-        "       </state>" +
-        "       <state name=\"OD1\">" +
-        "           <condition>" +
-        "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
-        "                   <unit>DAYS</unit><number>30</number>" +
-        "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
-        "           </condition>" +
-        "           <externalMessage>Reached OD1</externalMessage>" +
-        "           <blockChanges>true</blockChanges>" +
-        "           <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>" +
-        "           <autoReevaluationInterval>" +
-        "               <unit>DAYS</unit><number>5</number>" +
-        "           </autoReevaluationInterval>" +
-        "       </state>" +
-        "   </bundleOverdueStates>" +
-        "</overdueConfig>";
+                                 "   <bundleOverdueStates>" +
+                                 "       <state name=\"OD3\">" +
+                                 "           <condition>" +
+                                 "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+                                 "                   <unit>DAYS</unit><number>50</number>" +
+                                 "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+                                 "           </condition>" +
+                                 "           <externalMessage>Reached OD3</externalMessage>" +
+                                 "           <blockChanges>true</blockChanges>" +
+                                 "           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
+                                 "           <autoReevaluationInterval>" +
+                                 "               <unit>DAYS</unit><number>5</number>" +
+                                 "           </autoReevaluationInterval>" +
+                                 "       </state>" +
+                                 "       <state name=\"OD2\">" +
+                                 "           <condition>" +
+                                 "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+                                 "                   <unit>DAYS</unit><number>40</number>" +
+                                 "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+                                 "           </condition>" +
+                                 "           <externalMessage>Reached OD2</externalMessage>" +
+                                 "           <blockChanges>true</blockChanges>" +
+                                 "           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
+                                 "           <autoReevaluationInterval>" +
+                                 "               <unit>DAYS</unit><number>5</number>" +
+                                 "           </autoReevaluationInterval>" +
+                                 "       </state>" +
+                                 "       <state name=\"OD1\">" +
+                                 "           <condition>" +
+                                 "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+                                 "                   <unit>DAYS</unit><number>30</number>" +
+                                 "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+                                 "           </condition>" +
+                                 "           <externalMessage>Reached OD1</externalMessage>" +
+                                 "           <blockChanges>true</blockChanges>" +
+                                 "           <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>" +
+                                 "           <autoReevaluationInterval>" +
+                                 "               <unit>DAYS</unit><number>5</number>" +
+                                 "           </autoReevaluationInterval>" +
+                                 "       </state>" +
+                                 "   </bundleOverdueStates>" +
+                                 "</overdueConfig>";
 
         return configXml;
     }
@@ -109,59 +109,61 @@ public class TestOverdueIntegration extends TestOverdueBase {
         paymentPlugin.makeAllInvoicesFailWithError(true);
         final Subscription baseSubscription = createSubscriptionAndCheckForCompletion(bundle.getId(), productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
 
-        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 5, 1), callContext);
 
-        // DAY 30 have to get out of trial before first payment
+        // 2012, 5, 31 => DAY 30 have to get out of trial {I0, P0}
         addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
 
-        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 6, 30), callContext);
 
-        // Should still be in clear state
+        // 2012, 6, 8 => Retry P0
+        addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR);
         checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
 
-        // DAY 45 - 15 days after invoice
-        addDaysAndCheckForCompletion(15, NextEvent.PAYMENT_ERROR);
-
-        // Should still be in clear state
+        // 2012, 6, 16 => Retry P0
+        addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR);
         checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
 
-        // Note: we have two tracks of payment retries because of the invoice generated at the phase change
-
-        // DAY 65 - 35 days after invoice
-        addDaysAndCheckForCompletion(20, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.PAYMENT_ERROR);
-
-        invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
-        invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 7, 31), callContext);
+        // 2012, 6, 24 => Retry P0
+        addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR);
+        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
 
-        // Now we should be in OD1
+        // 2012, 6, 31 => P1 (We se 6/31 instead of 6/30 because invoice might happen later in that day)
+        addDaysAndCheckForCompletion(7, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
         checkODState("OD1");
         checkChangePlanWithOverdueState(baseSubscription, true);
+        invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 7, 31), callContext);
 
-        // DAY 67 - 37 days after invoice
-        addDaysAndCheckForCompletion(2);
+        // 2012, 7, 2 => Retry P0
+        addDaysAndCheckForCompletion(1, NextEvent.PAYMENT_ERROR);
+        checkODState("OD1");
 
-        // Should still be in OD1
+        // 2012, 7, 9 => Retry P1
+        addDaysAndCheckForCompletion(7, NextEvent.PAYMENT_ERROR);
         checkODState("OD1");
-        checkChangePlanWithOverdueState(baseSubscription, true);
 
-        // DAY 75 - 45 days after invoice
-        addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR, NextEvent.PAYMENT_ERROR);
+        // 2012, 7, 10 => Retry P0
+        addDaysAndCheckForCompletion(1, NextEvent.PAYMENT_ERROR);
+        checkODState("OD2");
 
-        // Should now be in OD2
+        // 2012, 7, 17 => Retry P1
+        addDaysAndCheckForCompletion(7, NextEvent.PAYMENT_ERROR);
         checkODState("OD2");
-        checkChangePlanWithOverdueState(baseSubscription, true);
 
-        // DAY 85 - 55 days after invoice
-        addDaysAndCheckForCompletion(10, NextEvent.PAYMENT_ERROR, NextEvent.PAYMENT_ERROR);
+        // 2012, 7, 18 => Retry P0
+        addDaysAndCheckForCompletion(1, NextEvent.PAYMENT_ERROR);
+        checkODState("OD2");
 
-        // Should now be in OD3
+        // 2012, 7, 23 => Should be 20 but notficationQ event occurs on 23...
+        addDaysAndCheckForCompletion(5);
         checkODState("OD3");
-        checkChangePlanWithOverdueState(baseSubscription, true);
+
 
         paymentPlugin.makeAllInvoicesFailWithError(false);
-        final Collection<Invoice> invoices = invoiceApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext);
+        final Collection<Invoice> invoices = invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext);
         for (final Invoice invoice : invoices) {
             if (invoice.getBalance().compareTo(BigDecimal.ZERO) > 0) {
                 createPaymentAndCheckForCompletion(account, invoice, NextEvent.PAYMENT);
@@ -172,22 +174,22 @@ public class TestOverdueIntegration extends TestOverdueBase {
         checkChangePlanWithOverdueState(baseSubscription, false);
 
         invoiceChecker.checkRepairedInvoice(account.getId(), 3,
-                                            callContext, new ExpectedItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
+                                            callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
                                             // We paid up to 07-31, hence the adjustment
-                                            new ExpectedItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-249.95")),
-                                            new ExpectedItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 25), InvoiceItemType.CBA_ADJ, new BigDecimal("249.95")));
+                                            new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-249.95")),
+                                            new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 23), new LocalDate(2012, 7, 23), InvoiceItemType.CBA_ADJ, new BigDecimal("249.95")));
         invoiceChecker.checkInvoice(account.getId(), 4, callContext,
-                                    // Note the end date here is not 07-25, but 07-15. The overdue configuration disabled invoicing between 07-15 and 07-25 (e.g. the bundle
+                                    // Note the end date here is not 07-25, but 07-9. The overdue configuration disabled invoicing between 07-09 and 07-23 (e.g. the bundle
                                     // was inaccessible, hence we didn't want to charge the customer for that period, even though the account was overdue).
-                                    new ExpectedItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 15), InvoiceItemType.RECURRING, new BigDecimal("124.98")),
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 9), InvoiceItemType.RECURRING, new BigDecimal("74.99")),
                                     // Item for the upgraded recurring plan
-                                    new ExpectedItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("116.09")),
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 23), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("154.85")),
                                     // Credits consumed
-                                    new ExpectedItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 25), InvoiceItemType.CBA_ADJ, new BigDecimal("-241.07")));
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 23), new LocalDate(2012, 7, 23), InvoiceItemType.CBA_ADJ, new BigDecimal("-229.84")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 7, 31), callContext);
 
-        // Verify the account balance: 249.95 - 124.98 - 116.09
-        assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(new BigDecimal("-8.88")), 0);
+        // Verify the account balance: 249.95 - 74.99 - 154.85
+        assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(new BigDecimal("-20.11")), 0);
     }
 
     @Test(groups = "slow")
@@ -203,13 +205,13 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // Create subscription
         final Subscription baseSubscription = createSubscriptionAndCheckForCompletion(bundle.getId(), productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
 
-        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 5, 1), callContext);
 
         // DAY 30 have to get out of trial before first payment. A payment error, one for each invoice, should be on the bus (because there is no payment method)
         addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.PAYMENT_ERROR);
 
-        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 6, 30), callContext);
 
         // Should still be in clear state
@@ -225,7 +227,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // Single PAYMENT_ERROR here here triggered by the invoice
         addDaysAndCheckForCompletion(20, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
 
-        invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 7, 31), callContext);
 
         // Now we should be in OD1
@@ -254,10 +256,10 @@ public class TestOverdueIntegration extends TestOverdueBase {
         checkChangePlanWithOverdueState(baseSubscription, true);
 
         // Add a payment method and set it as default
-        paymentApi.addPaymentMethod(BeatrixModule.PLUGIN_NAME, account, true, paymentMethodPlugin, callContext);
+        paymentApi.addPaymentMethod(BeatrixIntegrationModule.PLUGIN_NAME, account, true, paymentMethodPlugin, callContext);
 
         // Pay all invoices
-        final Collection<Invoice> invoices = invoiceApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext);
+        final Collection<Invoice> invoices = invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext);
         for (final Invoice invoice : invoices) {
             if (invoice.getBalance().compareTo(BigDecimal.ZERO) > 0) {
                 createPaymentAndCheckForCompletion(account, invoice, NextEvent.PAYMENT);
@@ -268,18 +270,18 @@ public class TestOverdueIntegration extends TestOverdueBase {
         checkChangePlanWithOverdueState(baseSubscription, false);
 
         invoiceChecker.checkRepairedInvoice(account.getId(), 3,
-                                            callContext, new ExpectedItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
+                                            callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
                                             // We paid up to 07-31, hence the adjustment
-                                            new ExpectedItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-249.95")),
-                                            new ExpectedItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 25), InvoiceItemType.CBA_ADJ, new BigDecimal("249.95")));
+                                            new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-249.95")),
+                                            new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 25), InvoiceItemType.CBA_ADJ, new BigDecimal("249.95")));
         invoiceChecker.checkInvoice(account.getId(), 4, callContext,
                                     // Note the end date here is not 07-25, but 07-15. The overdue configuration disabled invoicing between 07-15 and 07-25 (e.g. the bundle
                                     // was inaccessible, hence we didn't want to charge the customer for that period, even though the account was overdue).
-                                    new ExpectedItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 15), InvoiceItemType.RECURRING, new BigDecimal("124.98")),
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 15), InvoiceItemType.RECURRING, new BigDecimal("124.98")),
                                     // Item for the upgraded recurring plan
-                                    new ExpectedItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("116.09")),
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("116.09")),
                                     // Credits consumed
-                                    new ExpectedItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 25), InvoiceItemType.CBA_ADJ, new BigDecimal("-241.07")));
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 25), InvoiceItemType.CBA_ADJ, new BigDecimal("-241.07")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 7, 31), callContext);
 
         // Verify the account balance: 249.95 - 124.98 - 116.09
@@ -293,26 +295,26 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // Create a subscription without failing payments
         final Subscription baseSubscription = createSubscriptionAndCheckForCompletion(bundle.getId(), productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
 
-        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 5, 1), callContext);
 
         // Create an external charge on a new invoice
         addDaysAndCheckForCompletion(5);
         busHandler.pushExpectedEvents(NextEvent.INVOICE_ADJUSTMENT);
-        invoiceApi.insertExternalChargeForBundle(account.getId(), bundle.getId(), BigDecimal.TEN, "For overdue", new LocalDate(2012, 5, 6), Currency.USD, callContext);
+        invoiceUserApi.insertExternalChargeForBundle(account.getId(), bundle.getId(), BigDecimal.TEN, "For overdue", new LocalDate(2012, 5, 6), Currency.USD, callContext);
         assertTrue(busHandler.isCompleted(DELAY));
         assertListenerStatus();
-        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 6), null, InvoiceItemType.EXTERNAL_CHARGE, BigDecimal.TEN));
+        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 6), null, InvoiceItemType.EXTERNAL_CHARGE, BigDecimal.TEN));
 
         // DAY 30 have to get out of trial before first payment
         addDaysAndCheckForCompletion(25, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
 
-        invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 6, 30), callContext);
 
         // Should still be in clear state - the invoice for the bundle has been paid, but not the invoice with the external charge
         // We refresh overdue just to be safe, see below
-        overdueApi.refreshOverdueStateFor(bundle, callContext);
+        overdueUserApi.refreshOverdueStateFor(bundle, callContext);
         checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
 
         // Past 30 days since the external charge
@@ -320,12 +322,12 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // Note! We need to explicitly refresh here because overdue won't get notified to refresh up until the next
         // payment (when the next invoice is generated)
         // TODO - we should fix this
-        overdueApi.refreshOverdueStateFor(bundle, callContext);
+        overdueUserApi.refreshOverdueStateFor(bundle, callContext);
         // We should now be in OD1
         checkODState("OD1");
 
         // Pay the invoice
-        final Invoice externalChargeInvoice = invoiceApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext).iterator().next();
+        final Invoice externalChargeInvoice = invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext).iterator().next();
         createExternalPaymentAndCheckForCompletion(account, externalChargeInvoice, NextEvent.PAYMENT);
         // We should be clear now
         checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
@@ -338,13 +340,13 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // Create subscription and don't fail payments
         final Subscription baseSubscription = createSubscriptionAndCheckForCompletion(bundle.getId(), productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
 
-        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 5, 1), callContext);
 
         // DAY 30 have to get out of trial before first payment
         addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
 
-        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 6, 30), callContext);
 
         // Should still be in clear state
@@ -359,14 +361,14 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // DAY 65 - 35 days after invoice
         addDaysAndCheckForCompletion(20, NextEvent.INVOICE, NextEvent.PAYMENT);
 
-        invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 7, 31), callContext);
 
         // Should still be in clear state
         checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
 
         // Now, refund the second (first non-zero dollar) invoice
-        final Payment payment = paymentApi.getPayment(invoiceApi.getInvoicesByAccount(account.getId(), callContext).get(1).getPayments().get(0).getPaymentId(), callContext);
+        final Payment payment = paymentApi.getPayment(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).get(1).getPayments().get(0).getPaymentId(), callContext);
         refundPaymentAndCheckForCompletion(account, payment, NextEvent.INVOICE_ADJUSTMENT);
         // We should now be in OD1
         checkODState("OD1");
@@ -380,13 +382,13 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // Create subscription and don't fail payments
         final Subscription baseSubscription = createSubscriptionAndCheckForCompletion(bundle.getId(), productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
 
-        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 5, 1), callContext);
 
         // DAY 30 have to get out of trial before first payment
         addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
 
-        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 6, 30), callContext);
 
         // Should still be in clear state
@@ -401,14 +403,14 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // DAY 65 - 35 days after invoice
         addDaysAndCheckForCompletion(20, NextEvent.INVOICE, NextEvent.PAYMENT);
 
-        invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 7, 31), callContext);
 
         // Should still be in clear state
         checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
 
         // Now, create a chargeback for the second (first non-zero dollar) invoice
-        final InvoicePayment payment = invoicePaymentApi.getInvoicePayments(invoiceApi.getInvoicesByAccount(account.getId(), callContext).get(1).getPayments().get(0).getPaymentId(), callContext).get(0);
+        final InvoicePayment payment = invoicePaymentApi.getInvoicePayments(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).get(1).getPayments().get(0).getPaymentId(), callContext).get(0);
         createChargeBackAndCheckForCompletion(payment, NextEvent.INVOICE_ADJUSTMENT);
         // We should now be in OD1
         checkODState("OD1");
@@ -423,13 +425,13 @@ public class TestOverdueIntegration extends TestOverdueBase {
         paymentPlugin.makeAllInvoicesFailWithError(true);
         final Subscription baseSubscription = createSubscriptionAndCheckForCompletion(bundle.getId(), productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
 
-        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 5, 1), callContext);
 
         // DAY 30 have to get out of trial before first payment
         addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
 
-        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 6, 30), callContext);
 
         // Should still be in clear state
@@ -444,7 +446,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // DAY 65 - 35 days after invoice
         addDaysAndCheckForCompletion(20, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.PAYMENT_ERROR);
 
-        invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 7, 31), callContext);
 
         // Now we should be in OD1
@@ -454,7 +456,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // We have two unpaid non-zero dollar invoices at this point
         // Pay the first one via an external payment - we should then be 5 days apart from the second invoice
         // (which is the earliest unpaid one) and hence come back to a clear state (see configuration)
-        final Invoice firstNonZeroInvoice = invoiceApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext).iterator().next();
+        final Invoice firstNonZeroInvoice = invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext).iterator().next();
         createExternalPaymentAndCheckForCompletion(account, firstNonZeroInvoice, NextEvent.PAYMENT);
         // We should be clear now
         checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
@@ -473,13 +475,13 @@ public class TestOverdueIntegration extends TestOverdueBase {
         paymentPlugin.makeAllInvoicesFailWithError(true);
         final Subscription baseSubscription = createSubscriptionAndCheckForCompletion(bundle.getId(), productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
 
-        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 5, 1), callContext);
 
         // DAY 30 have to get out of trial before first payment
         addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
 
-        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 6, 30), callContext);
 
         // Should still be in clear state
@@ -494,7 +496,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // DAY 65 - 35 days after invoice
         addDaysAndCheckForCompletion(20, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.PAYMENT_ERROR);
 
-        invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 7, 31), callContext);
 
         // Now we should be in OD1
@@ -504,14 +506,14 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // We have two unpaid non-zero dollar invoices at this point
         // Adjust the first (and only) item of the first invoice - we should then be 5 days apart from the second invoice
         // (which is the earliest unpaid one) and hence come back to a clear state (see configuration)
-        final Invoice firstNonZeroInvoice = invoiceApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext).iterator().next();
+        final Invoice firstNonZeroInvoice = invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext).iterator().next();
         fullyAdjustInvoiceItemAndCheckForCompletion(account, firstNonZeroInvoice, 1, NextEvent.INVOICE_ADJUSTMENT);
         // We should be clear now
         checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
 
         invoiceChecker.checkRepairedInvoice(account.getId(), 2,
-                                            callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
-                                            new ExpectedItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 5, 31), InvoiceItemType.ITEM_ADJ, new BigDecimal("-249.95")));
+                                            callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
+                                            new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 5, 31), InvoiceItemType.ITEM_ADJ, new BigDecimal("-249.95")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 7, 31), callContext);
 
         // DAY 70 - 10 days after second invoice
@@ -533,10 +535,10 @@ public class TestOverdueIntegration extends TestOverdueBase {
         checkODState("OD1");
         checkChangePlanWithOverdueState(baseSubscription, true);
 
-        invoiceChecker.checkInvoice(account.getId(), 4, callContext, new ExpectedItemCheck(new LocalDate(2012, 7, 31), new LocalDate(2012, 8, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(account.getId(), 4, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 31), new LocalDate(2012, 8, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
 
         // Fully adjust all invoices
-        final Collection<Invoice> invoices = invoiceApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext);
+        final Collection<Invoice> invoices = invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext);
         for (final Invoice invoice : invoices) {
             if (invoice.getBalance().compareTo(BigDecimal.ZERO) > 0) {
                 fullyAdjustInvoiceAndCheckForCompletion(account, invoice, NextEvent.INVOICE_ADJUSTMENT);
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
index ab4c4e6..749b65a 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
@@ -25,8 +25,8 @@ import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.ning.billing.api.TestApiListener.NextEvent;
-import com.ning.billing.beatrix.integration.BeatrixModule;
-import com.ning.billing.beatrix.util.InvoiceChecker.ExpectedItemCheck;
+import com.ning.billing.beatrix.integration.BeatrixIntegrationModule;
+import com.ning.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
@@ -34,7 +34,7 @@ import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.util.svcapi.junction.DefaultBlockingState;
 
 @Test(groups = "slow")
-@Guice(modules = {BeatrixModule.class})
+@Guice(modules = {BeatrixIntegrationModule.class})
 public class TestOverdueWithSubscriptionCancellation extends TestOverdueBase {
 
 
@@ -69,13 +69,13 @@ public class TestOverdueWithSubscriptionCancellation extends TestOverdueBase {
         paymentPlugin.makeAllInvoicesFailWithError(true);
         final Subscription baseSubscription = createSubscriptionAndCheckForCompletion(bundle.getId(), productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
 
-        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 5, 1), callContext);
 
         // DAY 30 have to get out of trial before first payment
         addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
 
-        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
         invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 6, 30), callContext);
 
         // Should still be in clear state
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
index 328ab3f..ac33a76 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
@@ -65,7 +65,7 @@ import com.ning.billing.util.tag.TagDefinition;
 
 import static org.testng.Assert.assertTrue;
 
-@Guice(modules = BeatrixModule.class)
+@Guice(modules = BeatrixIntegrationModule.class)
 public class TestAnalytics extends TestIntegrationBase {
 
     private Account account;
@@ -228,7 +228,7 @@ public class TestAnalytics extends TestIntegrationBase {
         Assert.assertNull(analyticsUserApi.getAccountByKey(account.getExternalKey(), callContext).getLastPaymentStatus());
 
         // Verify the initial overdue status
-        Assert.assertEquals(analyticsUserApi.getOverdueStatusesForBundle(bundle.getKey(), callContext).size(), 0);
+        Assert.assertEquals(analyticsUserApi.getOverdueStatusesForBundle(bundle.getExternalKey(), callContext).size(), 0);
 
         // Move after trial
         busHandler.pushExpectedEvents(TestApiListener.NextEvent.PHASE, TestApiListener.NextEvent.INVOICE, TestApiListener.NextEvent.PAYMENT_ERROR);
@@ -248,7 +248,7 @@ public class TestAnalytics extends TestIntegrationBase {
         Assert.assertNull(invoicePaymentsForAccount.get(0).getExtFirstPaymentRefId());
         Assert.assertNull(invoicePaymentsForAccount.get(0).getExtSecondPaymentRefId());
         Assert.assertEquals(invoicePaymentsForAccount.get(0).getProcessingStatus(), PaymentStatus.PAYMENT_FAILURE.toString());
-        Assert.assertEquals(invoicePaymentsForAccount.get(0).getPluginName(), BeatrixModule.PLUGIN_NAME);
+        Assert.assertEquals(invoicePaymentsForAccount.get(0).getPluginName(), BeatrixIntegrationModule.PLUGIN_NAME);
 
         // Verify the account object has been updated
         Assert.assertEquals(analyticsUserApi.getAccountByKey(account.getExternalKey(), callContext).getBalance(),
@@ -260,7 +260,7 @@ public class TestAnalytics extends TestIntegrationBase {
                             invoicePaymentsForAccount.get(0).getAmount());
 
         // Verify overdue status - we should still be in clear state
-        Assert.assertEquals(analyticsUserApi.getOverdueStatusesForBundle(bundle.getKey(), callContext).size(), 0);
+        Assert.assertEquals(analyticsUserApi.getOverdueStatusesForBundle(bundle.getExternalKey(), callContext).size(), 0);
 
         clock.addDays(15); // DAY 45 - 15 days after invoice
         assertTrue(busHandler.isCompleted(DELAY));
@@ -269,7 +269,7 @@ public class TestAnalytics extends TestIntegrationBase {
         verifyBSTWithTrialAndEvergreenPhases(account, bundle, subscription);
 
         // Verify overdue status - we should still be in clear state
-        Assert.assertEquals(analyticsUserApi.getOverdueStatusesForBundle(bundle.getKey(), callContext).size(), 0);
+        Assert.assertEquals(analyticsUserApi.getOverdueStatusesForBundle(bundle.getExternalKey(), callContext).size(), 0);
 
         busHandler.pushExpectedEvents(TestApiListener.NextEvent.INVOICE, TestApiListener.NextEvent.PAYMENT_ERROR);
         clock.addDays(20); // DAY 65 - 35 days after invoice
@@ -277,7 +277,7 @@ public class TestAnalytics extends TestIntegrationBase {
         waitALittle();
 
         // Verify overdue status - we should be in OD1
-        final List<BusinessOverdueStatus> od1Bundle = analyticsUserApi.getOverdueStatusesForBundle(bundle.getKey(), callContext);
+        final List<BusinessOverdueStatus> od1Bundle = analyticsUserApi.getOverdueStatusesForBundle(bundle.getExternalKey(), callContext);
         Assert.assertEquals(od1Bundle.size(), 1);
         Assert.assertEquals(od1Bundle.get(0).getStatus(), "OD1");
         Assert.assertEquals(od1Bundle.get(0).getId(), bundle.getId());
@@ -287,7 +287,7 @@ public class TestAnalytics extends TestIntegrationBase {
         assertTrue(busHandler.isCompleted(DELAY));
         waitALittle();
         // Verify overdue status - we should still be in OD1
-        final List<BusinessOverdueStatus> stillOd1Bundle = analyticsUserApi.getOverdueStatusesForBundle(bundle.getKey(), callContext);
+        final List<BusinessOverdueStatus> stillOd1Bundle = analyticsUserApi.getOverdueStatusesForBundle(bundle.getExternalKey(), callContext);
         Assert.assertEquals(stillOd1Bundle.size(), 1);
         Assert.assertEquals(stillOd1Bundle.get(0).getStatus(), "OD1");
         Assert.assertEquals(stillOd1Bundle.get(0).getId(), bundle.getId());
@@ -297,7 +297,7 @@ public class TestAnalytics extends TestIntegrationBase {
         assertTrue(busHandler.isCompleted(DELAY));
         waitALittle();
         // Verify overdue status - we should be in OD2
-        final List<BusinessOverdueStatus> od2Bundle = analyticsUserApi.getOverdueStatusesForBundle(bundle.getKey(), callContext);
+        final List<BusinessOverdueStatus> od2Bundle = analyticsUserApi.getOverdueStatusesForBundle(bundle.getExternalKey(), callContext);
         Assert.assertEquals(od2Bundle.size(), 2);
         Assert.assertEquals(od2Bundle.get(0).getStatus(), "OD1");
         Assert.assertEquals(od2Bundle.get(1).getStatus(), "OD2");
@@ -312,7 +312,7 @@ public class TestAnalytics extends TestIntegrationBase {
         assertTrue(busHandler.isCompleted(DELAY));
         waitALittle();
         // Verify overdue status - we should be in OD3
-        final List<BusinessOverdueStatus> od3Bundle = analyticsUserApi.getOverdueStatusesForBundle(bundle.getKey(), callContext);
+        final List<BusinessOverdueStatus> od3Bundle = analyticsUserApi.getOverdueStatusesForBundle(bundle.getExternalKey(), callContext);
         Assert.assertEquals(od3Bundle.size(), 3);
         Assert.assertEquals(od3Bundle.get(0).getStatus(), "OD1");
         Assert.assertEquals(od3Bundle.get(1).getStatus(), "OD2");
@@ -422,7 +422,7 @@ public class TestAnalytics extends TestIntegrationBase {
         waitALittle();
 
         // Verify BST is still empty since no subscription has been added yet
-        Assert.assertEquals(analyticsUserApi.getTransitionsForBundle(bundle.getKey(), callContext).size(), 0);
+        Assert.assertEquals(analyticsUserApi.getTransitionsForBundle(bundle.getExternalKey(), callContext).size(), 0);
 
         // The account should still not have any invoice
         Assert.assertEquals(analyticsUserApi.getInvoicesForAccount(account.getExternalKey(), callContext).size(), 0);
@@ -468,7 +468,7 @@ public class TestAnalytics extends TestIntegrationBase {
         Assert.assertEquals(invoiceItem.getBillingPeriod(), subscription.getCurrentPhase().getBillingPeriod().toString());
         Assert.assertEquals(invoiceItem.getCurrency(), account.getCurrency());
         Assert.assertEquals(invoiceItem.getEndDate(), invoiceItem.getStartDate().plusDays(30));
-        Assert.assertEquals(invoiceItem.getExternalKey(), bundle.getKey());
+        Assert.assertEquals(invoiceItem.getExternalKey(), bundle.getExternalKey());
         Assert.assertEquals(invoiceItem.getInvoiceId(), invoice.getInvoiceId());
         Assert.assertEquals(invoiceItem.getItemType(), "FIXED");
         Assert.assertEquals(invoiceItem.getPhase(), subscription.getCurrentPhase().getPhaseType().toString());
@@ -484,7 +484,7 @@ public class TestAnalytics extends TestIntegrationBase {
 
     private void verifyBSTWithTrialAndEvergreenPhases(final Account account, final SubscriptionBundle bundle, final Subscription subscription) throws CatalogApiException {
         // BST should have two transitions
-        final List<BusinessSubscriptionTransition> transitions = analyticsUserApi.getTransitionsForBundle(bundle.getKey(), callContext);
+        final List<BusinessSubscriptionTransition> transitions = analyticsUserApi.getTransitionsForBundle(bundle.getExternalKey(), callContext);
         Assert.assertEquals(transitions.size(), 2);
 
         verifyTrialAndEvergreenPhases(account, bundle, subscription);
@@ -492,7 +492,7 @@ public class TestAnalytics extends TestIntegrationBase {
 
     private void verifyBSTWithTrialAndEvergreenPhasesAndCancellation(final Account account, final SubscriptionBundle bundle, final Subscription subscription) throws CatalogApiException {
         // BST should have three transitions
-        final List<BusinessSubscriptionTransition> transitions = analyticsUserApi.getTransitionsForBundle(bundle.getKey(), callContext);
+        final List<BusinessSubscriptionTransition> transitions = analyticsUserApi.getTransitionsForBundle(bundle.getExternalKey(), callContext);
         Assert.assertEquals(transitions.size(), 3);
 
         verifyTrialAndEvergreenPhases(account, bundle, subscription);
@@ -501,7 +501,7 @@ public class TestAnalytics extends TestIntegrationBase {
 
     private void verifyBSTWithTrialAndEvergreenPhasesAndCancellationAndSystemCancellation(final Account account, final SubscriptionBundle bundle, final Subscription subscription) throws CatalogApiException {
         // BST should have four transitions
-        final List<BusinessSubscriptionTransition> transitions = analyticsUserApi.getTransitionsForBundle(bundle.getKey(), callContext);
+        final List<BusinessSubscriptionTransition> transitions = analyticsUserApi.getTransitionsForBundle(bundle.getExternalKey(), callContext);
         Assert.assertEquals(transitions.size(), 4);
 
         verifyTrialAndEvergreenPhases(account, bundle, subscription);
@@ -511,12 +511,12 @@ public class TestAnalytics extends TestIntegrationBase {
 
     private void verifyTrialAndEvergreenPhases(final Account account, final SubscriptionBundle bundle, final Subscription subscription) throws CatalogApiException {
         final Product currentProduct = subscriptionPlan.getProduct();
-        final List<BusinessSubscriptionTransition> transitions = analyticsUserApi.getTransitionsForBundle(bundle.getKey(), callContext);
+        final List<BusinessSubscriptionTransition> transitions = analyticsUserApi.getTransitionsForBundle(bundle.getExternalKey(), callContext);
 
         // Check the first transition (into trial phase)
         final BusinessSubscriptionTransition initialTransition = transitions.get(0);
         Assert.assertEquals(initialTransition.getBundleId(), bundle.getId());
-        Assert.assertEquals(initialTransition.getExternalKey(), bundle.getKey());
+        Assert.assertEquals(initialTransition.getExternalKey(), bundle.getExternalKey());
         Assert.assertEquals(initialTransition.getAccountId(), account.getId());
         Assert.assertEquals(initialTransition.getAccountKey(), account.getExternalKey());
         Assert.assertEquals(initialTransition.getSubscriptionId(), subscription.getId());
@@ -541,7 +541,7 @@ public class TestAnalytics extends TestIntegrationBase {
 
         // Check the second transition (from trial to evergreen)
         final BusinessSubscriptionTransition futureTransition = transitions.get(1);
-        Assert.assertEquals(futureTransition.getExternalKey(), bundle.getKey());
+        Assert.assertEquals(futureTransition.getExternalKey(), bundle.getExternalKey());
         Assert.assertEquals(futureTransition.getAccountKey(), account.getExternalKey());
         Assert.assertEquals(futureTransition.getCategory(), currentProduct.getCategory().toString());
         Assert.assertEquals(futureTransition.getEventType(), BusinessSubscriptionEvent.EventType.SYSTEM_CHANGE.toString());
@@ -564,10 +564,10 @@ public class TestAnalytics extends TestIntegrationBase {
 
     private void verifyCancellationTransition(final Account account, final SubscriptionBundle bundle) throws CatalogApiException {
         final Product currentProduct = subscriptionPlan.getProduct();
-        final List<BusinessSubscriptionTransition> transitions = analyticsUserApi.getTransitionsForBundle(bundle.getKey(), callContext);
+        final List<BusinessSubscriptionTransition> transitions = analyticsUserApi.getTransitionsForBundle(bundle.getExternalKey(), callContext);
 
         final BusinessSubscriptionTransition cancellationRequest = transitions.get(2);
-        Assert.assertEquals(cancellationRequest.getExternalKey(), bundle.getKey());
+        Assert.assertEquals(cancellationRequest.getExternalKey(), bundle.getExternalKey());
         Assert.assertEquals(cancellationRequest.getAccountKey(), account.getExternalKey());
         Assert.assertEquals(cancellationRequest.getCategory(), currentProduct.getCategory().toString());
         Assert.assertEquals(cancellationRequest.getEventType(), BusinessSubscriptionEvent.EventType.CANCEL.toString());
@@ -576,10 +576,10 @@ public class TestAnalytics extends TestIntegrationBase {
     }
 
     private void verifySystemCancellationTransition(final Account account, final SubscriptionBundle bundle) throws CatalogApiException {
-        final List<BusinessSubscriptionTransition> transitions = analyticsUserApi.getTransitionsForBundle(bundle.getKey(), callContext);
+        final List<BusinessSubscriptionTransition> transitions = analyticsUserApi.getTransitionsForBundle(bundle.getExternalKey(), callContext);
 
         final BusinessSubscriptionTransition systemCancellation = transitions.get(3);
-        Assert.assertEquals(systemCancellation.getExternalKey(), bundle.getKey());
+        Assert.assertEquals(systemCancellation.getExternalKey(), bundle.getExternalKey());
         Assert.assertEquals(systemCancellation.getAccountKey(), account.getExternalKey());
         Assert.assertEquals(systemCancellation.getCategory(), ProductCategory.BASE.toString());
         Assert.assertEquals(systemCancellation.getEventType(), BusinessSubscriptionEvent.EventType.SYSTEM_CANCEL.toString());
@@ -597,12 +597,12 @@ public class TestAnalytics extends TestIntegrationBase {
         waitALittle();
 
         // BST should have three transitions (a ADD_BASE, CHANGE_BASE and SYSTEM_CHANGE_BASE)
-        final List<BusinessSubscriptionTransition> transitions = analyticsUserApi.getTransitionsForBundle(bundle.getKey(), callContext);
+        final List<BusinessSubscriptionTransition> transitions = analyticsUserApi.getTransitionsForBundle(bundle.getExternalKey(), callContext);
         Assert.assertEquals(transitions.size(), 3);
         final BusinessSubscriptionTransition previousTransition = transitions.get(0);
         final BusinessSubscriptionTransition transition = transitions.get(1);
         Assert.assertEquals(transition.getBundleId(), bundle.getId());
-        Assert.assertEquals(transition.getExternalKey(), bundle.getKey());
+        Assert.assertEquals(transition.getExternalKey(), bundle.getExternalKey());
         Assert.assertEquals(transition.getAccountId(), account.getId());
         Assert.assertEquals(transition.getAccountKey(), account.getExternalKey());
         Assert.assertEquals(transition.getSubscriptionId(), subscription.getId());
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBundleTransfer.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBundleTransfer.java
index 9f0ac8c..d76e178 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBundleTransfer.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBundleTransfer.java
@@ -30,7 +30,7 @@ import org.testng.annotations.Test;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.BillCycleDay;
 import com.ning.billing.api.TestApiListener.NextEvent;
-import com.ning.billing.beatrix.util.InvoiceChecker.ExpectedItemCheck;
+import com.ning.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.PriceListSet;
@@ -43,7 +43,7 @@ import com.ning.billing.invoice.api.InvoiceItemType;
 
 import com.google.common.collect.ImmutableList;
 
-@Guice(modules = {BeatrixModule.class})
+@Guice(modules = {BeatrixIntegrationModule.class})
 public class TestBundleTransfer extends TestIntegrationBase {
 
 
@@ -230,23 +230,23 @@ public class TestBundleTransfer extends TestIntegrationBase {
 
 
         // CHECK OLD ACCOUNTS ITEMS
-        ImmutableList<ExpectedItemCheck> toBeChecked = ImmutableList.<ExpectedItemCheck>of(
-                new ExpectedItemCheck(new LocalDate(2012,5,1), new LocalDate(2012,5,9), InvoiceItemType.RECURRING, new BigDecimal("66.66")),
-                new ExpectedItemCheck(new LocalDate(2012,5,1), new LocalDate(2012,5,9), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-66.66")),
-                new ExpectedItemCheck(new LocalDate(2012,5,3), new LocalDate(2012,5,3), InvoiceItemType.CBA_ADJ, new BigDecimal("66.66")));
+        ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,1), new LocalDate(2012,5,9), InvoiceItemType.RECURRING, new BigDecimal("66.66")),
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,1), new LocalDate(2012,5,9), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-66.66")),
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,3), new LocalDate(2012,5,3), InvoiceItemType.CBA_ADJ, new BigDecimal("66.66")));
         invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, toBeChecked);
 
-        toBeChecked = ImmutableList.<ExpectedItemCheck>of(
-                new ExpectedItemCheck(new LocalDate(2012,5,1), new LocalDate(2012,5,3), InvoiceItemType.RECURRING, new BigDecimal("16.67")),
-                new ExpectedItemCheck(new LocalDate(2012,5,3), new LocalDate(2012,5,3), InvoiceItemType.CBA_ADJ, new BigDecimal("-16.67")));
+        toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,1), new LocalDate(2012,5,3), InvoiceItemType.RECURRING, new BigDecimal("16.67")),
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,3), new LocalDate(2012,5,3), InvoiceItemType.CBA_ADJ, new BigDecimal("-16.67")));
         invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
 
         // CHECK NEW ACCOUNT ITEMS
         invoices =invoiceUserApi.getInvoicesByAccount(newAccount.getId(), callContext);
         assertEquals(invoices.size(), 1);
 
-        toBeChecked = ImmutableList.<ExpectedItemCheck>of(
-                new ExpectedItemCheck(new LocalDate(2012,5,3), new LocalDate(2012,5,15), InvoiceItemType.RECURRING, new BigDecimal("99.98")));
+        toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,3), new LocalDate(2012,5,15), InvoiceItemType.RECURRING, new BigDecimal("99.98")));
         invoiceChecker.checkInvoice(invoices.get(0).getId(), callContext, toBeChecked);
     }
 }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestEntitlement.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestEntitlement.java
index 6876f32..3b7adf4 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestEntitlement.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestEntitlement.java
@@ -25,7 +25,7 @@ import org.testng.annotations.Test;
 
 import com.ning.billing.account.api.Account;
 import com.ning.billing.api.TestApiListener.NextEvent;
-import com.ning.billing.beatrix.util.InvoiceChecker.ExpectedItemCheck;
+import com.ning.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
 import com.ning.billing.catalog.api.ActionPolicy;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
@@ -42,9 +42,10 @@ import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
-@Guice(modules = {BeatrixModule.class})
+@Guice(modules = {BeatrixIntegrationModule.class})
 public class TestEntitlement extends TestIntegrationBase {
 
+
     @Test(groups = "slow")
     public void testForcePolicy() throws Exception {
         // We take april as it has 30 days (easier to play with BCD)
@@ -83,8 +84,8 @@ public class TestEntitlement extends TestIntegrationBase {
 
         List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
         assertEquals(invoices.size(), 2);
-        ImmutableList<ExpectedItemCheck> toBeChecked = ImmutableList.<ExpectedItemCheck>of(
-                new ExpectedItemCheck(new LocalDate(2012,5,1), new LocalDate(2013,5,1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
+        ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,1), new LocalDate(2013,5,1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
         invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, toBeChecked);
 
         //
@@ -98,16 +99,16 @@ public class TestEntitlement extends TestIntegrationBase {
 
         invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
         assertEquals(invoices.size(), 3);
-        toBeChecked = ImmutableList.<ExpectedItemCheck>of(
-                new ExpectedItemCheck(new LocalDate(2012,5,1), new LocalDate(2013,5,1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
-                new ExpectedItemCheck(new LocalDate(2012,5,1), new LocalDate(2013,5,1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-2399.95")),
-                new ExpectedItemCheck(new LocalDate(2012,5,11), new LocalDate(2012,5,11), InvoiceItemType.CBA_ADJ, new BigDecimal("2399.95")));
+        toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,1), new LocalDate(2013,5,1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,1), new LocalDate(2013,5,1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-2399.95")),
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,11), new LocalDate(2012,5,11), InvoiceItemType.CBA_ADJ, new BigDecimal("2399.95")));
         invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, toBeChecked);
 
-        toBeChecked = ImmutableList.<ExpectedItemCheck>of(
-                new ExpectedItemCheck(new LocalDate(2012,5,1), new LocalDate(2012,5,11), InvoiceItemType.RECURRING, new BigDecimal("65.76")),
-                new ExpectedItemCheck(new LocalDate(2012,5,11), new LocalDate(2012,6,1), InvoiceItemType.RECURRING, new BigDecimal("169.32")),
-                new ExpectedItemCheck(new LocalDate(2012,5,11), new LocalDate(2012,5,11), InvoiceItemType.CBA_ADJ, new BigDecimal("-235.08")));
+        toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,1), new LocalDate(2012,5,11), InvoiceItemType.RECURRING, new BigDecimal("65.76")),
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,11), new LocalDate(2012,6,1), InvoiceItemType.RECURRING, new BigDecimal("169.32")),
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,11), new LocalDate(2012,5,11), InvoiceItemType.CBA_ADJ, new BigDecimal("-235.08")));
         invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
 
 
@@ -125,20 +126,21 @@ public class TestEntitlement extends TestIntegrationBase {
         assertEquals(invoices.size(), 4);
 
 
-        toBeChecked = ImmutableList.<ExpectedItemCheck>of(
-                new ExpectedItemCheck(new LocalDate(2012,5,1), new LocalDate(2012,5,11), InvoiceItemType.RECURRING, new BigDecimal("65.76")),
-                new ExpectedItemCheck(new LocalDate(2012,5,11), new LocalDate(2012,6,1), InvoiceItemType.RECURRING, new BigDecimal("169.32")),
-                new ExpectedItemCheck(new LocalDate(2012,5,11), new LocalDate(2012,5,11), InvoiceItemType.CBA_ADJ, new BigDecimal("-235.08")),
-                new ExpectedItemCheck(new LocalDate(2012,5,11), new LocalDate(2012,6,1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-169.32")),
-                new ExpectedItemCheck(new LocalDate(2012,5,11), new LocalDate(2012,5,11), InvoiceItemType.CBA_ADJ, new BigDecimal("169.32")));
+        toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,1), new LocalDate(2012,5,11), InvoiceItemType.RECURRING, new BigDecimal("65.76")),
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,11), new LocalDate(2012,6,1), InvoiceItemType.RECURRING, new BigDecimal("169.32")),
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,11), new LocalDate(2012,5,11), InvoiceItemType.CBA_ADJ, new BigDecimal("-235.08")),
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,11), new LocalDate(2012,6,1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-169.32")),
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,11), new LocalDate(2012,5,11), InvoiceItemType.CBA_ADJ, new BigDecimal("169.32")));
         invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
 
 
-        toBeChecked = ImmutableList.<ExpectedItemCheck>of(
-                new ExpectedItemCheck(new LocalDate(2012,5,11), new LocalDate(2012,6,1), InvoiceItemType.RECURRING, new BigDecimal("137.76")),
-                new ExpectedItemCheck(new LocalDate(2012,5,11), new LocalDate(2012,5,11), InvoiceItemType.CBA_ADJ, new BigDecimal("-137.76")));
+        toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,11), new LocalDate(2012,6,1), InvoiceItemType.RECURRING, new BigDecimal("137.76")),
+                new ExpectedInvoiceItemCheck(new LocalDate(2012,5,11), new LocalDate(2012,5,11), InvoiceItemType.CBA_ADJ, new BigDecimal("-137.76")));
         invoiceChecker.checkInvoice(invoices.get(3).getId(), callContext, toBeChecked);
 
 
     }
+
 }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
index 8f21dfa..b1cb705 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
@@ -16,10 +16,6 @@
 
 package com.ning.billing.beatrix.integration;
 
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
-
 import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
@@ -31,8 +27,10 @@ import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountData;
 import com.ning.billing.api.TestApiListener.NextEvent;
-import com.ning.billing.beatrix.util.InvoiceChecker.ExpectedItemCheck;
+import com.ning.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
+import com.ning.billing.beatrix.util.PaymentChecker.ExpectedPaymentCheck;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.api.PhaseType;
@@ -44,43 +42,57 @@ import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItemType;
+import com.ning.billing.payment.api.PaymentStatus;
 
-@Guice(modules = {BeatrixModule.class})
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+@Guice(modules = {BeatrixIntegrationModule.class})
 public class TestIntegration extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testCancelBPWithAOTheSameDay() throws Exception {
 
-        final Account account = createAccountWithPaymentMethod(getAccountData(1));
+        final AccountData accountData = getAccountData(1);
+        final Account account = createAccountWithPaymentMethod(accountData);
+        accountChecker.checkAccount(account.getId(), accountData, callContext);
 
         // We take april as it has 30 days (easier to play with BCD)
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
         clock.setDay(new LocalDate(2012, 4, 1));
 
         final SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", callContext);
+        entitlementChecker.checkBundleNoAudits(bundle.getId(), bundle.getAccountId(), bundle.getExternalKey(), callContext);
 
         //
         // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
         //
         final Subscription bpSubscription = createSubscriptionAndCheckForCompletion(bundle.getId(), "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
-        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
-
+        // Check bundle after BP got created otherwise we get an error from auditApi.
+        entitlementChecker.checkSubscriptionCreated(bpSubscription.getId(), callContext);
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        entitlementChecker.checkBundleAuditUpdated(bundle.getId(), callContext);
         //
         // ADD ADD_ON ON THE SAME DAY
         //
         createSubscriptionAndCheckForCompletion(bundle.getId(), "Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT);
-        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("399.95")));
+        Invoice invoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("399.95")));
+        paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 4, 1), new BigDecimal("399.95"), PaymentStatus.SUCCESS, invoice.getId(), Currency.USD));
+        entitlementChecker.checkBundleAuditUpdated(bundle.getId(), callContext);
 
         //
-        // CANCEL BP ON THE SAME DAY (we should have two cancellations, BP and AO)
-        // There is no invoice created as we only adjust the previous invoice.
-        //
-        cancelSubscriptionAndCheckForCompletion(bpSubscription, clock.getUTCNow(), NextEvent.CANCEL, NextEvent.CANCEL);
+                                    // CANCEL BP ON THE SAME DAY (we should have two cancellations, BP and AO)
+                                    // There is no invoice created as we only adjust the previous invoice.
+                                    //
+                                    cancelSubscriptionAndCheckForCompletion(bpSubscription, clock.getUTCNow(), NextEvent.CANCEL, NextEvent.CANCEL);
         invoiceChecker.checkInvoice(account.getId(), 2,
-                                    callContext, new ExpectedItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("399.95")),
+                                    callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("399.95")),
                                     // The second invoice should be adjusted for the AO (we paid for the full period) and since we paid we should also see a CBA
-                                    new ExpectedItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-399.95")),
-                                    new ExpectedItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 4, 1), InvoiceItemType.CBA_ADJ, new BigDecimal("399.95")));
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-399.95")),
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 4, 1), InvoiceItemType.CBA_ADJ, new BigDecimal("399.95")));
+        entitlementChecker.checkBundleAuditUpdated(bundle.getId(), callContext);
+
     }
 
     @Test(groups = "slow")
@@ -102,7 +114,7 @@ public class TestIntegration extends TestIntegrationBase {
         // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
         //
         SubscriptionData subscription = subscriptionDataFromSubscription(createSubscriptionAndCheckForCompletion(bundle.getId(), "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE));
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         // No end date for the trial item (fixed price of zero), and CTD should be today (i.e. when the trial started)
         invoiceChecker.checkChargedThroughDate(subscription.getId(), clock.getUTCToday(), callContext);
 
@@ -110,7 +122,7 @@ public class TestIntegration extends TestIntegrationBase {
         // CHANGE PLAN IMMEDIATELY AND EXPECT BOTH EVENTS: NextEvent.CHANGE NextEvent.INVOICE
         //
         subscription = subscriptionDataFromSubscription(changeSubscriptionAndCheckForCompletion(subscription, "Assault-Rifle", BillingPeriod.MONTHLY, NextEvent.CHANGE, NextEvent.INVOICE));
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), clock.getUTCToday(), callContext);
 
         //
@@ -120,8 +132,8 @@ public class TestIntegration extends TestIntegrationBase {
         setDateAndCheckForCompletion(new DateTime(2012, 2, 29, 23, 59, 59, 0, testTimeZone));
         setDateAndCheckForCompletion(new DateTime(2012, 3, 1, 23, 59, 59, 0, testTimeZone));
         setDateAndCheckForCompletion(new DateTime(2012, 3, 2, 23, 59, 59, 0, testTimeZone), NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 3, 2),
-                                                                                               new LocalDate(2012, 3, 31), InvoiceItemType.RECURRING, new BigDecimal("561.25")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 3, 2),
+                                                                                                                   new LocalDate(2012, 3, 31), InvoiceItemType.RECURRING, new BigDecimal("561.25")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 3, 31), callContext);
 
         //
@@ -135,22 +147,22 @@ public class TestIntegration extends TestIntegrationBase {
         final LocalDate firstRecurringPistolDate = subscription.getChargedThroughDate().toLocalDate();
         final LocalDate secondRecurringPistolDate = firstRecurringPistolDate.plusMonths(1);
         addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT);
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 3, 31), new LocalDate(2012, 4, 30), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 3, 31), new LocalDate(2012, 4, 30), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), secondRecurringPistolDate, callContext);
 
         //
         // MOVE 3 * TIME AFTER NEXT BILL CYCLE DAY AND EXPECT EVENT : NextEvent.INVOICE, NextEvent.PAYMENT
         //
         addDaysAndCheckForCompletion(31, NextEvent.INVOICE, NextEvent.PAYMENT);
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 4, 30), new LocalDate(2012, 5, 31), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 30), new LocalDate(2012, 5, 31), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 5, 31), callContext);
 
         addDaysAndCheckForCompletion(31, NextEvent.INVOICE, NextEvent.PAYMENT);
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 6, 30), callContext);
 
         addDaysAndCheckForCompletion(31, NextEvent.INVOICE, NextEvent.PAYMENT);
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 7, 31), callContext);
 
         //
@@ -184,7 +196,7 @@ public class TestIntegration extends TestIntegrationBase {
         // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
         //
         SubscriptionData subscription = subscriptionDataFromSubscription(createSubscriptionAndCheckForCompletion(bundle.getId(), "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE));
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         // No end date for the trial item (fixed price of zero), and CTD should be today (i.e. when the trial started)
         invoiceChecker.checkChargedThroughDate(subscription.getId(), clock.getUTCToday(), callContext);
 
@@ -192,7 +204,7 @@ public class TestIntegration extends TestIntegrationBase {
         // CHANGE PLAN IMMEDIATELY AND EXPECT BOTH EVENTS: NextEvent.CHANGE NextEvent.INVOICE
         //
         subscription = subscriptionDataFromSubscription(changeSubscriptionAndCheckForCompletion(subscription, "Assault-Rifle", BillingPeriod.MONTHLY, NextEvent.CHANGE, NextEvent.INVOICE));
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), clock.getUTCToday(), callContext);
 
         //
@@ -202,8 +214,8 @@ public class TestIntegration extends TestIntegrationBase {
         setDateAndCheckForCompletion(new DateTime(2012, 2, 29, 23, 59, 59, 0, testTimeZone));
         setDateAndCheckForCompletion(new DateTime(2012, 3, 1, 23, 59, 59, 0, testTimeZone));
         setDateAndCheckForCompletion(new DateTime(2012, 3, 2, 23, 59, 59, 0, testTimeZone), NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 3, 2),
-                                                                                               new LocalDate(2012, 4, 2), InvoiceItemType.RECURRING, new BigDecimal("599.95")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 3, 2),
+                                                                                                                   new LocalDate(2012, 4, 2), InvoiceItemType.RECURRING, new BigDecimal("599.95")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 4, 2), callContext);
 
         //
@@ -217,22 +229,22 @@ public class TestIntegration extends TestIntegrationBase {
         final LocalDate firstRecurringPistolDate = subscription.getChargedThroughDate().toLocalDate();
         final LocalDate secondRecurringPistolDate = firstRecurringPistolDate.plusMonths(1);
         addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT);
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 4, 2), new LocalDate(2012, 5, 2), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 2), new LocalDate(2012, 5, 2), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), secondRecurringPistolDate, callContext);
 
         //
         // MOVE 3 * TIME AFTER NEXT BILL CYCLE DAY AND EXPECT EVENT : NextEvent.INVOICE, NextEvent.PAYMENT
         //
         addDaysAndCheckForCompletion(31, NextEvent.INVOICE, NextEvent.PAYMENT);
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 2), new LocalDate(2012, 6, 2), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 2), new LocalDate(2012, 6, 2), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 6, 2), callContext);
 
         addDaysAndCheckForCompletion(31, NextEvent.INVOICE, NextEvent.PAYMENT);
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 6, 2), new LocalDate(2012, 7, 2), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 2), new LocalDate(2012, 7, 2), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 7, 2), callContext);
 
         addDaysAndCheckForCompletion(31, NextEvent.INVOICE, NextEvent.PAYMENT);
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 7, 2), new LocalDate(2012, 8, 2), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 2), new LocalDate(2012, 8, 2), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 8, 2), callContext);
 
         //
@@ -266,7 +278,9 @@ public class TestIntegration extends TestIntegrationBase {
         // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
         //
         SubscriptionData subscription = subscriptionDataFromSubscription(createSubscriptionAndCheckForCompletion(bundle.getId(), "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE));
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+
+
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         // No end date for the trial item (fixed price of zero), and CTD should be today (i.e. when the trial started)
         invoiceChecker.checkChargedThroughDate(subscription.getId(), clock.getUTCToday(), callContext);
 
@@ -274,24 +288,24 @@ public class TestIntegration extends TestIntegrationBase {
         // CHANGE PLAN IMMEDIATELY AND EXPECT BOTH EVENTS: NextEvent.CHANGE NextEvent.INVOICE
         //
         subscription = subscriptionDataFromSubscription(changeSubscriptionAndCheckForCompletion(subscription, "Assault-Rifle", BillingPeriod.MONTHLY, NextEvent.CHANGE, NextEvent.INVOICE));
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), clock.getUTCToday(), callContext);
 
         //
         // MOVE 4 * TIME THE CLOCK
         //
-        setDateAndCheckForCompletion(new DateTime(2012, 2, 28,  23, 59, 59, 0, testTimeZone));
-        setDateAndCheckForCompletion(new DateTime(2012, 2, 29,  23, 59, 59, 0, testTimeZone));
-        setDateAndCheckForCompletion(new DateTime(2012, 3, 1,  23, 59, 59, 0, testTimeZone));
-        setDateAndCheckForCompletion(new DateTime(2012, 3, 2,  23, 59, 59, 0, testTimeZone), NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
+        setDateAndCheckForCompletion(new DateTime(2012, 2, 28, 23, 59, 59, 0, testTimeZone));
+        setDateAndCheckForCompletion(new DateTime(2012, 2, 29, 23, 59, 59, 0, testTimeZone));
+        setDateAndCheckForCompletion(new DateTime(2012, 3, 1, 23, 59, 59, 0, testTimeZone));
+        setDateAndCheckForCompletion(new DateTime(2012, 3, 2, 23, 59, 59, 0, testTimeZone), NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
         // PRO_RATION
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 3, 2),
-                                                                                               new LocalDate(2012, 3, 3), InvoiceItemType.RECURRING, new BigDecimal("20.70")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 3, 2),
+                                                                                                                   new LocalDate(2012, 3, 3), InvoiceItemType.RECURRING, new BigDecimal("20.70")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 3, 3), callContext);
 
         setDateAndCheckForCompletion(new DateTime(2012, 3, 3, 23, 59, 59, 0, testTimeZone), NextEvent.INVOICE, NextEvent.PAYMENT);
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 3, 3),
-                                                                                               new LocalDate(2012, 4, 3), InvoiceItemType.RECURRING, new BigDecimal("599.95")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 3, 3),
+                                                                                                                   new LocalDate(2012, 4, 3), InvoiceItemType.RECURRING, new BigDecimal("599.95")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 4, 3), callContext);
 
         //
@@ -305,22 +319,22 @@ public class TestIntegration extends TestIntegrationBase {
         final LocalDate firstRecurringPistolDate = subscription.getChargedThroughDate().toLocalDate();
         final LocalDate secondRecurringPistolDate = firstRecurringPistolDate.plusMonths(1);
         addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT);
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 4, 3), new LocalDate(2012, 5, 3), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 3), new LocalDate(2012, 5, 3), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), secondRecurringPistolDate, callContext);
 
         //
         // MOVE 3 * TIME AFTER NEXT BILL CYCLE DAY AND EXPECT EVENT : NextEvent.INVOICE, NextEvent.PAYMENT
         //
         addDaysAndCheckForCompletion(31, NextEvent.INVOICE, NextEvent.PAYMENT);
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 5, 3), new LocalDate(2012, 6, 3), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 3), new LocalDate(2012, 6, 3), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 6, 3), callContext);
 
         addDaysAndCheckForCompletion(31, NextEvent.INVOICE, NextEvent.PAYMENT);
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 6, 3), new LocalDate(2012, 7, 3), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 3), new LocalDate(2012, 7, 3), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 7, 3), callContext);
 
         addDaysAndCheckForCompletion(31, NextEvent.INVOICE, NextEvent.PAYMENT);
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 7, 3), new LocalDate(2012, 8, 3), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 3), new LocalDate(2012, 8, 3), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 8, 3), callContext);
 
         //
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
index bbd2e47..8d1962c 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
@@ -49,6 +49,8 @@ import com.ning.billing.api.TestListenerStatus;
 import com.ning.billing.beatrix.BeatrixTestSuiteWithEmbeddedDB;
 import com.ning.billing.beatrix.bus.api.ExternalBus;
 import com.ning.billing.beatrix.lifecycle.Lifecycle;
+import com.ning.billing.beatrix.util.AccountChecker;
+import com.ning.billing.beatrix.util.EntitlementChecker;
 import com.ning.billing.beatrix.util.InvoiceChecker;
 import com.ning.billing.beatrix.util.PaymentChecker;
 import com.ning.billing.catalog.api.BillingPeriod;
@@ -74,6 +76,7 @@ import com.ning.billing.invoice.model.InvoicingConfiguration;
 import com.ning.billing.junction.plumbing.api.BlockingSubscription;
 import com.ning.billing.mock.MockAccountBuilder;
 import com.ning.billing.mock.api.MockBillCycleDay;
+import com.ning.billing.overdue.OverdueUserApi;
 import com.ning.billing.overdue.wrapper.OverdueWrapperFactory;
 import com.ning.billing.payment.api.Payment;
 import com.ning.billing.payment.api.PaymentApi;
@@ -83,6 +86,7 @@ import com.ning.billing.payment.provider.MockPaymentProviderPlugin;
 import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.svcapi.account.AccountInternalApi;
+import com.ning.billing.util.svcapi.junction.BlockingInternalApi;
 import com.ning.billing.util.svcsapi.bus.BusService;
 
 import com.google.common.base.Function;
@@ -141,15 +145,21 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
     protected EntitlementTimelineApi repairApi;
 
     @Inject
+    protected OverdueUserApi overdueUserApi;
+
+    @Inject
     protected InvoiceUserApi invoiceUserApi;
 
     @Inject
     protected InvoicePaymentApi invoicePaymentApi;
 
     @Inject
+    protected BlockingInternalApi blockingApi;
+
+    @Inject
     protected PaymentApi paymentApi;
 
-    @Named(BeatrixModule.PLUGIN_NAME)
+    @Named(BeatrixIntegrationModule.PLUGIN_NAME)
     @Inject
     protected MockPaymentProviderPlugin paymentPlugin;
 
@@ -175,9 +185,15 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
     protected PaymentChecker paymentChecker;
 
     @Inject
+    protected AccountChecker accountChecker;
+
+    @Inject
     protected ExternalBus externalBus;
 
     @Inject
+    protected EntitlementChecker entitlementChecker;
+
+    @Inject
     protected AccountInternalApi accountInternalApi;
 
     protected TestApiListener busHandler;
@@ -222,6 +238,8 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
         lifecycle.fireStartupSequencePriorEventRegistration();
         busService.getBus().register(busHandler);
         lifecycle.fireStartupSequencePostEventRegistration();
+
+        paymentPlugin.clear();
     }
 
     @AfterMethod(groups = "slow")
@@ -304,7 +322,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
             }
         };
 
-        paymentApi.addPaymentMethod(BeatrixModule.PLUGIN_NAME, account, true, info, callContext);
+        paymentApi.addPaymentMethod(BeatrixIntegrationModule.PLUGIN_NAME, account, true, info, callContext);
         return accountUserApi.getAccountById(account.getId(), callContext);
     }
 
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
index 14cde9d..b390d97 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
@@ -17,7 +17,7 @@
 package com.ning.billing.beatrix.integration;
 
 import java.util.Collection;
-import java.util.Map;
+import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
@@ -48,7 +48,7 @@ import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
-@Guice(modules = {BeatrixModule.class})
+@Guice(modules = {BeatrixIntegrationModule.class})
 public class TestIntegrationWithAutoInvoiceOffTag extends TestIntegrationBase {
 
     @Inject
@@ -175,7 +175,7 @@ public class TestIntegrationWithAutoInvoiceOffTag extends TestIntegrationBase {
 
     private void add_AUTO_INVOICING_OFF_Tag(final UUID id, final ObjectType type) throws TagDefinitionApiException, TagApiException {
         tagApi.addTag(id, type, ControlTagType.AUTO_INVOICING_OFF.getId(), callContext);
-        final Map<String, Tag> tags = tagApi.getTags(id, type, callContext);
+        final List<Tag> tags = tagApi.getTags(id, type, callContext);
         assertEquals(tags.size(), 1);
     }
 
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java
index 4871538..a3e3931 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java
@@ -17,7 +17,7 @@ package com.ning.billing.beatrix.integration;
 
 import java.math.BigDecimal;
 import java.util.Collection;
-import java.util.Map;
+import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
@@ -49,7 +49,7 @@ import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
-@Guice(modules = {BeatrixModule.class})
+@Guice(modules = {BeatrixIntegrationModule.class})
 public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
 
 
@@ -280,7 +280,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
 
     private void add_AUTO_PAY_OFF_Tag(final UUID id, final ObjectType type) throws TagDefinitionApiException, TagApiException {
         tagApi.addTag(id, type, ControlTagType.AUTO_PAY_OFF.getId(), callContext);
-        final Map<String, Tag> tags = tagApi.getTags(id, type, callContext);
+        final List<Tag> tags = tagApi.getTags(id, type, callContext);
         assertEquals(tags.size(), 1);
     }
 
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestPublicBus.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestPublicBus.java
index 4faf255..bc807a5 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestPublicBus.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestPublicBus.java
@@ -37,7 +37,7 @@ import com.ning.billing.entitlement.api.user.SubscriptionData;
 
 import com.google.common.eventbus.Subscribe;
 
-@Guice(modules = {BeatrixModule.class})
+@Guice(modules = {BeatrixIntegrationModule.class})
 public class TestPublicBus extends TestIntegrationBase {
 
     private PublicListener publicListener;
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java
index d3fd015..c8844bd 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java
@@ -50,7 +50,7 @@ import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
 @Test(groups = "slow")
-@Guice(modules = {BeatrixModule.class})
+@Guice(modules = {BeatrixIntegrationModule.class})
 public class TestRepairIntegration extends TestIntegrationBase {
 
 
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/util/AccountChecker.java b/beatrix/src/test/java/com/ning/billing/beatrix/util/AccountChecker.java
new file mode 100644
index 0000000..e5de4ca
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/util/AccountChecker.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2010-2012 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.beatrix.util;
+
+import java.util.UUID;
+
+
+import javax.inject.Inject;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountData;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.util.callcontext.CallContext;
+
+public class AccountChecker {
+
+    private static final Logger log = LoggerFactory.getLogger(AccountChecker.class);
+
+    private final AccountUserApi accountApi;
+    private final AuditChecker auditChecker;
+
+
+    @Inject
+    public AccountChecker(final AccountUserApi accountApi, final AuditChecker auditChecker) {
+        this.accountApi = accountApi;
+        this.auditChecker = auditChecker;
+    }
+
+
+    public Account checkAccount(final UUID accountId, final AccountData accountData, final CallContext context) throws Exception {
+
+        final Account account = accountApi.getAccountById(accountId, context);
+        // Not all test pass it, since this is always the same test
+        if (accountData != null) {
+            Assert.assertEquals(account.getName(), accountData.getName());
+            Assert.assertEquals(account.getFirstNameLength(), accountData.getFirstNameLength());
+            Assert.assertEquals(account.getEmail(), accountData.getEmail());
+            Assert.assertEquals(account.getPhone(), accountData.getPhone());
+            Assert.assertEquals(account.isNotifiedForInvoices(), accountData.isNotifiedForInvoices());
+            Assert.assertEquals(account.getExternalKey(), accountData.getExternalKey());
+            Assert.assertEquals(account.getBillCycleDay().getDayOfMonthUTC(), accountData.getBillCycleDay().getDayOfMonthUTC());
+            Assert.assertEquals(account.getBillCycleDay().getDayOfMonthLocal(), accountData.getBillCycleDay().getDayOfMonthLocal());
+            Assert.assertEquals(account.getCurrency(), accountData.getCurrency());
+            Assert.assertEquals(account.getTimeZone(), accountData.getTimeZone());
+            // createWithPaymentMethod will update the paymentMethod
+            //Assert.assertEquals(account.getPaymentMethodId(), accountData.getPaymentMethodId());
+        }
+
+        auditChecker.checkAccountCreated(account, context);
+        return account;
+    }
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/util/AuditChecker.java b/beatrix/src/test/java/com/ning/billing/beatrix/util/AuditChecker.java
new file mode 100644
index 0000000..9a7cd01
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/util/AuditChecker.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2010-2012 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.beatrix.util;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.IDBI;
+import org.skife.jdbi.v2.tweak.HandleCallback;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.dao.AccountSqlDao;
+import com.ning.billing.entitlement.api.timeline.EntitlementRepairException;
+import com.ning.billing.entitlement.engine.dao.BundleSqlDao;
+import com.ning.billing.entitlement.engine.dao.EntitlementEventSqlDao;
+import com.ning.billing.entitlement.engine.dao.SubscriptionSqlDao;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.dao.InvoiceItemSqlDao;
+import com.ning.billing.invoice.dao.InvoiceSqlDao;
+import com.ning.billing.payment.api.Payment;
+import com.ning.billing.payment.dao.PaymentSqlDao;
+import com.ning.billing.util.api.AuditLevel;
+import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.audit.AuditLogsForAccount;
+import com.ning.billing.util.audit.AuditLogsForBundles;
+import com.ning.billing.util.audit.AuditLogsForInvoices;
+import com.ning.billing.util.audit.AuditLogsForPayments;
+import com.ning.billing.util.audit.ChangeType;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.entity.Entity;
+import com.ning.billing.util.entity.dao.EntityModelDao;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+
+import com.google.inject.Inject;
+
+public class AuditChecker {
+
+    private static final Logger log = LoggerFactory.getLogger(AuditChecker.class);
+
+    private final AuditUserApi auditUserApi;
+    private final IDBI dbi;
+    private final InternalCallContextFactory callContextFactory;
+
+
+    @Inject
+    public AuditChecker(final AuditUserApi auditUserApi, final IDBI dbi, final InternalCallContextFactory callContextFactory) {
+        this.auditUserApi = auditUserApi;
+        this.dbi = dbi;
+        this.callContextFactory = callContextFactory;
+    }
+
+
+    /***********************************************  ACCOUNT ********************************************************/
+
+    public void checkAccountCreated(final Account account, final CallContext context) {
+        AuditLogsForAccount result = auditUserApi.getAuditLogsForAccount(account.getId(), AuditLevel.FULL, context);
+        checkAuditLog(ChangeType.INSERT, context, result.getAccountAuditLogs().get(0), account.getId(), AccountSqlDao.class, true, true);
+        checkAuditLog(ChangeType.UPDATE, context, result.getAccountAuditLogs().get(1), account.getId(), AccountSqlDao.class, true, true);
+    }
+
+
+    public void checkPaymentCreated(final Payment payment, final CallContext context) {
+        AuditLogsForPayments result = getAuditLogsForPayment(payment, context);
+        Assert.assertEquals(result.getPaymentsAuditLogs().size(), 1);
+        final List<AuditLog> paymentLogs = result.getPaymentsAuditLogs().get(payment.getId());
+        Assert.assertEquals(paymentLogs.size(), 2);
+        checkAuditLog(ChangeType.INSERT, context, paymentLogs.get(0), payment.getId(), PaymentSqlDao.class, true, false);
+        checkAuditLog(ChangeType.UPDATE, context, paymentLogs.get(1), payment.getId(), PaymentSqlDao.class, true, false);
+    }
+
+    /***********************************************  BUNDLE ********************************************************/
+
+
+    // Pass the call context used to create the bundle
+    public void checkBundleCreated(final UUID bundleId, final CallContext context) {
+        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
+        Assert.assertEquals(auditLogsForBundles.getBundlesAuditLogs().keySet().size(), 1);
+        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.getBundlesAuditLogs().get(bundleId).get(0), bundleId, BundleSqlDao.class, false, false);
+    }
+
+    // Pass the call context used to update the bundle
+    public void checkBundleUpdated(final UUID bundleId, final CallContext context) {
+        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
+        Assert.assertEquals(auditLogsForBundles.getBundlesAuditLogs().keySet().size(), 1);
+        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.getBundlesAuditLogs().get(bundleId).get(auditLogsForBundles.getBundlesAuditLogs().get(bundleId).size() - 1), bundleId, BundleSqlDao.class, false, false);
+    }
+
+    /***********************************************  SUBSCRIPTION ********************************************************/
+
+    // Pass the call context used to create the subscription
+    public void checkSubscriptionCreated(final UUID bundleId, final UUID subscriptionId, final CallContext context) {
+        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
+
+        Assert.assertEquals(auditLogsForBundles.getSubscriptionsAuditLogs().keySet().size(), 1);
+        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).get(0), subscriptionId, SubscriptionSqlDao.class, false, true);
+    }
+
+    // Pass the call context used to update the subscription
+    public void checkSubscriptionUpdated(final UUID bundleId, final UUID subscriptionId, final CallContext context) {
+        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
+
+        Assert.assertEquals(auditLogsForBundles.getSubscriptionsAuditLogs().keySet().size(), 1);
+        Assert.assertEquals(auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).size(), 2);
+        checkAuditLog(ChangeType.INSERT, auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).get(0));
+        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).get(1), subscriptionId, SubscriptionSqlDao.class, false, false);
+    }
+
+    /***********************************************  SUBSCRIPTION EVENTS ********************************************************/
+
+    // Pass the call context used to create the subscription event
+    public void checkSubscriptionEventCreated(final UUID bundleId, final UUID subscriptionEventId, final CallContext context) {
+        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
+        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).get(0), subscriptionEventId, EntitlementEventSqlDao.class, false, true);
+    }
+
+    // Pass the call context used to update the subscription event
+    public void checkSubscriptionEventUpdated(final UUID bundleId, final UUID subscriptionEventId, final CallContext context) {
+        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
+        checkAuditLog(ChangeType.INSERT, auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).get(0));
+        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).get(1), subscriptionEventId, EntitlementEventSqlDao.class, false, true);
+    }
+
+
+
+    /***********************************************  PAYMENT ********************************************************/
+
+    private AuditLogsForPayments getAuditLogsForPayment(final Payment payment, final CallContext context) {
+        AuditLogsForPayments results = auditUserApi.getAuditLogsForPayments(Collections.singletonList(payment), AuditLevel.FULL, context);
+        return results;
+    }
+
+    /***********************************************  INVOICE ********************************************************/
+
+    public void checkInvoiceCreated(final Invoice invoice, final CallContext context) {
+        AuditLogsForInvoices result = getAuditLogForInvoice(invoice, context);
+        Assert.assertEquals(result.getInvoiceAuditLogs().keySet().size(), 1);
+        final List<AuditLog> invoiceLogs = result.getInvoiceAuditLogs().get(invoice.getId());
+        Assert.assertEquals(invoiceLogs.size(), 1);
+        checkAuditLog(ChangeType.INSERT, context, invoiceLogs.get(0), invoice.getId(), InvoiceSqlDao.class, false, false);
+
+        Assert.assertEquals(result.getInvoiceItemsAuditLogs().keySet().size(), invoice.getInvoiceItems().size());
+        for (InvoiceItem cur : invoice.getInvoiceItems()) {
+            final List<AuditLog> auditLogs = result.getInvoiceItemsAuditLogs().get(cur.getId());
+            Assert.assertEquals(auditLogs.size(), 1);
+            checkAuditLog(ChangeType.INSERT, context, auditLogs.get(0), cur.getId(), InvoiceItemSqlDao.class, false, false);
+        }
+    }
+
+
+
+
+    private AuditLogsForBundles getAuditLogsForBundle(final UUID bundleId, final CallContext context) {
+        try {
+            return auditUserApi.getAuditLogsForBundle(bundleId, AuditLevel.FULL, context);
+        } catch (EntitlementRepairException e) {
+            Assert.fail(e.toString());
+            return null;
+        }
+    }
+
+    private AuditLogsForInvoices getAuditLogForInvoice(final Invoice invoice, final CallContext context) {
+        return auditUserApi.getAuditLogsForInvoices(Collections.singletonList(invoice), AuditLevel.FULL, context);
+    }
+
+
+    private void checkAuditLog(final ChangeType insert, final AuditLog auditLog) {
+        checkAuditLog(insert, null, auditLog, null, EntitySqlDao.class, false, false);
+    }
+
+
+    private <T extends EntitySqlDao<M, E>, M extends EntityModelDao<E>, E extends Entity> void checkAuditLog(final ChangeType changeType, @Nullable final CallContext context, final AuditLog auditLog, final UUID entityId, Class<T> sqlDao,
+                                                                                                             boolean useHistory, boolean checkContext) {
+        Assert.assertEquals(auditLog.getChangeType(), changeType);
+
+        if (checkContext) {
+            Assert.assertEquals(auditLog.getUserName(), context.getUserName());
+            Assert.assertEquals(auditLog.getComment(), context.getComments());
+            //Assert.assertEquals(auditLog.getCreatedDate().comparesTo(context.getCreatedDate()));
+        }
+        Assert.assertEquals(auditLog.getUserToken(), context.getUserToken().toString());
+        final M entityModel = extractEntityModelFromEntityWithTargetRecordId(auditLog.getId(), sqlDao, context, useHistory);
+        Assert.assertEquals(entityModel.getId(), entityId);
+
+    }
+
+
+    private <T extends EntitySqlDao<M, E>, M extends EntityModelDao<E>, E extends Entity> M extractEntityModelFromEntityWithTargetRecordId(final UUID entityId, final Class<T> sqlDao, final CallContext context, final boolean useHistory) {
+        Integer targetRecordId = dbi.withHandle(new HandleCallback<Integer>() {
+            @Override
+            public Integer withHandle(final Handle handle) throws Exception {
+
+                List<Map<String, Object>> res = handle.select("select target_record_id from audit_log where id = '" + entityId.toString() + "';");
+                return (Integer) res.get(0).get("target_record_id");
+            }
+        });
+
+        if (useHistory) {
+            Long entityRecordId =  dbi.onDemand(sqlDao).getHistoryTargetRecordId(Long.valueOf(targetRecordId), callContextFactory.createInternalCallContext(context));
+            targetRecordId = new Integer(entityRecordId.intValue());
+        }
+        return dbi.onDemand(sqlDao).getByRecordId(Long.valueOf(targetRecordId), callContextFactory.createInternalCallContext(context));
+    }
+
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/util/EntitlementChecker.java b/beatrix/src/test/java/com/ning/billing/beatrix/util/EntitlementChecker.java
new file mode 100644
index 0000000..b2c0d02
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/util/EntitlementChecker.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2010-2012 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.beatrix.util;
+
+import java.util.List;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+
+import com.ning.billing.account.api.AccountUserApi;
+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.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
+import com.ning.billing.junction.plumbing.api.BlockingSubscription;
+import com.ning.billing.util.callcontext.CallContext;
+
+public class EntitlementChecker {
+
+
+    private static final Logger log = LoggerFactory.getLogger(EntitlementChecker.class);
+
+    private final EntitlementUserApi entitlementApi;
+    private final AuditChecker auditChecker;
+
+    @Inject
+    public EntitlementChecker(final EntitlementUserApi entitlementApi, final AuditChecker auditChecker) {
+        this.entitlementApi = entitlementApi;
+        this.auditChecker = auditChecker;
+    }
+
+    public SubscriptionBundle checkBundleNoAudits(final UUID bundleId, final UUID expectedAccountId, final String expectedKey, final CallContext context) throws EntitlementUserApiException {
+        final SubscriptionBundle bundle =  entitlementApi.getBundleFromId(bundleId, context);
+        Assert.assertNotNull(bundle);
+        Assert.assertEquals(bundle.getAccountId(), expectedAccountId);
+        Assert.assertEquals(bundle.getExternalKey(), expectedKey);
+        return bundle;
+    }
+
+    public SubscriptionBundle checkBundleAuditUpdated(final UUID bundleId, final CallContext context) throws EntitlementUserApiException {
+        final SubscriptionBundle bundle =  entitlementApi.getBundleFromId(bundleId, context);
+        auditChecker.checkBundleUpdated(bundle.getId(), context);
+        return bundle;
+    }
+
+    public Subscription checkSubscriptionCreated(final UUID subscriptionId, final CallContext context) throws EntitlementUserApiException {
+        final Subscription subscription = entitlementApi.getSubscriptionFromId(subscriptionId, context);
+        Assert.assertNotNull(subscription);
+        auditChecker.checkSubscriptionCreated(subscription.getBundleId(), subscriptionId, context);
+
+        List<SubscriptionTransitionData> subscriptionEvents = getSubscriptionEvents(subscription);
+        Assert.assertTrue(subscriptionEvents.size() >= 1);
+        auditChecker.checkSubscriptionEventCreated(subscription.getBundleId(), subscriptionEvents.get(0).getId(), context);
+
+        auditChecker.checkBundleCreated(subscription.getBundleId(), context);
+        return subscription;
+    }
+
+    private List<SubscriptionTransitionData> getSubscriptionEvents(final Subscription subscription) {
+        return ((SubscriptionData) ((BlockingSubscription) subscription).getDelegateSubscription()).getAllTransitions();
+    }
+
+
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/util/InvoiceChecker.java b/beatrix/src/test/java/com/ning/billing/beatrix/util/InvoiceChecker.java
index 4af8df7..1f1e849 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/util/InvoiceChecker.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/util/InvoiceChecker.java
@@ -36,7 +36,7 @@ import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.invoice.api.InvoiceUserApi;
-import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.callcontext.CallContext;
 
 import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
@@ -52,42 +52,45 @@ public class InvoiceChecker {
 
     private final InvoiceUserApi invoiceUserApi;
     private final EntitlementUserApi entitlementApi;
+    private final AuditChecker auditChecker;
 
     @Inject
-    public InvoiceChecker(final InvoiceUserApi invoiceUserApi, final EntitlementUserApi entitlementApi) {
+    public InvoiceChecker(final InvoiceUserApi invoiceUserApi, final EntitlementUserApi entitlementApi, final AuditChecker auditChecker) {
         this.invoiceUserApi = invoiceUserApi;
         this.entitlementApi = entitlementApi;
+        this.auditChecker = auditChecker;
     }
 
-    public void checkInvoice(final UUID accountId, final int invoiceOrderingNumber, final TenantContext context, final ExpectedItemCheck... expected) throws InvoiceApiException {
-        checkInvoice(accountId, invoiceOrderingNumber, context, ImmutableList.<ExpectedItemCheck>copyOf(expected));
+    public Invoice checkInvoice(final UUID accountId, final int invoiceOrderingNumber, final CallContext context, final ExpectedInvoiceItemCheck... expected) throws InvoiceApiException {
+        return checkInvoice(accountId, invoiceOrderingNumber, context, ImmutableList.<ExpectedInvoiceItemCheck>copyOf(expected));
     }
 
-    public void checkInvoice(final UUID accountId, final int invoiceOrderingNumber, final TenantContext context, final List<ExpectedItemCheck> expected) throws InvoiceApiException {
+    public Invoice checkInvoice(final UUID accountId, final int invoiceOrderingNumber, final CallContext context, final List<ExpectedInvoiceItemCheck> expected) throws InvoiceApiException {
         final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId, context);
         Assert.assertEquals(invoices.size(), invoiceOrderingNumber);
         final Invoice invoice = invoices.get(invoiceOrderingNumber - 1);
         checkInvoice(invoice.getId(), context, expected);
+        return invoice;
     }
 
-    public void checkRepairedInvoice(final UUID accountId, final int invoiceNb, final TenantContext context, final ExpectedItemCheck... expected) throws InvoiceApiException {
-        checkRepairedInvoice(accountId, invoiceNb, context, ImmutableList.<ExpectedItemCheck>copyOf(expected));
+    public void checkRepairedInvoice(final UUID accountId, final int invoiceNb, final CallContext context, final ExpectedInvoiceItemCheck... expected) throws InvoiceApiException {
+        checkRepairedInvoice(accountId, invoiceNb, context, ImmutableList.<ExpectedInvoiceItemCheck>copyOf(expected));
     }
 
-    public void checkRepairedInvoice(final UUID accountId, final int invoiceNb, final TenantContext context, final List<ExpectedItemCheck> expected) throws InvoiceApiException {
+    public void checkRepairedInvoice(final UUID accountId, final int invoiceNb, final CallContext context, final List<ExpectedInvoiceItemCheck> expected) throws InvoiceApiException {
         final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId, context);
         Assert.assertTrue(invoices.size() > invoiceNb);
         final Invoice invoice = invoices.get(invoiceNb - 1);
         checkInvoice(invoice.getId(), context, expected);
     }
 
-    public void checkInvoice(final UUID invoiceId, final TenantContext context, final List<ExpectedItemCheck> expected) throws InvoiceApiException {
+    public void checkInvoice(final UUID invoiceId, final CallContext context, final List<ExpectedInvoiceItemCheck> expected) throws InvoiceApiException {
         final Invoice invoice = invoiceUserApi.getInvoice(invoiceId, context);
         Assert.assertNotNull(invoice);
 
         final List<InvoiceItem> actual = invoice.getInvoiceItems();
         Assert.assertEquals(expected.size(), actual.size());
-        for (final ExpectedItemCheck cur : expected) {
+        for (final ExpectedInvoiceItemCheck cur : expected) {
             boolean found = false;
             for (final InvoiceItem in : actual) {
                 // Match first on type and start date
@@ -113,19 +116,20 @@ public class InvoiceChecker {
                                           cur.getType(), cur.getStartDate(), cur.getAmount(), cur.getEndDate(), invoice.getId()));
             }
         }
+        auditChecker.checkInvoiceCreated(invoice, context);
     }
 
-    public void checkNullChargedThroughDate(final UUID subscriptionId, final TenantContext context) {
+    public void checkNullChargedThroughDate(final UUID subscriptionId, final CallContext context) {
         checkChargedThroughDate(subscriptionId, null, context);
     }
 
-    public void checkChargedThroughDate(final UUID subscriptionId, final LocalDate expectedLocalCTD, final TenantContext context) {
+    public void checkChargedThroughDate(final UUID subscriptionId, final LocalDate expectedLocalCTD, final CallContext context) {
         try {
             final Subscription subscription = entitlementApi.getSubscriptionFromId(subscriptionId, context);
             if (expectedLocalCTD == null) {
                 assertNull(subscription.getChargedThroughDate());
             } else {
-                final DateTime expectedCTD = expectedLocalCTD.toDateTime(new LocalTime(subscription.getStartDate().getMillis()), DateTimeZone.UTC);
+                final DateTime expectedCTD = expectedLocalCTD.toDateTime(new LocalTime(subscription.getStartDate().getMillis(), DateTimeZone.UTC), DateTimeZone.UTC);
                 final String msg = String.format("Checking CTD for subscription %s : expectedLocalCTD = %s => expectedCTD = %s, got %s",
                                                  subscriptionId, expectedLocalCTD, expectedCTD, subscription.getChargedThroughDate());
                 log.info(msg);
@@ -137,15 +141,15 @@ public class InvoiceChecker {
         }
     }
 
-    public static class ExpectedItemCheck {
+    public static class ExpectedInvoiceItemCheck {
 
         private final LocalDate startDate;
         private final LocalDate endDate;
         private final InvoiceItemType type;
         private final BigDecimal Amount;
 
-        public ExpectedItemCheck(final LocalDate startDate, final LocalDate endDate,
-                                 final InvoiceItemType type, final BigDecimal amount) {
+        public ExpectedInvoiceItemCheck(final LocalDate startDate, final LocalDate endDate,
+                                        final InvoiceItemType type, final BigDecimal amount) {
             this.startDate = startDate;
             this.endDate = endDate;
             this.type = type;
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/util/PaymentChecker.java b/beatrix/src/test/java/com/ning/billing/beatrix/util/PaymentChecker.java
index e875590..fbb54a5 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/util/PaymentChecker.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/util/PaymentChecker.java
@@ -13,13 +13,25 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
+
 package com.ning.billing.beatrix.util;
 
 
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.LocalDate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.testng.Assert;
 
-import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+import com.ning.billing.catalog.api.Currency;
+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.PaymentStatus;
+import com.ning.billing.util.callcontext.CallContext;
 
 import com.google.inject.Inject;
 
@@ -27,11 +39,67 @@ public class PaymentChecker {
 
     private static final Logger log = LoggerFactory.getLogger(PaymentChecker.class);
 
-    //private final EntitlementUserApi entitlementApi;
+
+    private final PaymentApi paymentApi;
+    private final AuditChecker auditChecker;
 
     @Inject
-    public PaymentChecker(final EntitlementUserApi entitlementApi) {
-        //this.entitlementApi = entitlementApi;
+    public PaymentChecker(final PaymentApi paymentApi, final AuditChecker auditChecker) {
+        this.paymentApi = paymentApi;
+        this.auditChecker = auditChecker;
+    }
+
+    public Payment checkPayment(final UUID accountId, final int paymentOrderingNumber, final CallContext context, ExpectedPaymentCheck expected) throws PaymentApiException {
+        final List<Payment> payments = paymentApi.getAccountPayments(accountId, context);
+        Assert.assertEquals(payments.size(), paymentOrderingNumber);
+        final Payment payment = payments.get(paymentOrderingNumber - 1);
+        checkPayment(accountId, payment, context, expected);
+        return payment;
+    }
+
+    private void checkPayment(final UUID accountId, final Payment payment, final CallContext context, final ExpectedPaymentCheck expected) {
+        Assert.assertEquals(payment.getAccountId(), accountId);
+        Assert.assertTrue(payment.getAmount().compareTo(expected.getAmount()) == 0);
+        Assert.assertEquals(payment.getPaymentStatus(),expected.getStatus());
+        Assert.assertEquals(payment.getInvoiceId(), expected.getInvoiceId());
+        Assert.assertEquals(payment.getCurrency(), expected.getCurrency());
+        auditChecker.checkPaymentCreated(payment, context);
     }
 
+    public static class ExpectedPaymentCheck {
+
+        private final LocalDate paymentDate;
+        private final BigDecimal amount;
+        private final PaymentStatus status;
+        private final UUID invoiceId;
+        private final Currency currency;
+
+        public ExpectedPaymentCheck(final LocalDate paymentDate, final BigDecimal amount, final PaymentStatus status, final UUID invoiceId, final Currency currency) {
+            this.paymentDate = paymentDate;
+            this.amount = amount;
+            this.status = status;
+            this.invoiceId = invoiceId;
+            this.currency = currency;
+        }
+
+        public Currency getCurrency() {
+            return currency;
+        }
+
+        public LocalDate getPaymentDate() {
+            return paymentDate;
+        }
+
+        public BigDecimal getAmount() {
+            return amount;
+        }
+
+        public PaymentStatus getStatus() {
+            return status;
+        }
+
+        public UUID getInvoiceId() {
+            return invoiceId;
+        }
+    }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultEntitlementTimelineApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultEntitlementTimelineApi.java
index 4e0d044..f61d2a6 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultEntitlementTimelineApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultEntitlementTimelineApi.java
@@ -79,7 +79,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
     @Override
     public BundleTimeline getBundleTimeline(final SubscriptionBundle bundle, final TenantContext context)
             throws EntitlementRepairException {
-        return getBundleTimelineInternal(bundle, bundle.getKey(), context);
+        return getBundleTimelineInternal(bundle, bundle.getExternalKey(), context);
     }
 
     @Override
@@ -105,9 +105,9 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
             if (subscriptions.size() == 0) {
                 throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NO_ACTIVE_SUBSCRIPTIONS, bundle.getId());
             }
-            final String viewId = getViewId(((SubscriptionBundleData) bundle).getLastSysUpdateTime(), subscriptions);
+            final String viewId = getViewId(((SubscriptionBundleData) bundle).getLastSysUpdateDate(), subscriptions);
             final List<SubscriptionTimeline> repairs = createGetSubscriptionRepairList(subscriptions, Collections.<SubscriptionTimeline>emptyList());
-            return createGetBundleRepair(bundle.getId(), bundle.getKey(), viewId, repairs);
+            return createGetBundleRepair(bundle.getId(), bundle.getExternalKey(), viewId, repairs);
         } catch (CatalogApiException e) {
             throw new EntitlementRepairException(e);
         }
@@ -128,7 +128,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
                 throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NO_ACTIVE_SUBSCRIPTIONS, input.getId());
             }
 
-            final String viewId = getViewId(((SubscriptionBundleData) bundle).getLastSysUpdateTime(), subscriptions);
+            final String viewId = getViewId(((SubscriptionBundleData) bundle).getLastSysUpdateDate(), subscriptions);
             if (!viewId.equals(input.getViewId())) {
                 throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_VIEW_CHANGED, input.getId(), input.getViewId(), viewId);
             }
@@ -232,7 +232,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
                 baseSubscriptionRepair.addFutureAddonCancellation(addOnSubscriptionInRepair, context);
 
                 final List<SubscriptionTimeline> repairs = createGetSubscriptionRepairList(subscriptions, convertDataRepair(inRepair));
-                return createGetBundleRepair(input.getId(), bundle.getKey(), input.getViewId(), repairs);
+                return createGetBundleRepair(input.getId(), bundle.getExternalKey(), input.getViewId(), repairs);
             } else {
                 dao.repair(bundle.getAccountId(), input.getId(), inRepair, internalCallContextFactory.createInternalCallContext(bundle.getAccountId(), context));
                 return getBundleTimeline(input.getId(), context);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundleData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundleData.java
index 8e0630a..7197b33 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundleData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundleData.java
@@ -30,7 +30,7 @@ public class SubscriptionBundleData extends EntityBase implements SubscriptionBu
 
     private final String key;
     private final UUID accountId;
-    private final DateTime lastSysTimeUpdate;
+    private final DateTime lastSysUpdateDate;
     private final OverdueState<SubscriptionBundle> overdueState;
 
     public SubscriptionBundleData(final String name, final UUID accountId, final DateTime startDate) {
@@ -46,12 +46,12 @@ public class SubscriptionBundleData extends EntityBase implements SubscriptionBu
         super(id, null, null);
         this.key = key;
         this.accountId = accountId;
-        this.lastSysTimeUpdate = lastSysUpdate;
+        this.lastSysUpdateDate = lastSysUpdate;
         this.overdueState = overdueState;
     }
 
     @Override
-    public String getKey() {
+    public String getExternalKey() {
         return key;
     }
 
@@ -60,8 +60,8 @@ public class SubscriptionBundleData extends EntityBase implements SubscriptionBu
         return accountId;
     }
 
-    public DateTime getLastSysUpdateTime() {
-        return lastSysTimeUpdate;
+    public DateTime getLastSysUpdateDate() {
+        return lastSysUpdateDate;
     }
 
     @Override
@@ -81,7 +81,7 @@ public class SubscriptionBundleData extends EntityBase implements SubscriptionBu
         sb.append("{accountId=").append(accountId);
         sb.append(", id=").append(id);
         sb.append(", key='").append(key).append('\'');
-        sb.append(", lastSysTimeUpdate=").append(lastSysTimeUpdate);
+        sb.append(", lastSysUpdateDate=").append(lastSysUpdateDate);
         sb.append(", overdueState=").append(overdueState);
         sb.append('}');
         return sb.toString();
@@ -107,7 +107,7 @@ public class SubscriptionBundleData extends EntityBase implements SubscriptionBu
         if (key != null ? !key.equals(that.key) : that.key != null) {
             return false;
         }
-        if (lastSysTimeUpdate != null ? !lastSysTimeUpdate.equals(that.lastSysTimeUpdate) : that.lastSysTimeUpdate != null) {
+        if (lastSysUpdateDate != null ? !lastSysUpdateDate.equals(that.lastSysUpdateDate) : that.lastSysUpdateDate != null) {
             return false;
         }
         if (overdueState != null ? !overdueState.equals(that.overdueState) : that.overdueState != null) {
@@ -122,7 +122,7 @@ public class SubscriptionBundleData extends EntityBase implements SubscriptionBu
         int result = id != null ? id.hashCode() : 0;
         result = 31 * result + (key != null ? key.hashCode() : 0);
         result = 31 * result + (accountId != null ? accountId.hashCode() : 0);
-        result = 31 * result + (lastSysTimeUpdate != null ? lastSysTimeUpdate.hashCode() : 0);
+        result = 31 * result + (lastSysUpdateDate != null ? lastSysUpdateDate.hashCode() : 0);
         result = 31 * result + (overdueState != null ? overdueState.hashCode() : 0);
         return result;
     }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
index 0750c93..bf6caa8 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
@@ -16,84 +16,42 @@
 
 package com.ning.billing.entitlement.engine.dao;
 
-import java.sql.ResultSet;
-import java.sql.SQLException;
 import java.util.Date;
 import java.util.List;
-import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.skife.jdbi.v2.SQLStatement;
-import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 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.mixins.CloseMe;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
-import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
-import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
+import com.ning.billing.entitlement.engine.dao.model.SubscriptionBundleModelDao;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.AuditSqlDao;
-import com.ning.billing.util.dao.BinderBase;
-import com.ning.billing.util.dao.MapperBase;
+import com.ning.billing.util.entity.dao.Audited;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 
-@ExternalizedSqlViaStringTemplate3()
-@RegisterMapper(BundleSqlDao.ISubscriptionBundleSqlMapper.class)
-public interface BundleSqlDao extends Transactional<BundleSqlDao>, EntitySqlDao<SubscriptionBundle>,
-                                      AuditSqlDao, CloseMe, Transmogrifier {
-    @SqlUpdate
-    public void insertBundle(@Bind(binder = SubscriptionBundleBinder.class) SubscriptionBundleData bundle,
-                             @InternalTenantContextBinder final InternalCallContext context);
+@EntitySqlDaoStringTemplate
+public interface BundleSqlDao extends EntitySqlDao<SubscriptionBundleModelDao, SubscriptionBundle> {
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     public void updateBundleLastSysTime(@Bind("id") String id,
                                         @Bind("lastSysUpdateDate") Date lastSysUpdate,
-                                        @InternalTenantContextBinder final InternalCallContext context);
-
-    @SqlQuery
-    public SubscriptionBundle getBundleFromId(@Bind("id") String id,
-                                              @InternalTenantContextBinder final InternalTenantContext context);
+                                        @BindBean final InternalCallContext context);
 
     @SqlQuery
-    public SubscriptionBundle getBundleFromAccountAndKey(@Bind("accountId") String accountId,
-                                                         @Bind("externalKey") String externalKey,
-                                                         @InternalTenantContextBinder final InternalTenantContext context);
+    public SubscriptionBundleModelDao getBundleFromAccountAndKey(@Bind("accountId") String accountId,
+                                                                 @Bind("externalKey") String externalKey,
+                                                                 @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    public List<SubscriptionBundle> getBundleFromAccount(@Bind("accountId") String accountId,
-                                                         @InternalTenantContextBinder final InternalTenantContext context);
+    public List<SubscriptionBundleModelDao> getBundleFromAccount(@Bind("accountId") String accountId,
+                                                                 @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    public List<SubscriptionBundle> getBundlesForKey(@Bind("externalKey") String externalKey,
-                                                     @InternalTenantContextBinder final InternalTenantContext context);
-
-    public static class SubscriptionBundleBinder extends BinderBase implements Binder<Bind, SubscriptionBundleData> {
-        @Override
-        public void bind(@SuppressWarnings("rawtypes") final SQLStatement stmt, final Bind bind, final SubscriptionBundleData bundle) {
-            stmt.bind("id", bundle.getId().toString());
-            stmt.bind("externalKey", bundle.getKey());
-            stmt.bind("accountId", bundle.getAccountId().toString());
-            stmt.bind("lastSysUpdateDate", getDate(bundle.getLastSysUpdateTime()));
-        }
-    }
-
-    public static class ISubscriptionBundleSqlMapper extends MapperBase implements ResultSetMapper<SubscriptionBundle> {
-        @Override
-        public SubscriptionBundle map(final int arg, final ResultSet r, final StatementContext ctx) throws SQLException {
-            final UUID id = UUID.fromString(r.getString("id"));
-            final String key = r.getString("external_key");
-            final UUID accountId = UUID.fromString(r.getString("account_id"));
-            final DateTime lastSysUpdateDate = getDateTime(r, "last_sys_update_date");
-            return new SubscriptionBundleData(id, key, accountId, lastSysUpdateDate);
-        }
-    }
+    public List<SubscriptionBundleModelDao> getBundlesForKey(@Bind("externalKey") String externalKey,
+                                                             @BindBean final InternalTenantContext context);
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.java
index 9fff155..3fe18f0 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.java
@@ -16,192 +16,48 @@
 
 package com.ning.billing.entitlement.engine.dao;
 
-import java.sql.ResultSet;
-import java.sql.SQLException;
 import java.util.Date;
 import java.util.List;
-import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.skife.jdbi.v2.SQLStatement;
-import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
-import org.skife.jdbi.v2.sqlobject.customizers.Mapper;
-import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
-import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
+import com.ning.billing.entitlement.engine.dao.model.EntitlementEventModelDao;
 import com.ning.billing.entitlement.events.EntitlementEvent;
-import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
-import com.ning.billing.entitlement.events.EventBaseBuilder;
-import com.ning.billing.entitlement.events.phase.PhaseEvent;
-import com.ning.billing.entitlement.events.phase.PhaseEventBuilder;
-import com.ning.billing.entitlement.events.phase.PhaseEventData;
-import com.ning.billing.entitlement.events.user.ApiEvent;
-import com.ning.billing.entitlement.events.user.ApiEventBuilder;
-import com.ning.billing.entitlement.events.user.ApiEventCancel;
-import com.ning.billing.entitlement.events.user.ApiEventChange;
-import com.ning.billing.entitlement.events.user.ApiEventCreate;
-import com.ning.billing.entitlement.events.user.ApiEventMigrateBilling;
-import com.ning.billing.entitlement.events.user.ApiEventMigrateEntitlement;
-import com.ning.billing.entitlement.events.user.ApiEventReCreate;
-import com.ning.billing.entitlement.events.user.ApiEventTransfer;
-import com.ning.billing.entitlement.events.user.ApiEventType;
-import com.ning.billing.entitlement.events.user.ApiEventUncancel;
-import com.ning.billing.entitlement.exceptions.EntitlementError;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.AuditSqlDao;
-import com.ning.billing.util.dao.BinderBase;
-import com.ning.billing.util.dao.MapperBase;
+import com.ning.billing.util.entity.dao.Audited;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 
-@ExternalizedSqlViaStringTemplate3()
-public interface EntitlementEventSqlDao extends Transactional<EntitlementEventSqlDao>, AuditSqlDao,
-                                                EntitySqlDao<EntitlementEvent>, CloseMe, Transmogrifier {
-
-    @SqlQuery
-    @Mapper(EventSqlMapper.class)
-    public EntitlementEvent getEventById(@Bind("id") String id,
-                                         @InternalTenantContextBinder final InternalTenantContext context);
-
-    @SqlUpdate
-    public void insertEvent(@Bind(binder = EventSqlDaoBinder.class) EntitlementEvent evt,
-                            @InternalTenantContextBinder final InternalCallContext context);
+@EntitySqlDaoStringTemplate
+public interface EntitlementEventSqlDao extends EntitySqlDao<EntitlementEventModelDao, EntitlementEvent> {
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     public void unactiveEvent(@Bind("id") String id,
-                              @InternalTenantContextBinder final InternalCallContext context);
+                              @BindBean final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     public void reactiveEvent(@Bind("id") String id,
-                              @InternalTenantContextBinder final InternalCallContext context);
+                              @BindBean final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     public void updateVersion(@Bind("id") String id,
                               @Bind("currentVersion") Long currentVersion,
-                              @InternalTenantContextBinder final InternalCallContext context);
+                              @BindBean final InternalCallContext context);
 
     @SqlQuery
-    @Mapper(EventSqlMapper.class)
-    public List<EntitlementEvent> getFutureActiveEventForSubscription(@Bind("subscriptionId") String subscriptionId,
-                                                                      @Bind("now") Date now,
-                                                                      @InternalTenantContextBinder final InternalTenantContext context);
+    public List<EntitlementEventModelDao> getFutureActiveEventForSubscription(@Bind("subscriptionId") String subscriptionId,
+                                                                              @Bind("now") Date now,
+                                                                              @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    @Mapper(EventSqlMapper.class)
-    public List<EntitlementEvent> getEventsForSubscription(@Bind("subscriptionId") String subscriptionId,
-                                                           @InternalTenantContextBinder final InternalTenantContext context);
-
-    public static class EventSqlDaoBinder extends BinderBase implements Binder<Bind, EntitlementEvent> {
-
-        @Override
-        public void bind(@SuppressWarnings("rawtypes") final SQLStatement stmt, final Bind bind, final EntitlementEvent evt) {
-
-            String planName = null;
-            String phaseName = null;
-            String priceListName = null;
-            String userType = null;
-            if (evt.getType() == EventType.API_USER) {
-                final ApiEvent userEvent = (ApiEvent) evt;
-                planName = userEvent.getEventPlan();
-                phaseName = userEvent.getEventPlanPhase();
-                priceListName = userEvent.getPriceList();
-                userType = userEvent.getEventType().toString();
-            } else {
-                phaseName = ((PhaseEvent) evt).getPhase();
-            }
-            stmt.bind("id", evt.getId().toString());
-            stmt.bind("eventType", evt.getType().toString());
-            stmt.bind("userType", userType);
-            stmt.bind("requestedDate", getDate(evt.getRequestedDate()));
-            stmt.bind("effectiveDate", getDate(evt.getEffectiveDate()));
-            stmt.bind("subscriptionId", evt.getSubscriptionId().toString());
-            stmt.bind("planName", planName);
-            stmt.bind("phaseName", phaseName);
-            stmt.bind("priceListName", priceListName);
-            stmt.bind("currentVersion", evt.getActiveVersion());
-            stmt.bind("isActive", evt.isActive());
-        }
-    }
-
-    public static class EventSqlMapper extends MapperBase implements ResultSetMapper<EntitlementEvent> {
-
-        @Override
-        public EntitlementEvent map(final int index, final ResultSet r, final StatementContext ctx)
-                throws SQLException {
-
-            final long totalOrdering = r.getLong("record_id");
-            final UUID id = UUID.fromString(r.getString("id"));
-            final EventType eventType = EventType.valueOf(r.getString("event_type"));
-            final ApiEventType userType = (eventType == EventType.API_USER) ? ApiEventType.valueOf(r.getString("user_type")) : null;
-            final DateTime createdDate = getDateTime(r, "created_date");
-            final DateTime updatedDate = getDateTime(r, "updated_date");
-            final DateTime requestedDate = getDateTime(r, "requested_date");
-            final DateTime effectiveDate = getDateTime(r, "effective_date");
-            final UUID subscriptionId = UUID.fromString(r.getString("subscription_id"));
-            final String planName = r.getString("plan_name");
-            final String phaseName = r.getString("phase_name");
-            final String priceListName = r.getString("price_list_name");
-            final long currentVersion = r.getLong("current_version");
-            final boolean isActive = r.getBoolean("is_active");
-            final UUID userToken = r.getString("user_token") != null ? UUID.fromString(r.getString("user_token")) : null;
-
-            final EventBaseBuilder<?> base = ((eventType == EventType.PHASE) ?
-                                              new PhaseEventBuilder() :
-                                              new ApiEventBuilder())
-                    .setTotalOrdering(totalOrdering)
-                    .setUuid(id)
-                    .setSubscriptionId(subscriptionId)
-                    .setCreatedDate(createdDate)
-                    .setUpdatedDate(updatedDate)
-                    .setRequestedDate(requestedDate)
-                    .setEffectiveDate(effectiveDate)
-                    .setProcessedDate(createdDate)
-                    .setActiveVersion(currentVersion)
-                    .setActive(isActive);
-
-            EntitlementEvent result = null;
-            if (eventType == EventType.PHASE) {
-                result = new PhaseEventData(new PhaseEventBuilder(base).setPhaseName(phaseName));
-            } else if (eventType == EventType.API_USER) {
-                final ApiEventBuilder builder = new ApiEventBuilder(base)
-                        .setEventPlan(planName)
-                        .setEventPlanPhase(phaseName)
-                        .setEventPriceList(priceListName)
-                        .setEventType(userType)
-                        .setUserToken(userToken)
-                        .setFromDisk(true);
-
-                if (userType == ApiEventType.CREATE) {
-                    result = new ApiEventCreate(builder);
-                } else if (userType == ApiEventType.RE_CREATE) {
-                    result = new ApiEventReCreate(builder);
-                } else if (userType == ApiEventType.MIGRATE_ENTITLEMENT) {
-                    result = new ApiEventMigrateEntitlement(builder);
-                } else if (userType == ApiEventType.MIGRATE_BILLING) {
-                    result = new ApiEventMigrateBilling(builder);
-                } else if (userType == ApiEventType.TRANSFER) {
-                    result = new ApiEventTransfer(builder);
-                } else if (userType == ApiEventType.CHANGE) {
-                    result = new ApiEventChange(builder);
-                } else if (userType == ApiEventType.CANCEL) {
-                    result = new ApiEventCancel(builder);
-                } else if (userType == ApiEventType.RE_CREATE) {
-                    result = new ApiEventReCreate(builder);
-                } else if (userType == ApiEventType.UNCANCEL) {
-                    result = new ApiEventUncancel(builder);
-                }
-            } else {
-                throw new EntitlementError(String.format("Can't deserialize event %s", eventType));
-            }
-            return result;
-        }
-    }
+    public List<EntitlementEventModelDao> getEventsForSubscription(@Bind("subscriptionId") String subscriptionId,
+                                                                   @BindBean final InternalTenantContext context);
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/model/EntitlementEventModelDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/model/EntitlementEventModelDao.java
new file mode 100644
index 0000000..fa2507a
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/model/EntitlementEventModelDao.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.entitlement.engine.dao.model;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
+import com.ning.billing.entitlement.events.EventBaseBuilder;
+import com.ning.billing.entitlement.events.phase.PhaseEvent;
+import com.ning.billing.entitlement.events.phase.PhaseEventBuilder;
+import com.ning.billing.entitlement.events.phase.PhaseEventData;
+import com.ning.billing.entitlement.events.user.ApiEvent;
+import com.ning.billing.entitlement.events.user.ApiEventBuilder;
+import com.ning.billing.entitlement.events.user.ApiEventCancel;
+import com.ning.billing.entitlement.events.user.ApiEventChange;
+import com.ning.billing.entitlement.events.user.ApiEventCreate;
+import com.ning.billing.entitlement.events.user.ApiEventMigrateBilling;
+import com.ning.billing.entitlement.events.user.ApiEventMigrateEntitlement;
+import com.ning.billing.entitlement.events.user.ApiEventReCreate;
+import com.ning.billing.entitlement.events.user.ApiEventTransfer;
+import com.ning.billing.entitlement.events.user.ApiEventType;
+import com.ning.billing.entitlement.events.user.ApiEventUncancel;
+import com.ning.billing.entitlement.exceptions.EntitlementError;
+import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.dao.EntityModelDao;
+
+public class EntitlementEventModelDao extends EntityBase implements EntityModelDao<EntitlementEvent> {
+
+    private long totalOrdering;
+    private EventType eventType;
+    private ApiEventType userType;
+    private DateTime requestedDate;
+    private DateTime effectiveDate;
+    private UUID subscriptionId;
+    private String planName;
+    private String phaseName;
+    private String priceListName;
+    private long currentVersion;
+    private boolean isActive;
+    private UUID userToken;
+
+    public EntitlementEventModelDao() { /* For the DAO mapper */ }
+
+    public EntitlementEventModelDao(final UUID id, final long totalOrdering, final EventType eventType, final ApiEventType userType,
+                                    final DateTime requestedDate, final DateTime effectiveDate, final UUID subscriptionId,
+                                    final String planName, final String phaseName, final String priceListName, final long currentVersion,
+                                    final boolean active, final DateTime createDate, final DateTime updateDate, final UUID userToken) {
+        super(id, createDate, updateDate);
+        this.totalOrdering = totalOrdering;
+        this.eventType = eventType;
+        this.userType = userType;
+        this.requestedDate = requestedDate;
+        this.effectiveDate = effectiveDate;
+        this.subscriptionId = subscriptionId;
+        this.planName = planName;
+        this.phaseName = phaseName;
+        this.priceListName = priceListName;
+        this.currentVersion = currentVersion;
+        this.isActive = active;
+        this.userToken = userToken;
+    }
+
+    public EntitlementEventModelDao(final EntitlementEvent src) {
+        super(src.getId(), src.getCreatedDate(), src.getUpdatedDate());
+        this.totalOrdering = src.getTotalOrdering();
+        this.eventType = src.getType();
+        this.userType = eventType == EventType.API_USER ? ((ApiEvent) src).getEventType() : null;
+        this.requestedDate = src.getRequestedDate();
+        this.effectiveDate = src.getEffectiveDate();
+        this.subscriptionId = src.getSubscriptionId();
+        this.planName = eventType == EventType.API_USER ? ((ApiEvent) src).getEventPlan() : null;
+        this.phaseName = eventType == EventType.API_USER ? ((ApiEvent) src).getEventPlanPhase() : ((PhaseEvent) src).getPhase();
+        this.priceListName = eventType == EventType.API_USER ? ((ApiEvent) src).getPriceList() : null;
+        this.currentVersion = src.getActiveVersion();
+        this.isActive = src.isActive();
+    }
+
+    public long getTotalOrdering() {
+        return totalOrdering;
+    }
+
+    public UUID getUserToken() {
+        return userToken;
+    }
+
+
+    public EventType getEventType() {
+        return eventType;
+    }
+
+    public ApiEventType getUserType() {
+        return userType;
+    }
+
+    public DateTime getRequestedDate() {
+        return requestedDate;
+    }
+
+    public DateTime getEffectiveDate() {
+        return effectiveDate;
+    }
+
+    public UUID getSubscriptionId() {
+        return subscriptionId;
+    }
+
+    public String getPlanName() {
+        return planName;
+    }
+
+    public String getPhaseName() {
+        return phaseName;
+    }
+
+    public String getPriceListName() {
+        return priceListName;
+    }
+
+    public long getCurrentVersion() {
+        return currentVersion;
+    }
+
+    // TODO required for jdbi binder
+    public boolean getIsActive() {
+        return isActive;
+    }
+
+    public boolean isActive() {
+        return isActive;
+    }
+
+    public static EntitlementEvent toEntitlementEvent(final EntitlementEventModelDao src) {
+
+        if (src == null) {
+            return null;
+        }
+
+        final EventBaseBuilder<?> base = ((src.getEventType() == EventType.PHASE) ?
+                                          new PhaseEventBuilder() :
+                                          new ApiEventBuilder())
+                .setTotalOrdering(src.getTotalOrdering())
+                .setUuid(src.getId())
+                .setSubscriptionId(src.getSubscriptionId())
+                .setCreatedDate(src.getCreatedDate())
+                .setUpdatedDate(src.getUpdatedDate())
+                .setRequestedDate(src.getRequestedDate())
+                .setEffectiveDate(src.getEffectiveDate())
+                .setProcessedDate(src.getCreatedDate())
+                .setActiveVersion(src.getCurrentVersion())
+                .setActive(src.isActive());
+
+        EntitlementEvent result = null;
+        if (src.getEventType() == EventType.PHASE) {
+            result = new PhaseEventData(new PhaseEventBuilder(base).setPhaseName(src.getPhaseName()));
+        } else if (src.getEventType() == EventType.API_USER) {
+            final ApiEventBuilder builder = new ApiEventBuilder(base)
+                    .setEventPlan(src.getPlanName())
+                    .setEventPlanPhase(src.getPhaseName())
+                    .setEventPriceList(src.getPriceListName())
+                    .setEventType(src.getUserType())
+                    .setFromDisk(true)
+                    .setUserToken(src.getUserToken());
+
+            if (src.getUserType() == ApiEventType.CREATE) {
+                result = new ApiEventCreate(builder);
+            } else if (src.getUserType() == ApiEventType.RE_CREATE) {
+                result = new ApiEventReCreate(builder);
+            } else if (src.getUserType() == ApiEventType.MIGRATE_ENTITLEMENT) {
+                result = new ApiEventMigrateEntitlement(builder);
+            } else if (src.getUserType() == ApiEventType.MIGRATE_BILLING) {
+                result = new ApiEventMigrateBilling(builder);
+            } else if (src.getUserType() == ApiEventType.TRANSFER) {
+                result = new ApiEventTransfer(builder);
+            } else if (src.getUserType() == ApiEventType.CHANGE) {
+                result = new ApiEventChange(builder);
+            } else if (src.getUserType() == ApiEventType.CANCEL) {
+                result = new ApiEventCancel(builder);
+            } else if (src.getUserType() == ApiEventType.RE_CREATE) {
+                result = new ApiEventReCreate(builder);
+            } else if (src.getUserType() == ApiEventType.UNCANCEL) {
+                result = new ApiEventUncancel(builder);
+            }
+        } else {
+            throw new EntitlementError(String.format("Can't figure out event %s", src.getEventType()));
+        }
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("EntitlementEventModelDao");
+        sb.append("{totalOrdering=").append(totalOrdering);
+        sb.append(", eventType=").append(eventType);
+        sb.append(", userType=").append(userType);
+        sb.append(", requestedDate=").append(requestedDate);
+        sb.append(", effectiveDate=").append(effectiveDate);
+        sb.append(", subscriptionId=").append(subscriptionId);
+        sb.append(", planName='").append(planName).append('\'');
+        sb.append(", phaseName='").append(phaseName).append('\'');
+        sb.append(", priceListName='").append(priceListName).append('\'');
+        sb.append(", currentVersion=").append(currentVersion);
+        sb.append(", isActive=").append(isActive);
+        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 EntitlementEventModelDao that = (EntitlementEventModelDao) o;
+
+        if (currentVersion != that.currentVersion) {
+            return false;
+        }
+        if (isActive != that.isActive) {
+            return false;
+        }
+        if (totalOrdering != that.totalOrdering) {
+            return false;
+        }
+        if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
+            return false;
+        }
+        if (eventType != that.eventType) {
+            return false;
+        }
+        if (phaseName != null ? !phaseName.equals(that.phaseName) : that.phaseName != null) {
+            return false;
+        }
+        if (planName != null ? !planName.equals(that.planName) : that.planName != null) {
+            return false;
+        }
+        if (priceListName != null ? !priceListName.equals(that.priceListName) : that.priceListName != null) {
+            return false;
+        }
+        if (requestedDate != null ? !requestedDate.equals(that.requestedDate) : that.requestedDate != null) {
+            return false;
+        }
+        if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
+            return false;
+        }
+        if (userType != that.userType) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (int) (totalOrdering ^ (totalOrdering >>> 32));
+        result = 31 * result + (eventType != null ? eventType.hashCode() : 0);
+        result = 31 * result + (userType != null ? userType.hashCode() : 0);
+        result = 31 * result + (requestedDate != null ? requestedDate.hashCode() : 0);
+        result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
+        result = 31 * result + (subscriptionId != null ? subscriptionId.hashCode() : 0);
+        result = 31 * result + (planName != null ? planName.hashCode() : 0);
+        result = 31 * result + (phaseName != null ? phaseName.hashCode() : 0);
+        result = 31 * result + (priceListName != null ? priceListName.hashCode() : 0);
+        result = 31 * result + (int) (currentVersion ^ (currentVersion >>> 32));
+        result = 31 * result + (isActive ? 1 : 0);
+        return result;
+    }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.SUBSCRIPTION_EVENTS;
+    }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/model/SubscriptionBundleModelDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/model/SubscriptionBundleModelDao.java
new file mode 100644
index 0000000..10612c9
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/model/SubscriptionBundleModelDao.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.entitlement.engine.dao.model;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
+import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.dao.EntityModelDao;
+
+public class SubscriptionBundleModelDao extends EntityBase implements EntityModelDao<SubscriptionBundle> {
+
+    private String externalKey;
+    private UUID accountId;
+    private DateTime lastSysUpdateDate;
+
+    public SubscriptionBundleModelDao() { /* For the DAO mapper */ }
+
+    public SubscriptionBundleModelDao(final UUID id, final String key, final UUID accountId, final DateTime lastSysUpdateDate,
+                                      final DateTime createdDate, final DateTime updateDate) {
+        super(id, createdDate, updateDate);
+        this.externalKey = key;
+        this.accountId = accountId;
+        this.lastSysUpdateDate = lastSysUpdateDate;
+    }
+
+    public SubscriptionBundleModelDao(final SubscriptionBundleData input) {
+        this(input.getId(), input.getExternalKey(), input.getAccountId(), input.getLastSysUpdateDate(), input.getCreatedDate(), input.getUpdatedDate());
+    }
+
+    public String getExternalKey() {
+        return externalKey;
+    }
+
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    public DateTime getLastSysUpdateDate() {
+        return lastSysUpdateDate;
+    }
+
+    public static SubscriptionBundle toSubscriptionbundle(final SubscriptionBundleModelDao src) {
+        if (src == null) {
+            return null;
+        }
+        return new SubscriptionBundleData(src.getId(), src.getExternalKey(), src.getAccountId(), src.getLastSysUpdateDate());
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("SubscriptionBundleModelDao");
+        sb.append("{externalKey='").append(externalKey).append('\'');
+        sb.append(", accountId=").append(accountId);
+        sb.append(", lastSysUpdateDate=").append(lastSysUpdateDate);
+        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 SubscriptionBundleModelDao that = (SubscriptionBundleModelDao) o;
+
+        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
+            return false;
+        }
+        if (externalKey != null ? !externalKey.equals(that.externalKey) : that.externalKey != null) {
+            return false;
+        }
+        if (lastSysUpdateDate != null ? !lastSysUpdateDate.equals(that.lastSysUpdateDate) : that.lastSysUpdateDate != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (externalKey != null ? externalKey.hashCode() : 0);
+        result = 31 * result + (accountId != null ? accountId.hashCode() : 0);
+        result = 31 * result + (lastSysUpdateDate != null ? lastSysUpdateDate.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.BUNDLES;
+    }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/model/SubscriptionModelDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/model/SubscriptionModelDao.java
new file mode 100644
index 0000000..0f0e18d
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/model/SubscriptionModelDao.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.entitlement.engine.dao.model;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.dao.EntityModelDao;
+
+public class SubscriptionModelDao extends EntityBase implements EntityModelDao<Subscription> {
+
+    private UUID bundleId;
+    private ProductCategory category;
+    private DateTime startDate;
+    private DateTime bundleStartDate;
+    private long activeVersion;
+    private DateTime chargedThroughDate;
+    private DateTime paidThroughDate;
+
+    public SubscriptionModelDao() { /* For the DAO mapper */ }
+
+    public SubscriptionModelDao(final UUID id, final UUID bundleId, final ProductCategory category, final DateTime startDate, final DateTime bundleStartDate,
+                                final long activeVersion, final DateTime chargedThroughDate, final DateTime paidThroughDate, final DateTime createdDate, final DateTime updateDate) {
+        super(id, createdDate, updateDate);
+        this.bundleId = bundleId;
+        this.category = category;
+        this.startDate = startDate;
+        this.bundleStartDate = bundleStartDate;
+        this.activeVersion = activeVersion;
+        this.chargedThroughDate = chargedThroughDate;
+        this.paidThroughDate = paidThroughDate;
+    }
+
+    public SubscriptionModelDao(final SubscriptionData src) {
+        this(src.getId(), src.getBundleId(), src.getCategory(), src.getAlignStartDate(), src.getBundleStartDate(), src.getActiveVersion(),
+             src.getChargedThroughDate(), src.getPaidThroughDate(), src.getCreatedDate(), src.getUpdatedDate());
+    }
+
+    public UUID getBundleId() {
+        return bundleId;
+    }
+
+    public ProductCategory getCategory() {
+        return category;
+    }
+
+    public DateTime getStartDate() {
+        return startDate;
+    }
+
+    public DateTime getBundleStartDate() {
+        return bundleStartDate;
+    }
+
+    public long getActiveVersion() {
+        return activeVersion;
+    }
+
+    public DateTime getChargedThroughDate() {
+        return chargedThroughDate;
+    }
+
+    public DateTime getPaidThroughDate() {
+        return paidThroughDate;
+    }
+
+    public static Subscription toSubscription(final SubscriptionModelDao src) {
+        if (src == null) {
+            return null;
+        }
+        return new SubscriptionData(new SubscriptionBuilder()
+                                            .setId(src.getId())
+                                            .setBundleId(src.getBundleId())
+                                            .setCategory(src.getCategory())
+                                            .setCreatedDate(src.getCreatedDate())
+                                            .setUpdatedDate(src.getUpdatedDate())
+                                            .setBundleStartDate(src.getBundleStartDate())
+                                            .setAlignStartDate(src.getStartDate())
+                                            .setActiveVersion(src.getActiveVersion())
+                                            .setChargedThroughDate(src.getChargedThroughDate())
+                                            .setPaidThroughDate(src.getPaidThroughDate())
+                                            .setCreatedDate(src.getCreatedDate())
+                                            .setUpdatedDate(src.getUpdatedDate()));
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("SubscriptionModelDao");
+        sb.append("{bundleId=").append(bundleId);
+        sb.append(", category=").append(category);
+        sb.append(", startDate=").append(startDate);
+        sb.append(", bundleStartDate=").append(bundleStartDate);
+        sb.append(", activeVersion=").append(activeVersion);
+        sb.append(", chargedThroughDate=").append(chargedThroughDate);
+        sb.append(", paidThroughDate=").append(paidThroughDate);
+        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 SubscriptionModelDao that = (SubscriptionModelDao) o;
+
+        if (activeVersion != that.activeVersion) {
+            return false;
+        }
+        if (bundleId != null ? !bundleId.equals(that.bundleId) : that.bundleId != null) {
+            return false;
+        }
+        if (bundleStartDate != null ? !bundleStartDate.equals(that.bundleStartDate) : that.bundleStartDate != null) {
+            return false;
+        }
+        if (category != that.category) {
+            return false;
+        }
+        if (chargedThroughDate != null ? !chargedThroughDate.equals(that.chargedThroughDate) : that.chargedThroughDate != null) {
+            return false;
+        }
+        if (paidThroughDate != null ? !paidThroughDate.equals(that.paidThroughDate) : that.paidThroughDate != 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 + (bundleId != null ? bundleId.hashCode() : 0);
+        result = 31 * result + (category != null ? category.hashCode() : 0);
+        result = 31 * result + (startDate != null ? startDate.hashCode() : 0);
+        result = 31 * result + (bundleStartDate != null ? bundleStartDate.hashCode() : 0);
+        result = 31 * result + (int) (activeVersion ^ (activeVersion >>> 32));
+        result = 31 * result + (chargedThroughDate != null ? chargedThroughDate.hashCode() : 0);
+        result = 31 * result + (paidThroughDate != null ? paidThroughDate.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.SUBSCRIPTIONS;
+    }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
index 9a7dc3f..c613d93 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
@@ -16,109 +16,44 @@
 
 package com.ning.billing.entitlement.engine.dao;
 
-import java.sql.ResultSet;
-import java.sql.SQLException;
 import java.util.Date;
 import java.util.List;
-import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.skife.jdbi.v2.SQLStatement;
-import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
-import org.skife.jdbi.v2.sqlobject.customizers.Mapper;
-import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
-import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
-import com.ning.billing.catalog.api.ProductCategory;
-import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
 import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.engine.dao.model.SubscriptionModelDao;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.AuditSqlDao;
-import com.ning.billing.util.dao.BinderBase;
-import com.ning.billing.util.dao.MapperBase;
+import com.ning.billing.util.entity.dao.Audited;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 
-@ExternalizedSqlViaStringTemplate3()
-public interface SubscriptionSqlDao extends Transactional<SubscriptionSqlDao>, AuditSqlDao, CloseMe, Transmogrifier {
-
-    @SqlUpdate
-    public void insertSubscription(@Bind(binder = SubscriptionBinder.class) SubscriptionData sub,
-                                   @InternalTenantContextBinder final InternalCallContext context);
-
-    @SqlQuery
-    @Mapper(SubscriptionMapper.class)
-    public Subscription getSubscriptionFromId(@Bind("id") String id,
-                                              @InternalTenantContextBinder final InternalTenantContext context);
+@EntitySqlDaoStringTemplate
+public interface SubscriptionSqlDao extends EntitySqlDao<SubscriptionModelDao, Subscription> {
 
     @SqlQuery
-    @Mapper(SubscriptionMapper.class)
-    public List<Subscription> getSubscriptionsFromBundleId(@Bind("bundleId") String bundleId,
-                                                           @InternalTenantContextBinder final InternalTenantContext context);
+    public List<SubscriptionModelDao> getSubscriptionsFromBundleId(@Bind("bundleId") String bundleId,
+                                                                   @BindBean final InternalTenantContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     public void updateChargedThroughDate(@Bind("id") String id, @Bind("chargedThroughDate") Date chargedThroughDate,
-                                         @InternalTenantContextBinder final InternalCallContext context);
+                                         @BindBean final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     void updateActiveVersion(@Bind("id") String id, @Bind("activeVersion") long activeVersion,
-                             @InternalTenantContextBinder final InternalCallContext context);
+                             @BindBean final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     public void updateForRepair(@Bind("id") String id, @Bind("activeVersion") long activeVersion,
                                 @Bind("startDate") Date startDate,
                                 @Bind("bundleStartDate") Date bundleStartDate,
-                                @InternalTenantContextBinder final InternalCallContext context);
-
-    public static class SubscriptionBinder extends BinderBase implements Binder<Bind, SubscriptionData> {
-
-        @Override
-        public void bind(@SuppressWarnings("rawtypes") final SQLStatement stmt, final Bind bind, final SubscriptionData sub) {
-            stmt.bind("id", sub.getId().toString());
-            stmt.bind("bundleId", sub.getBundleId().toString());
-            stmt.bind("category", sub.getCategory().toString());
-            stmt.bind("startDate", getDate(sub.getAlignStartDate()));
-            stmt.bind("bundleStartDate", getDate(sub.getBundleStartDate()));
-            stmt.bind("activeVersion", sub.getActiveVersion());
-            stmt.bind("chargedThroughDate", getDate(sub.getChargedThroughDate()));
-            stmt.bind("paidThroughDate", getDate(sub.getPaidThroughDate()));
-        }
-    }
-
-    public static class SubscriptionMapper extends MapperBase implements ResultSetMapper<SubscriptionData> {
-
-        @Override
-        public SubscriptionData map(final int arg0, final ResultSet r, final StatementContext ctx)
-                throws SQLException {
-            final UUID id = UUID.fromString(r.getString("id"));
-            final UUID bundleId = UUID.fromString(r.getString("bundle_id"));
-            final ProductCategory category = ProductCategory.valueOf(r.getString("category"));
-            final DateTime bundleStartDate = getDateTime(r, "bundle_start_date");
-            final DateTime startDate = getDateTime(r, "start_date");
-            final DateTime ctd = getDateTime(r, "charged_through_date");
-            final DateTime ptd = getDateTime(r, "paid_through_date");
-            final long activeVersion = r.getLong("active_version");
-            final DateTime createdDate = getDateTime(r, "created_date");
-            final DateTime updatedDate = getDateTime(r, "updated_date");
-            return new SubscriptionData(new SubscriptionBuilder()
-                                                .setId(id)
-                                                .setBundleId(bundleId)
-                                                .setCategory(category)
-                                                .setCreatedDate(createdDate)
-                                                .setUpdatedDate(updatedDate)
-                                                .setBundleStartDate(bundleStartDate)
-                                                .setAlignStartDate(startDate)
-                                                .setActiveVersion(activeVersion)
-                                                .setChargedThroughDate(ctd)
-                                                .setPaidThroughDate(ptd));
-        }
-    }
+                                @BindBean final InternalCallContext context);
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java b/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
index 96d5e66..459cae7 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
@@ -18,6 +18,7 @@ package com.ning.billing.entitlement.glue;
 
 import org.skife.config.ConfigurationObjectFactory;
 
+import com.ning.billing.entitlement.engine.dao.DefaultEntitlementDao;
 import com.ning.billing.util.config.EntitlementConfig;
 import com.ning.billing.entitlement.alignment.MigrationPlanAligner;
 import com.ning.billing.entitlement.alignment.PlanAligner;
@@ -40,7 +41,6 @@ import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.engine.addon.AddonUtils;
 import com.ning.billing.entitlement.engine.core.Engine;
-import com.ning.billing.entitlement.engine.dao.AuditedEntitlementDao;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.engine.dao.RepairEntitlementDao;
 import com.ning.billing.glue.EntitlementModule;
@@ -60,7 +60,7 @@ public class DefaultEntitlementModule extends AbstractModule implements Entitlem
     }
 
     protected void installEntitlementDao() {
-        bind(EntitlementDao.class).to(AuditedEntitlementDao.class).asEagerSingleton();
+        bind(EntitlementDao.class).to(DefaultEntitlementDao.class).asEagerSingleton();
         bind(EntitlementDao.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairEntitlementDao.class);
         bind(RepairEntitlementLifecycleDao.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairEntitlementDao.class);
         bind(RepairEntitlementDao.class).asEagerSingleton();
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql b/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
index ee822a9..913c472 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
@@ -58,6 +58,10 @@ CREATE TABLE bundles (
     external_key varchar(64) NOT NULL,
     account_id char(36) NOT NULL,
     last_sys_update_date datetime,
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
+    updated_by varchar(50) NOT NULL,
+    updated_date datetime NOT NULL,
     account_record_id int(11) unsigned default null,
     tenant_record_id int(11) unsigned default null,
     PRIMARY KEY(record_id)
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
index 84fe7d3..2ef637e 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
@@ -1,89 +1,63 @@
-group BundleSqlDao;
+group BundleSqlDao: EntitySqlDao;
 
-CHECK_TENANT() ::= "tenant_record_id = :tenantRecordId"
-AND_CHECK_TENANT() ::= "AND <CHECK_TENANT()>"
+tableName() ::= "bundles"
 
-fields(prefix) ::= <<
-    <prefix>id,
-    <prefix>external_key,
-    <prefix>account_id,
-    <prefix>last_sys_update_date,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
 
-insertBundle() ::= <<
-    insert into bundles (<fields()>)
-    values (:id, :externalKey, :accountId, :lastSysUpdateDate, :accountRecordId, :tenantRecordId);
+tableFields(prefix) ::= <<
+  <prefix>external_key
+, <prefix>account_id
+, <prefix>last_sys_update_date
+, <prefix>created_by
+, <prefix>created_date
+, <prefix>updated_by
+, <prefix>updated_date
 >>
 
-updateBundleLastSysTime()  ::= <<
-    update bundles
-    set
-        last_sys_update_date = :lastSysUpdateDate
-    where id = :id
-    <AND_CHECK_TENANT()>
-    ;
+tableValues() ::= <<
+  :externalKey
+, :accountId
+, :lastSysUpdateDate
+, :createdBy
+, :createdDate
+, :updatedBy
+, :updatedDate
 >>
 
-getBundleFromId() ::= <<
-    select <fields()>
-    from bundles
-    where
-      id = :id
-    <AND_CHECK_TENANT()>
-    ;
+updateBundleLastSysTime()  ::= <<
+update <tableName()>
+set
+last_sys_update_date = :lastSysUpdateDate
+, updated_by = :createdBy
+, updated_date = :createdDate
+where id = :id
+<AND_CHECK_TENANT()>
+;
 >>
 
 getBundlesForKey() ::= <<
-    select <fields()>
-    from bundles
-    where
-      external_key = :externalKey
-    <AND_CHECK_TENANT()>
-    ;
+select <allTableFields()>
+from bundles
+where
+external_key = :externalKey
+<AND_CHECK_TENANT()>
+;
 >>
 
 getBundleFromAccountAndKey() ::= <<
-    select <fields()>
-    from bundles
-    where
-      external_key = :externalKey AND account_id = :accountId
-    <AND_CHECK_TENANT()>
-    ;
+select <allTableFields()>
+from bundles
+where
+external_key = :externalKey
+and account_id = :accountId
+<AND_CHECK_TENANT()>
+;
 >>
 
 getBundleFromAccount() ::= <<
-    select <fields()>
-    from bundles
-    where
-      account_id = :accountId
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-getRecordId() ::= <<
-    SELECT record_id
-    FROM bundles
-    WHERE id = :id
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-auditFields(prefix) ::= <<
-    <prefix>table_name,
-    <prefix>record_id,
-    <prefix>change_type,
-    <prefix>change_date,
-    <prefix>changed_by,
-    <prefix>reason_code,
-    <prefix>comments,
-    <prefix>user_token,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
-
-insertAuditFromTransaction() ::= <<
-    INSERT INTO audit_log(<auditFields()>)
-    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken, :accountRecordId, :tenantRecordId);
+select <allTableFields()>
+from bundles
+where
+account_id = :accountId
+<AND_CHECK_TENANT()>
+;
 >>
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.sql.stg
index b8d63d3..e2bd516 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.sql.stg
@@ -1,147 +1,113 @@
-group EventSqlDao;
+group EventSqlDao: EntitySqlDao;
 
-CHECK_TENANT() ::= "tenant_record_id = :tenantRecordId"
-AND_CHECK_TENANT() ::= "AND <CHECK_TENANT()>"
+tableName() ::= "subscription_events"
 
-fields(prefix) ::= <<
-    <prefix>id,
-    <prefix>event_type,
-    <prefix>user_type,
-    <prefix>requested_date,
-    <prefix>effective_date,
-    <prefix>subscription_id,
-    <prefix>plan_name,
-    <prefix>phase_name,
-    <prefix>price_list_name,
-    <prefix>user_token,
-    <prefix>current_version,
-    <prefix>is_active,
-    <prefix>created_by,
-    <prefix>created_date,
-    <prefix>updated_by,
-    <prefix>updated_date,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
+andCheckSoftDeletionWithComma(prefix) ::= "and <prefix>is_active"
+
+extraTableFieldsWithComma(prefix) ::= <<
+, <prefix>record_id as total_ordering
 >>
 
-getEventById() ::= <<
-  select record_id, <fields()>
-  from subscription_events
-  where
-      id = :id
-  <AND_CHECK_TENANT()>
-  ;
+tableFields(prefix) ::= <<
+  <prefix> event_type
+, <prefix> user_type
+, <prefix> requested_date
+, <prefix> effective_date
+, <prefix> subscription_id
+, <prefix> plan_name
+, <prefix> phase_name
+, <prefix> price_list_name
+, <prefix> user_token
+, <prefix> current_version
+, <prefix> is_active
+, <prefix> created_by
+, <prefix> created_date
+, <prefix> updated_by
+, <prefix> updated_date
 >>
 
-insertEvent() ::= <<
-    insert into subscription_events (<fields()>)
-    values (
-    :id
-    , :eventType
-    , :userType
-    , :requestedDate
-    , :effectiveDate
-    , :subscriptionId
-    , :planName
-    , :phaseName
-    , :priceListName
-    , :userToken
-    , :currentVersion
-    , :isActive
-    , :userName
-    , :createdDate
-    , :userName
-    , :updatedDate
-    , :accountRecordId
-    , :tenantRecordId
-    );
+tableValues() ::= <<
+  :eventType
+, :userType
+, :requestedDate
+, :effectiveDate
+, :subscriptionId
+, :planName
+, :phaseName
+, :priceListName
+, :userToken
+, :currentVersion
+, :isActive
+, :createdBy
+, :createdDate
+, :updatedBy
+, :updatedDate
 >>
 
+
 updateVersion() ::= <<
-    update subscription_events
-    set
-      current_version = :currentVersion
-    where
-      id = :id
-    <AND_CHECK_TENANT()>
-    ;
+update <tableName()>
+set
+current_version = :currentVersion
+, updated_by = :createdBy
+, updated_date = :createdDate
+where
+id = :id
+<AND_CHECK_TENANT()>
+;
 >>
 
 unactiveEvent() ::= <<
-    update subscription_events
-    set
-      is_active = 0
-      , updated_by = :userName
-      , updated_date = :updatedDate
-    where
-      id = :id
-    <AND_CHECK_TENANT()>
-    ;
+update <tableName()>
+set
+is_active = 0
+, updated_by = :createdBy
+, updated_date = :createdDate
+where
+id = :id
+<AND_CHECK_TENANT()>
+;
 >>
 
 reactiveEvent() ::= <<
-    update subscription_events
-    set
-      is_active = 1
-      , updated_by = :userName
-      , updated_date = :updatedDate
-    where
-      event_id = :eventId
-    <AND_CHECK_TENANT()>
-    ;
+update <tableName()>
+set
+is_active = 1
+, updated_by = :createdBy
+, updated_date = :createdDate
+where
+event_id = :eventId
+<AND_CHECK_TENANT()>
+;
 >>
 
 
+
 getFutureActiveEventForSubscription() ::= <<
-    select record_id, <fields()>
-    from subscription_events
-    where
-      subscription_id = :subscriptionId
-      and is_active = 1
-      and effective_date > :now
-    <AND_CHECK_TENANT()>
-    order by
-      effective_date asc
-      , record_id asc
-    ;
+select <allTableFields()>
+, record_id as total_ordering
+from <tableName()>
+where
+subscription_id = :subscriptionId
+and is_active = 1
+and effective_date > :now
+<AND_CHECK_TENANT()>
+order by
+effective_date asc
+, record_id asc
+;
 >> 
 
 getEventsForSubscription() ::= <<
-    select record_id, <fields()>
-    from subscription_events
-    where
-      subscription_id = :subscriptionId
-    <AND_CHECK_TENANT()>
-    order by
-      effective_date asc
-      , record_id asc
-    ;      
->>
-
-getRecordId() ::= <<
-    SELECT record_id
-    FROM subscription_events
-    WHERE id = :id
-    <AND_CHECK_TENANT()>
-    ;
+select <allTableFields()>
+, record_id as total_ordering
+from <tableName()>
+where
+subscription_id = :subscriptionId
+<AND_CHECK_TENANT()>
+order by
+effective_date asc
+, record_id asc
+;
 >>
 
-auditFields(prefix) ::= <<
-    <prefix>table_name,
-    <prefix>record_id,
-    <prefix>change_type,
-    <prefix>change_date,
-    <prefix>changed_by,
-    <prefix>reason_code,
-    <prefix>comments,
-    <prefix>user_token,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
-
-insertAuditFromTransaction() ::= <<
-    INSERT INTO audit_log(<auditFields()>)
-    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken, :accountRecordId, :tenantRecordId);
->>
-
-
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
index e74145a..378afef 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
@@ -1,134 +1,75 @@
-group SubscriptionSqlDao;
+group SubscriptionSqlDao: EntitySqlDao;
 
-CHECK_TENANT() ::= "tenant_record_id = :tenantRecordId"
-AND_CHECK_TENANT() ::= "AND <CHECK_TENANT()>"
+tableName() ::= "subscriptions"
 
-insertSubscription() ::= <<
-    insert into subscriptions (
-        id
-      , bundle_id
-      , category
-      , start_date
-      , bundle_start_date
-      , active_version
-      , charged_through_date
-      , paid_through_date
-      , created_by
-      , created_date
-      , updated_by
-      , updated_date
-      , account_record_id
-      , tenant_record_id
-    ) values (
-        :id
-      , :bundleId
-      , :category
-      , :startDate
-      , :bundleStartDate
-      , :activeVersion
-      , :chargedThroughDate
-      , :paidThroughDate
-      , :userName
-      , :createdDate
-      , :userName
-      , :updatedDate
-      , :accountRecordId
-      , :tenantRecordId
-    );
+tableFields(prefix) ::= <<
+  <prefix>bundle_id
+, <prefix>category
+, <prefix>start_date
+, <prefix>bundle_start_date
+, <prefix>active_version
+, <prefix>charged_through_date
+, <prefix>paid_through_date
+, <prefix>created_by
+, <prefix>created_date
+, <prefix>updated_by
+, <prefix>updated_date
 >>
 
-getSubscriptionFromId() ::= <<
-    select
-        id
-      , bundle_id
-      , category
-      , start_date
-      , bundle_start_date
-      , active_version
-      , charged_through_date
-      , paid_through_date
-      , created_date
-      , updated_date
-    from subscriptions
-    where id = :id
-    <AND_CHECK_TENANT()>
-    ;
+tableValues() ::= <<
+  :bundleId
+, :category
+, :startDate
+, :bundleStartDate
+, :activeVersion
+, :chargedThroughDate
+, :paidThroughDate
+, :createdBy
+, :createdDate
+, :updatedBy
+, :updatedDate
 >>
 
+
 getSubscriptionsFromBundleId() ::= <<
-    select
-      id
-      , bundle_id
-      , category
-      , start_date
-      , bundle_start_date
-      , active_version
-      , charged_through_date
-      , paid_through_date
-      , created_date
-      , updated_date
-    from subscriptions
-    where bundle_id = :bundleId
-    <AND_CHECK_TENANT()>
-    ;
+select
+<allTableFields()>
+from <tableName()>
+where bundle_id = :bundleId
+<AND_CHECK_TENANT()>
+;
 >>
 
 updateChargedThroughDate() ::= <<
-    update subscriptions
-    set
-      charged_through_date = :chargedThroughDate
-      , updated_by = :userName
-      , updated_date = :updatedDate
-    where id = :id
-    <AND_CHECK_TENANT()>
-    ;
+update <tableName()>
+set
+charged_through_date = :chargedThroughDate
+, updated_by = :createdBy
+, updated_date = :createdDate
+where id = :id
+<AND_CHECK_TENANT()>
+;
 >>
 
 updateActiveVersion() ::= <<
-    update subscriptions
-    set
-      active_version = :activeVersion
-      , updated_by = :userName
-      , updated_date = :updatedDate
-    where id = :id
-    ;
+update <tableName()>
+set
+active_version = :activeVersion
+, updated_by = :createdBy
+, updated_date = :createdDate
+where id = :id
+;
 >>
 
 updateForRepair() ::= <<
-    update subscriptions
-    set
-      active_version = :activeVersion
-      , start_date = :startDate
-      , bundle_start_date = :bundleStartDate
-      , updated_by = :userName
-      , updated_date = :updatedDate
-    where id = :id
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-getRecordId() ::= <<
-    SELECT record_id
-    FROM subscriptions
-    WHERE id = :id
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-auditFields(prefix) ::= <<
-    <prefix>table_name,
-    <prefix>record_id,
-    <prefix>change_type,
-    <prefix>change_date,
-    <prefix>changed_by,
-    <prefix>reason_code,
-    <prefix>comments,
-    <prefix>user_token,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
-
-insertAuditFromTransaction() ::= <<
-    INSERT INTO audit_log(<auditFields()>)
-    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken, :accountRecordId, :tenantRecordId);
+update <tableName()>
+set
+active_version = :activeVersion
+, start_date = :startDate
+, bundle_start_date = :bundleStartDate
+, updated_by = :createdBy
+, updated_date = :createdDate
+where id = :id
+<AND_CHECK_TENANT()>
+;
 >>
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestTransfer.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestTransfer.java
index f36d904..ec61b63 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestTransfer.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestTransfer.java
@@ -104,7 +104,7 @@ public class TestTransfer extends TestApiBase {
 
             testListener.pushExpectedEvent(NextEvent.TRANSFER);
             testListener.pushExpectedEvent(NextEvent.CANCEL);
-            transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, true, callContext);
+            transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getExternalKey(), transferRequestedDate, false, true, callContext);
             assertTrue(testListener.isCompleted(3000));
 
             final Subscription oldBaseSubscription = entitlementApi.getBaseSubscription(bundle.getId(), callContext);
@@ -142,7 +142,7 @@ public class TestTransfer extends TestApiBase {
 
         testListener.pushExpectedEvent(NextEvent.TRANSFER);
         testListener.pushExpectedEvent(NextEvent.CANCEL);
-        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, false, callContext);
+        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getExternalKey(), transferRequestedDate, false, false, callContext);
         assertTrue(testListener.isCompleted(3000));
         final DateTime afterTransferDate = clock.getUTCNow();
 
@@ -153,7 +153,7 @@ public class TestTransfer extends TestApiBase {
         assertTrue(oldBaseSubscription.getEndDate().compareTo(transferRequestedDate) == 0);
 
         // CHECK NEW BUNDLE EXIST, WITH ONE SUBSCRIPTION STARTING ON TRANSFER_DATE
-        final SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getKey(), callContext);
+        final SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getExternalKey(), callContext);
 
         final List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(newBundle.getId(), callContext);
         assertEquals(subscriptions.size(), 1);
@@ -193,7 +193,7 @@ public class TestTransfer extends TestApiBase {
 
         testListener.pushExpectedEvent(NextEvent.TRANSFER);
         final DateTime transferRequestedDate = clock.getUTCNow();
-        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, false, callContext);
+        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getExternalKey(), transferRequestedDate, false, false, callContext);
         assertTrue(testListener.isCompleted(3000));
 
         // CHECK OLD BASE IS CANCEL AT THE TRANSFER DATE
@@ -202,7 +202,7 @@ public class TestTransfer extends TestApiBase {
         assertTrue(oldBaseSubscription.getFutureEndDate().compareTo(ctd) == 0);
 
         // CHECK NEW BUNDLE EXIST, WITH ONE SUBSCRIPTION STARTING ON TRANSFER_DATE
-        final SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getKey(), callContext);
+        final SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getExternalKey(), callContext);
 
         final List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(newBundle.getId(), callContext);
         assertEquals(subscriptions.size(), 1);
@@ -241,7 +241,7 @@ public class TestTransfer extends TestApiBase {
         final DateTime transferRequestedDate = clock.getUTCNow();
         testListener.pushExpectedEvent(NextEvent.TRANSFER);
         testListener.pushExpectedEvent(NextEvent.CANCEL);
-        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, false, callContext);
+        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getExternalKey(), transferRequestedDate, false, false, callContext);
         assertTrue(testListener.isCompleted(3000));
         final DateTime afterTransferDate = clock.getUTCNow();
 
@@ -252,7 +252,7 @@ public class TestTransfer extends TestApiBase {
         assertTrue(oldBaseSubscription.getEndDate().compareTo(transferRequestedDate) == 0);
 
         // CHECK NEW BUNDLE EXIST, WITH ONE SUBSCRIPTION STARTING ON TRANSFER_DATE
-        final SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getKey(), callContext);
+        final SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getExternalKey(), callContext);
 
         final List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(newBundle.getId(), callContext);
         assertEquals(subscriptions.size(), 1);
@@ -292,7 +292,7 @@ public class TestTransfer extends TestApiBase {
 
         final DateTime transferRequestedDate = clock.getUTCNow();
         testListener.pushExpectedEvent(NextEvent.TRANSFER);
-        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, false, callContext);
+        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getExternalKey(), transferRequestedDate, false, false, callContext);
         assertTrue(testListener.isCompleted(3000));
 
         // CHECK OLD BASE IS CANCEL AT THE TRANSFER DATE
@@ -301,7 +301,7 @@ public class TestTransfer extends TestApiBase {
         assertTrue(oldBaseSubscription.getFutureEndDate().compareTo(ctd) == 0);
 
         // CHECK NEW BUNDLE EXIST, WITH ONE SUBSCRIPTION STARTING ON TRANSFER_DATE
-        final SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getKey(), callContext);
+        final SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getExternalKey(), callContext);
 
         final List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(newBundle.getId(), callContext);
         assertEquals(subscriptions.size(), 1);
@@ -387,11 +387,11 @@ public class TestTransfer extends TestApiBase {
 
         final DateTime transferRequestedDate = clock.getUTCNow();
         testListener.pushExpectedEvent(NextEvent.TRANSFER);
-        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, true, false, callContext);
+        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getExternalKey(), transferRequestedDate, true, false, callContext);
         assertTrue(testListener.isCompleted(3000));
 
         // RETRIEVE NEW BUNDLE AND CHECK SUBSCRIPTIONS
-        final SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getKey(), callContext);
+        final SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getExternalKey(), callContext);
         final List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(newBundle.getId(), callContext);
         assertEquals(subscriptions.size(), 3);
         boolean foundBP = false;
@@ -432,7 +432,7 @@ public class TestTransfer extends TestApiBase {
         final UUID finalNewAccountId = UUID.randomUUID();
         final DateTime newTransferRequestedDate = clock.getUTCNow();
         testListener.pushExpectedEvent(NextEvent.TRANSFER);
-        transferApi.transferBundle(newBundle.getAccountId(), finalNewAccountId, newBundle.getKey(), newTransferRequestedDate, true, false, callContext);
+        transferApi.transferBundle(newBundle.getAccountId(), finalNewAccountId, newBundle.getExternalKey(), newTransferRequestedDate, true, false, callContext);
         assertTrue(testListener.isCompleted(3000));
 
     }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
index c6a4362..1925799 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
@@ -47,9 +47,6 @@ public abstract class TestUserApiCreate extends TestApiBase {
 
     private static final Logger log = LoggerFactory.getLogger(TestUserApiCreate.class);
 
-    private final InternalTenantContext internalTenantContext = Mockito.mock(InternalTenantContext.class);
-    private final TenantContext tenantContext = Mockito.mock(TenantContext.class);
-
     public void testCreateWithRequestedDate() {
         try {
             final DateTime init = clock.getUTCNow();
@@ -150,7 +147,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
             assertEquals(currentPhase.getPhaseType(), PhaseType.TRIAL);
             assertTrue(testListener.isCompleted(5000));
 
-            final List<EntitlementEvent> events = dao.getPendingEventsForSubscription(subscription.getId(), internalTenantContext);
+            final List<EntitlementEvent> events = dao.getPendingEventsForSubscription(subscription.getId(), internalCallContext);
             assertNotNull(events);
             printEvents(events);
             assertTrue(events.size() == 1);
@@ -207,7 +204,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
             clock.addDeltaFromReality(it.toDurationMillis());
             assertTrue(testListener.isCompleted(5000));
 
-            subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId(), tenantContext);
+            subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId(), callContext);
             currentPhase = subscription.getCurrentPhase();
             assertNotNull(currentPhase);
             assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
index 2567e37..453d64d 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
@@ -27,7 +27,6 @@ import java.util.TreeSet;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -54,6 +53,8 @@ import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.notificationq.NotificationKey;
 import com.ning.billing.util.notificationq.NotificationQueue;
 import com.ning.billing.util.notificationq.NotificationQueueService;
@@ -106,7 +107,7 @@ public class MockEntitlementDaoMemory implements EntitlementDao {
     public List<SubscriptionBundle> getSubscriptionBundlesForKey(final String bundleKey, final InternalTenantContext context) {
         final List<SubscriptionBundle> results = new ArrayList<SubscriptionBundle>();
         for (final SubscriptionBundle cur : bundles) {
-            if (cur.getKey().equals(bundleKey)) {
+            if (cur.getExternalKey().equals(bundleKey)) {
                 results.add(cur);
             }
         }
@@ -126,7 +127,7 @@ public class MockEntitlementDaoMemory implements EntitlementDao {
     @Override
     public SubscriptionBundle getSubscriptionBundleFromAccountAndKey(final UUID accountId, final String bundleKey, final InternalTenantContext context) {
         for (final SubscriptionBundle cur : bundles) {
-            if (cur.getKey().equals(bundleKey) && cur.getAccountId().equals(accountId)) {
+            if (cur.getExternalKey().equals(bundleKey) && cur.getAccountId().equals(accountId)) {
                 return cur;
             }
         }
@@ -158,7 +159,7 @@ public class MockEntitlementDaoMemory implements EntitlementDao {
     public List<Subscription> getSubscriptionsForAccountAndKey(final SubscriptionFactory factory, final UUID accountId, final String bundleKey, final InternalTenantContext context) {
 
         for (final SubscriptionBundle cur : bundles) {
-            if (cur.getKey().equals(bundleKey) && cur.getAccountId().equals(bundleKey)) {
+            if (cur.getExternalKey().equals(bundleKey) && cur.getAccountId().equals(bundleKey)) {
                 return getSubscriptions(factory, cur.getId(), context);
             }
         }
@@ -406,7 +407,7 @@ public class MockEntitlementDaoMemory implements EntitlementDao {
         return null;
     }
 
-    private void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao, final DateTime effectiveDate,
+    private void recordFutureNotificationFromTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> transactionalDao, final DateTime effectiveDate,
                                                          final NotificationKey notificationKey, final InternalCallContext context) {
         try {
             final NotificationQueue subscriptionEventQueue = notificationQueueService.getNotificationQueue(Engine.ENTITLEMENT_SERVICE_NAME,
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java
index 048ab21..228e23f 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java
@@ -26,7 +26,7 @@ import com.ning.billing.util.notificationq.NotificationQueueService;
 
 import com.google.inject.Inject;
 
-public class MockEntitlementDaoSql extends AuditedEntitlementDao {
+public class MockEntitlementDaoSql extends DefaultEntitlementDao {
     @Inject
     public MockEntitlementDaoSql(final IDBI dbi, final Clock clock, final AddonUtils addonUtils, final NotificationQueueService notificationQueueService,
                                  final InternalBus eventBus, final CatalogService catalogService) {
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/DefaultInvoiceService.java b/invoice/src/main/java/com/ning/billing/invoice/api/DefaultInvoiceService.java
index 5d31d58..bf0734a 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/DefaultInvoiceService.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/DefaultInvoiceService.java
@@ -21,9 +21,9 @@ import com.ning.billing.invoice.TagHandler;
 import com.ning.billing.invoice.notification.NextBillingDateNotifier;
 import com.ning.billing.lifecycle.LifecycleHandlerType;
 import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel;
-import com.ning.billing.util.svcsapi.bus.InternalBus;
 import com.ning.billing.util.notificationq.NotificationQueueService.NoSuchNotificationQueue;
 import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueAlreadyExists;
+import com.ning.billing.util.svcsapi.bus.InternalBus;
 
 import com.google.inject.Inject;
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java
index c51f344..89bec31 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java
@@ -19,28 +19,28 @@ package com.ning.billing.invoice.api.invoice;
 
 import java.math.BigDecimal;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
-import org.joda.time.DateTime;
 import org.skife.jdbi.v2.exceptions.TransactionFailedException;
 
-import com.ning.billing.ErrorCode;
-import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.invoice.api.InvoicePayment.InvoicePaymentType;
 import com.ning.billing.invoice.api.InvoicePaymentApi;
 import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.invoice.dao.InvoiceModelDao;
+import com.ning.billing.invoice.dao.InvoicePaymentModelDao;
+import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.invoice.model.DefaultInvoicePayment;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
-import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.callcontext.TenantContext;
 
+import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
 public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
@@ -58,34 +58,45 @@ public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
 
     @Override
     public List<Invoice> getAllInvoicesByAccount(final UUID accountId, final TenantContext context) {
-        return dao.getAllInvoicesByAccount(accountId, internalCallContextFactory.createInternalTenantContext(context));
+        return ImmutableList.<Invoice>copyOf(Collections2.transform(dao.getAllInvoicesByAccount(accountId, internalCallContextFactory.createInternalTenantContext(context)),
+                                                                    new Function<InvoiceModelDao, Invoice>() {
+                                                                        @Override
+                                                                        public Invoice apply(final InvoiceModelDao input) {
+                                                                            return new DefaultInvoice(input);
+                                                                        }
+                                                                    }));
     }
 
     @Override
     public Invoice getInvoice(final UUID invoiceId, final TenantContext context) throws InvoiceApiException {
-        return dao.getById(invoiceId, internalCallContextFactory.createInternalTenantContext(context));
+        return new DefaultInvoice(dao.getById(invoiceId, internalCallContextFactory.createInternalTenantContext(context)));
     }
 
     @Override
     public List<InvoicePayment> getInvoicePayments(final UUID paymentId, final TenantContext context) {
-        return dao.getInvoicePayments(paymentId, internalCallContextFactory.createInternalTenantContext(context));
+        return ImmutableList.<InvoicePayment>copyOf(Collections2.transform(dao.getInvoicePayments(paymentId, internalCallContextFactory.createInternalTenantContext(context)),
+                                                                           new Function<InvoicePaymentModelDao, InvoicePayment>() {
+                                                                               @Override
+                                                                               public InvoicePayment apply(final InvoicePaymentModelDao input) {
+                                                                                   return new DefaultInvoicePayment(input);
+                                                                               }
+                                                                           }));
     }
 
     @Override
     public InvoicePayment getInvoicePaymentForAttempt(final UUID paymentId, final TenantContext context) {
-        final List<InvoicePayment> invoicePayments = dao.getInvoicePayments(paymentId, internalCallContextFactory.createInternalTenantContext(context));
+        final List<InvoicePayment> invoicePayments = getInvoicePayments(paymentId, context);
         if (invoicePayments.size() == 0) {
             return null;
         }
         return Collections2.filter(invoicePayments, new Predicate<InvoicePayment>() {
             @Override
-            public boolean apply(InvoicePayment input) {
+            public boolean apply(final InvoicePayment input) {
                 return input.getType() == InvoicePaymentType.ATTEMPT;
             }
         }).iterator().next();
     }
 
-
     @Override
     public BigDecimal getRemainingAmountPaid(final UUID invoicePaymentId, final TenantContext context) {
         return dao.getRemainingAmountPaid(invoicePaymentId, internalCallContextFactory.createInternalTenantContext(context));
@@ -93,17 +104,29 @@ public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
 
     @Override
     public List<InvoicePayment> getChargebacksByAccountId(final UUID accountId, final TenantContext context) {
-        return dao.getChargebacksByAccountId(accountId, internalCallContextFactory.createInternalTenantContext(context));
+        return ImmutableList.<InvoicePayment>copyOf(Collections2.transform(dao.getChargebacksByAccountId(accountId, internalCallContextFactory.createInternalTenantContext(context)),
+                                                                           new Function<InvoicePaymentModelDao, InvoicePayment>() {
+                                                                               @Override
+                                                                               public InvoicePayment apply(final InvoicePaymentModelDao input) {
+                                                                                   return new DefaultInvoicePayment(input);
+                                                                               }
+                                                                           }));
     }
 
     @Override
     public List<InvoicePayment> getChargebacksByPaymentId(final UUID paymentId, final TenantContext context) {
-        return dao.getChargebacksByPaymentId(paymentId, internalCallContextFactory.createInternalTenantContext(context));
+        return ImmutableList.<InvoicePayment>copyOf(Collections2.transform(dao.getChargebacksByPaymentId(paymentId, internalCallContextFactory.createInternalTenantContext(context)),
+                                                                           new Function<InvoicePaymentModelDao, InvoicePayment>() {
+                                                                               @Override
+                                                                               public InvoicePayment apply(final InvoicePaymentModelDao input) {
+                                                                                   return new DefaultInvoicePayment(input);
+                                                                               }
+                                                                           }));
     }
 
     @Override
     public InvoicePayment getChargebackById(final UUID chargebackId, final TenantContext context) throws InvoiceApiException {
-        return dao.getChargebackById(chargebackId, internalCallContextFactory.createInternalTenantContext(context));
+        return new DefaultInvoicePayment(dao.getChargebackById(chargebackId, internalCallContextFactory.createInternalTenantContext(context)));
     }
 
     @Override
@@ -111,7 +134,6 @@ public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
         return dao.getAccountIdFromInvoicePaymentId(invoicePaymentId, internalCallContextFactory.createInternalTenantContext(context));
     }
 
-
     @Override
     public InvoicePayment createChargeback(final UUID invoicePaymentId, final CallContext context) throws InvoiceApiException {
         return createChargeback(invoicePaymentId, null, context);
@@ -125,7 +147,7 @@ public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
             public InvoicePayment doHandle() throws InvoiceApiException {
                 // Retrieve the account id for the internal call context
                 final UUID accountId = dao.getAccountIdFromInvoicePaymentId(invoicePaymentId, internalCallContextFactory.createInternalTenantContext(context));
-                return dao.postChargeback(invoicePaymentId, amount, internalCallContextFactory.createInternalCallContext(accountId, context));
+                return new DefaultInvoicePayment(dao.postChargeback(invoicePaymentId, amount, internalCallContextFactory.createInternalCallContext(accountId, context)));
             }
         });
     }
@@ -134,11 +156,13 @@ public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
     // Allow to safely catch TransactionFailedException exceptions and rethrow the correct InvoiceApiException exception
     //
     private interface WithInvoiceApiExceptionCallback<T> {
+
         public T doHandle() throws InvoiceApiException;
     }
 
     private static final class WithInvoiceApiException<T> {
-        public T executeAndThrow(final WithInvoiceApiExceptionCallback<T> callback) throws InvoiceApiException  {
+
+        public T executeAndThrow(final WithInvoiceApiExceptionCallback<T> callback) throws InvoiceApiException {
 
             try {
                 return callback.doHandle();
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java
index be2e650..e613e9d 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java
@@ -23,32 +23,35 @@ import org.joda.time.LocalDate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.catalog.api.MigrationPlan;
+import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.invoice.api.InvoiceMigrationApi;
-import com.ning.billing.invoice.dao.AuditedInvoiceDao;
-import com.ning.billing.invoice.model.MigrationInvoiceItem;
+import com.ning.billing.invoice.dao.DefaultInvoiceDao;
+import com.ning.billing.invoice.dao.InvoiceItemModelDao;
+import com.ning.billing.invoice.dao.InvoiceModelDao;
+import com.ning.billing.invoice.dao.InvoicePaymentModelDao;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.svcapi.account.AccountInternalApi;
 
+import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
 public class DefaultInvoiceMigrationApi implements InvoiceMigrationApi {
+
     private static final Logger log = LoggerFactory.getLogger(DefaultInvoiceMigrationApi.class);
 
     private final AccountInternalApi accountUserApi;
-    private final AuditedInvoiceDao dao;
+    private final DefaultInvoiceDao dao;
     private final Clock clock;
     private final InternalCallContextFactory internalCallContextFactory;
 
     @Inject
     public DefaultInvoiceMigrationApi(final AccountInternalApi accountUserApi,
-                                      final AuditedInvoiceDao dao,
+                                      final DefaultInvoiceDao dao,
                                       final Clock clock,
                                       final InternalCallContextFactory internalCallContextFactory) {
         this.accountUserApi = accountUserApi;
@@ -59,19 +62,20 @@ public class DefaultInvoiceMigrationApi implements InvoiceMigrationApi {
 
     @Override
     public UUID createMigrationInvoice(final UUID accountId, final LocalDate targetDate, final BigDecimal balance, final Currency currency, final CallContext context) {
-        final Account account;
         try {
-            account = accountUserApi.getAccountById(accountId, internalCallContextFactory.createInternalTenantContext(context));
+            accountUserApi.getAccountById(accountId, internalCallContextFactory.createInternalTenantContext(context));
         } catch (AccountApiException e) {
             log.warn("Unable to find account for id {}", accountId);
             return null;
         }
 
-        final Invoice migrationInvoice = new MigrationInvoice(accountId, clock.getUTCToday(), targetDate, currency);
-        final InvoiceItem migrationInvoiceItem = new MigrationInvoiceItem(migrationInvoice.getId(), accountId, targetDate, balance, currency);
-        migrationInvoice.addInvoiceItem(migrationInvoiceItem);
+        final InvoiceModelDao migrationInvoice = new InvoiceModelDao(accountId, clock.getUTCToday(), targetDate, currency, true);
+        final InvoiceItemModelDao migrationInvoiceItem = new InvoiceItemModelDao(context.getCreatedDate(), InvoiceItemType.FIXED, migrationInvoice.getId(), accountId, null, null,
+                                                                                 MigrationPlan.MIGRATION_PLAN_NAME, MigrationPlan.MIGRATION_PLAN_PHASE_NAME,
+                                                                                 targetDate, null, balance, null, currency, null);
+        dao.createInvoice(migrationInvoice, ImmutableList.<InvoiceItemModelDao>of(migrationInvoiceItem),
+                          ImmutableList.<InvoicePaymentModelDao>of(), true, internalCallContextFactory.createInternalCallContext(accountId, context));
 
-        dao.create(migrationInvoice, account.getBillCycleDay().getDayOfMonthUTC(), true, internalCallContextFactory.createInternalCallContext(accountId, context));
         return migrationInvoice.getId();
     }
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/migration/MigrationInvoice.java b/invoice/src/main/java/com/ning/billing/invoice/api/migration/MigrationInvoice.java
index 3534e6d..b0131da 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/migration/MigrationInvoice.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/migration/MigrationInvoice.java
@@ -24,6 +24,7 @@ import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.model.DefaultInvoice;
 
 public class MigrationInvoice extends DefaultInvoice {
+
     public MigrationInvoice(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency) {
         super(UUID.randomUUID(), accountId, null, invoiceDate, targetDate, currency, true);
     }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
index ace0d51..152a31a 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
@@ -18,7 +18,6 @@ package com.ning.billing.invoice.api.svcs;
 
 import java.math.BigDecimal;
 import java.util.Collection;
-import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -35,11 +34,15 @@ import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.invoice.api.InvoicePayment.InvoicePaymentType;
 import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.invoice.dao.InvoiceModelDao;
+import com.ning.billing.invoice.dao.InvoicePaymentModelDao;
+import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.invoice.model.DefaultInvoicePayment;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.svcapi.invoice.InvoiceInternalApi;
 
+import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 
@@ -56,17 +59,27 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
 
     @Override
     public Invoice getInvoiceById(final UUID invoiceId, final InternalTenantContext context) throws InvoiceApiException {
-        return dao.getById(invoiceId, context);
+        return new DefaultInvoice(dao.getById(invoiceId, context));
     }
 
     @Override
     public Collection<Invoice> getUnpaidInvoicesByAccountId(final UUID accountId, final LocalDate upToDate, final InternalTenantContext context) {
-        return dao.getUnpaidInvoicesByAccountId(accountId, upToDate, context);
+        return Collections2.transform(dao.getUnpaidInvoicesByAccountId(accountId, upToDate, context), new Function<InvoiceModelDao, Invoice>() {
+            @Override
+            public Invoice apply(final InvoiceModelDao input) {
+                return new DefaultInvoice(input);
+            }
+        });
     }
 
     @Override
     public Collection<Invoice> getInvoicesByAccountId(final UUID accountId, final InternalTenantContext context) {
-        return dao.getInvoicesByAccount(accountId, context);
+        return Collections2.transform(dao.getInvoicesByAccount(accountId, context), new Function<InvoiceModelDao, Invoice>() {
+            @Override
+            public Invoice apply(final InvoiceModelDao input) {
+                return new DefaultInvoice(input);
+            }
+        });
     }
 
     @Override
@@ -77,23 +90,28 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
     @Override
     public void notifyOfPayment(final UUID invoiceId, final BigDecimal amount, final Currency currency, final UUID paymentId, final DateTime paymentDate, final InternalCallContext context) throws InvoiceApiException {
         final InvoicePayment invoicePayment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoiceId, paymentDate, amount, currency);
-        dao.notifyOfPayment(invoicePayment, context);
+        notifyOfPayment(invoicePayment, context);
     }
 
     @Override
     public void notifyOfPayment(final InvoicePayment invoicePayment, final InternalCallContext context) throws InvoiceApiException {
-        dao.notifyOfPayment(invoicePayment, context);
+        dao.notifyOfPayment(new InvoicePaymentModelDao(invoicePayment), context);
     }
 
     @Override
     public InvoicePayment getInvoicePaymentForAttempt(final UUID paymentId, final InternalTenantContext context) throws InvoiceApiException {
-        final List<InvoicePayment> invoicePayments = dao.getInvoicePayments(paymentId, context);
+        final Collection<InvoicePayment> invoicePayments = Collections2.transform(dao.getInvoicePayments(paymentId, context), new Function<InvoicePaymentModelDao, InvoicePayment>() {
+            @Override
+            public InvoicePayment apply(final InvoicePaymentModelDao input) {
+                return new DefaultInvoicePayment(input);
+            }
+        });
         if (invoicePayments.size() == 0) {
             return null;
         }
         return Collections2.filter(invoicePayments, new Predicate<InvoicePayment>() {
             @Override
-            public boolean apply(InvoicePayment input) {
+            public boolean apply(final InvoicePayment input) {
                 return input.getType() == InvoicePaymentType.ATTEMPT;
             }
         }).iterator().next();
@@ -102,7 +120,7 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
     @Override
     public Invoice getInvoiceForPaymentId(final UUID paymentId, final InternalTenantContext context) throws InvoiceApiException {
         final UUID invoiceIdStr = dao.getInvoiceIdByPaymentId(paymentId, context);
-        return invoiceIdStr == null ? null : dao.getById(invoiceIdStr, context);
+        return invoiceIdStr == null ? null : new DefaultInvoice(dao.getById(invoiceIdStr, context));
     }
 
     @Override
@@ -115,10 +133,14 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
                     throw new InvoiceApiException(ErrorCode.PAYMENT_REFUND_AMOUNT_NEGATIVE_OR_NULL);
                 }
 
-                final List<InvoicePayment> invoicePayments = dao.getInvoicePayments(paymentId, context);
-                final UUID accountId = dao.getAccountIdFromInvoicePaymentId(invoicePayments.get(0).getId(), context);
+                final Collection<InvoicePayment> invoicePayments = Collections2.transform(dao.getInvoicePayments(paymentId, context), new Function<InvoicePaymentModelDao, InvoicePayment>() {
+                    @Override
+                    public InvoicePayment apply(final InvoicePaymentModelDao input) {
+                        return new DefaultInvoicePayment(input);
+                    }
+                });
 
-                return dao.createRefund(paymentId, amount, isInvoiceAdjusted, invoiceItemIdsWithAmounts, paymentCookieId, context);
+                return new DefaultInvoicePayment(dao.createRefund(paymentId, amount, isInvoiceAdjusted, invoiceItemIdsWithAmounts, paymentCookieId, context));
             }
         });
     }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceAdjustmentEvent.java b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceAdjustmentEvent.java
index 614431e..db8076b 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceAdjustmentEvent.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceAdjustmentEvent.java
@@ -57,7 +57,6 @@ public class DefaultInvoiceAdjustmentEvent extends DefaultBusInternalEvent imple
         return BusInternalEventType.INVOICE_ADJUSTMENT;
     }
 
-
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceCreationEvent.java b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceCreationEvent.java
index 6c4d2c8..ba28002 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceCreationEvent.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceCreationEvent.java
@@ -55,7 +55,6 @@ public class DefaultInvoiceCreationEvent extends DefaultBusInternalEvent impleme
         return BusInternalEventType.INVOICE_CREATION;
     }
 
-
     @Override
     public UUID getInvoiceId() {
         return invoiceId;
@@ -83,15 +82,27 @@ public class DefaultInvoiceCreationEvent extends DefaultBusInternalEvent impleme
 
     @Override
     public boolean equals(final Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
 
         final DefaultInvoiceCreationEvent that = (DefaultInvoiceCreationEvent) o;
 
-        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) return false;
-        if (amountOwed != null ? !amountOwed.equals(that.amountOwed) : that.amountOwed != null) return false;
-        if (currency != that.currency) return false;
-        if (invoiceId != null ? !invoiceId.equals(that.invoiceId) : that.invoiceId != null) return false;
+        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
+            return false;
+        }
+        if (amountOwed != null ? !amountOwed.equals(that.amountOwed) : that.amountOwed != null) {
+            return false;
+        }
+        if (currency != that.currency) {
+            return false;
+        }
+        if (invoiceId != null ? !invoiceId.equals(that.invoiceId) : that.invoiceId != null) {
+            return false;
+        }
         return true;
     }
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
index 3ea248f..fa5da08 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -19,7 +19,6 @@ package com.ning.billing.invoice.api.user;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
@@ -38,11 +37,14 @@ import com.ning.billing.invoice.InvoiceDispatcher;
 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.InvoiceUserApi;
 import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.invoice.dao.InvoiceItemModelDao;
+import com.ning.billing.invoice.dao.InvoiceModelDao;
 import com.ning.billing.invoice.model.CreditAdjInvoiceItem;
+import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.invoice.model.ExternalChargeInvoiceItem;
+import com.ning.billing.invoice.model.InvoiceItemFactory;
 import com.ning.billing.invoice.template.HtmlInvoiceGenerator;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.callcontext.CallContext;
@@ -57,6 +59,9 @@ import com.ning.billing.util.svcsapi.bus.InternalBus.EventBusException;
 import com.ning.billing.util.tag.ControlTagType;
 import com.ning.billing.util.tag.Tag;
 
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
 public class DefaultInvoiceUserApi implements InvoiceUserApi {
@@ -85,12 +90,24 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
 
     @Override
     public List<Invoice> getInvoicesByAccount(final UUID accountId, final TenantContext context) {
-        return dao.getInvoicesByAccount(accountId, internalCallContextFactory.createInternalTenantContext(context));
+        return ImmutableList.<Invoice>copyOf(Collections2.transform(dao.getInvoicesByAccount(accountId, internalCallContextFactory.createInternalTenantContext(context)),
+                                                                    new Function<InvoiceModelDao, Invoice>() {
+                                                                        @Override
+                                                                        public Invoice apply(final InvoiceModelDao input) {
+                                                                            return new DefaultInvoice(input);
+                                                                        }
+                                                                    }));
     }
 
     @Override
     public List<Invoice> getInvoicesByAccount(final UUID accountId, final LocalDate fromDate, final TenantContext context) {
-        return dao.getInvoicesByAccount(accountId, fromDate, internalCallContextFactory.createInternalTenantContext(context));
+        return ImmutableList.<Invoice>copyOf(Collections2.transform(dao.getInvoicesByAccount(accountId, fromDate, internalCallContextFactory.createInternalTenantContext(context)),
+                                                                    new Function<InvoiceModelDao, Invoice>() {
+                                                                        @Override
+                                                                        public Invoice apply(final InvoiceModelDao input) {
+                                                                            return new DefaultInvoice(input);
+                                                                        }
+                                                                    }));
     }
 
     @Override
@@ -107,17 +124,23 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
 
     @Override
     public Invoice getInvoice(final UUID invoiceId, final TenantContext context) throws InvoiceApiException {
-        return dao.getById(invoiceId, internalCallContextFactory.createInternalTenantContext(context));
+        return new DefaultInvoice(dao.getById(invoiceId, internalCallContextFactory.createInternalTenantContext(context)));
     }
 
     @Override
     public Invoice getInvoiceByNumber(final Integer number, final TenantContext context) throws InvoiceApiException {
-        return dao.getByNumber(number, internalCallContextFactory.createInternalTenantContext(context));
+        return new DefaultInvoice(dao.getByNumber(number, internalCallContextFactory.createInternalTenantContext(context)));
     }
 
     @Override
     public List<Invoice> getUnpaidInvoicesByAccountId(final UUID accountId, final LocalDate upToDate, final TenantContext context) {
-        return dao.getUnpaidInvoicesByAccountId(accountId, upToDate, internalCallContextFactory.createInternalTenantContext(context));
+        return ImmutableList.<Invoice>copyOf(Collections2.transform(dao.getUnpaidInvoicesByAccountId(accountId, upToDate, internalCallContextFactory.createInternalTenantContext(context)),
+                                                                    new Function<InvoiceModelDao, Invoice>() {
+                                                                        @Override
+                                                                        public Invoice apply(final InvoiceModelDao input) {
+                                                                            return new DefaultInvoice(input);
+                                                                        }
+                                                                    }));
     }
 
     @Override
@@ -149,9 +172,9 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
         tagApi.addTag(invoiceId, ObjectType.INVOICE, ControlTagType.WRITTEN_OFF.getId(), internalContext);
 
         // Retrieve the invoice for the account id
-        final Invoice invoice = dao.getById(invoiceId, internalContext);
+        final Invoice invoice = new DefaultInvoice(dao.getById(invoiceId, internalContext));
         // This is for overdue
-        notifyBusOfInvoiceAdjustment(invoiceId, invoice.getAccountId(), context.getUserToken(), internalCallContextFactory.createInternalCallContext(invoice.getAccountId(), context));
+        notifyBusOfInvoiceAdjustment(invoiceId, invoice.getAccountId(), internalCallContextFactory.createInternalCallContext(invoice.getAccountId(), context));
     }
 
     @Override
@@ -161,14 +184,14 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
         tagApi.removeTag(invoiceId, ObjectType.INVOICE, ControlTagType.WRITTEN_OFF.getId(), internalContext);
 
         // Retrieve the invoice for the account id
-        final Invoice invoice = dao.getById(invoiceId, internalContext);
+        final Invoice invoice = new DefaultInvoice(dao.getById(invoiceId, internalContext));
         // This is for overdue
-        notifyBusOfInvoiceAdjustment(invoiceId, invoice.getAccountId(), context.getUserToken(), internalCallContextFactory.createInternalCallContext(invoice.getAccountId(), context));
+        notifyBusOfInvoiceAdjustment(invoiceId, invoice.getAccountId(), internalCallContextFactory.createInternalCallContext(invoice.getAccountId(), context));
     }
 
     @Override
     public InvoiceItem getExternalChargeById(final UUID externalChargeId, final TenantContext context) throws InvoiceApiException {
-        final InvoiceItem externalChargeItem = dao.getExternalChargeById(externalChargeId, internalCallContextFactory.createInternalTenantContext(context));
+        final InvoiceItem externalChargeItem = InvoiceItemFactory.fromModelDao(dao.getExternalChargeById(externalChargeId, internalCallContextFactory.createInternalTenantContext(context)));
         if (externalChargeItem == null) {
             throw new InvoiceApiException(ErrorCode.INVOICE_NO_SUCH_EXTERNAL_CHARGE, externalChargeId);
         }
@@ -204,12 +227,13 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
             throw new InvoiceApiException(ErrorCode.EXTERNAL_CHARGE_AMOUNT_INVALID, amount);
         }
 
-        return dao.insertExternalCharge(accountId, invoiceId, bundleId, description, amount, effectiveDate, currency, internalCallContextFactory.createInternalCallContext(accountId, context));
+        final InvoiceItemModelDao externalCharge = dao.insertExternalCharge(accountId, invoiceId, bundleId, description, amount, effectiveDate, currency, internalCallContextFactory.createInternalCallContext(accountId, context));
+        return InvoiceItemFactory.fromModelDao(externalCharge);
     }
 
     @Override
     public InvoiceItem getCreditById(final UUID creditId, final TenantContext context) throws InvoiceApiException {
-        final InvoiceItem creditItem = dao.getCreditById(creditId, internalCallContextFactory.createInternalTenantContext(context));
+        final InvoiceItem creditItem = InvoiceItemFactory.fromModelDao(dao.getCreditById(creditId, internalCallContextFactory.createInternalTenantContext(context)));
         if (creditItem == null) {
             throw new InvoiceApiException(ErrorCode.INVOICE_NO_SUCH_CREDIT, creditId);
         }
@@ -231,7 +255,8 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
             throw new InvoiceApiException(ErrorCode.CREDIT_AMOUNT_INVALID, amount);
         }
 
-        return dao.insertCredit(accountId, invoiceId, amount, effectiveDate, currency, internalCallContextFactory.createInternalCallContext(accountId, context));
+        final InvoiceItemModelDao credit = dao.insertCredit(accountId, invoiceId, amount, effectiveDate, currency, internalCallContextFactory.createInternalCallContext(accountId, context));
+        return InvoiceItemFactory.fromModelDao(credit);
     }
 
     @Override
@@ -248,7 +273,8 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
             throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_ADJUSTMENT_AMOUNT_INVALID, amount);
         }
 
-        return dao.insertInvoiceItemAdjustment(accountId, invoiceId, invoiceItemId, effectiveDate, amount, currency, internalCallContextFactory.createInternalCallContext(accountId, context));
+        final InvoiceItemModelDao adjustment = dao.insertInvoiceItemAdjustment(accountId, invoiceId, invoiceItemId, effectiveDate, amount, currency, internalCallContextFactory.createInternalCallContext(accountId, context));
+        return InvoiceItemFactory.fromModelDao(adjustment);
     }
 
     @Override
@@ -268,8 +294,8 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
 
         // Check if this account has the MANUAL_PAY system tag
         boolean manualPay = false;
-        final Map<String, Tag> accountTags = tagApi.getTags(account.getId(), ObjectType.ACCOUNT, internalContext);
-        for (final Tag tag : accountTags.values()) {
+        final List<Tag> accountTags = tagApi.getTags(account.getId(), ObjectType.ACCOUNT, internalContext);
+        for (final Tag tag : accountTags) {
             if (ControlTagType.MANUAL_PAY.getId().equals(tag.getTagDefinitionId())) {
                 manualPay = true;
                 break;
@@ -279,7 +305,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
         return generator.generateInvoice(account, invoice, manualPay);
     }
 
-    private void notifyBusOfInvoiceAdjustment(final UUID invoiceId, final UUID accountId, final UUID userToken, final InternalCallContext context) {
+    private void notifyBusOfInvoiceAdjustment(final UUID invoiceId, final UUID accountId, final InternalCallContext context) {
         try {
             eventBus.post(new DefaultInvoiceAdjustmentEvent(invoiceId, accountId, context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId()), context);
         } catch (EventBusException e) {
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultNullInvoiceEvent.java b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultNullInvoiceEvent.java
index 059dde1..30a8dfa 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultNullInvoiceEvent.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultNullInvoiceEvent.java
@@ -49,7 +49,6 @@ public class DefaultNullInvoiceEvent extends DefaultBusInternalEvent implements 
         return BusInternalEventType.INVOICE_EMPTY;
     }
 
-
     @Override
     public UUID getAccountId() {
         return accountId;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
new file mode 100644
index 0000000..730fbbc
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
@@ -0,0 +1,957 @@
+/*
+ * Copyright 2010-2012 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.invoice.dao;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.LocalDate;
+import org.skife.jdbi.v2.IDBI;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoiceItemType;
+import com.ning.billing.invoice.api.InvoicePayment.InvoicePaymentType;
+import com.ning.billing.invoice.api.user.DefaultInvoiceAdjustmentEvent;
+import com.ning.billing.invoice.model.InvoiceItemList;
+import com.ning.billing.invoice.model.RecurringInvoiceItem;
+import com.ning.billing.invoice.notification.NextBillingDatePoster;
+import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.entity.EntityPersistenceException;
+import com.ning.billing.util.entity.dao.EntityDaoBase;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+import com.ning.billing.util.svcsapi.bus.InternalBus;
+import com.ning.billing.util.svcsapi.bus.InternalBus.EventBusException;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.inject.Inject;
+
+public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, InvoiceApiException> implements InvoiceDao {
+
+    private static final Logger log = LoggerFactory.getLogger(DefaultInvoiceDao.class);
+
+    private final NextBillingDatePoster nextBillingDatePoster;
+    private final InternalBus eventBus;
+
+    @Inject
+    public DefaultInvoiceDao(final IDBI dbi,
+                             final NextBillingDatePoster nextBillingDatePoster,
+                             final InternalBus eventBus) {
+        super(new EntitySqlDaoTransactionalJdbiWrapper(dbi), InvoiceSqlDao.class);
+        this.nextBillingDatePoster = nextBillingDatePoster;
+        this.eventBus = eventBus;
+    }
+
+    @Override
+    protected InvoiceApiException generateAlreadyExistsException(final InvoiceModelDao entity, final InternalCallContext context) {
+        return new InvoiceApiException(ErrorCode.INVOICE_ACCOUNT_ID_INVALID, entity.getId());
+    }
+
+    @Override
+    public List<InvoiceModelDao> getInvoicesByAccount(final UUID accountId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceModelDao>>() {
+            @Override
+            public List<InvoiceModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
+                final List<InvoiceModelDao> invoices = invoiceDao.getInvoicesByAccount(accountId.toString(), context);
+                populateChildren(invoices, entitySqlDaoWrapperFactory, context);
+
+                return invoices;
+            }
+        });
+    }
+
+    @Override
+    public List<InvoiceModelDao> getAllInvoicesByAccount(final UUID accountId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceModelDao>>() {
+            @Override
+            public List<InvoiceModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return getAllInvoicesByAccountFromTransaction(accountId, entitySqlDaoWrapperFactory, context);
+            }
+        });
+    }
+
+    @Override
+    public List<InvoiceModelDao> getInvoicesByAccount(final UUID accountId, final LocalDate fromDate, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceModelDao>>() {
+            @Override
+            public List<InvoiceModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
+                final List<InvoiceModelDao> invoices = invoiceDao.getInvoicesByAccountAfterDate(accountId.toString(),
+                                                                                                fromDate.toDateTimeAtStartOfDay().toDate(),
+                                                                                                context);
+                populateChildren(invoices, entitySqlDaoWrapperFactory, context);
+
+                return invoices;
+            }
+        });
+    }
+
+    @Override
+    public List<InvoiceModelDao> get(final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceModelDao>>() {
+            @Override
+            public List<InvoiceModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
+                final List<InvoiceModelDao> invoices = invoiceDao.get(context);
+                populateChildren(invoices, entitySqlDaoWrapperFactory, context);
+
+                return invoices;
+            }
+        });
+    }
+
+    @Override
+    public InvoiceModelDao getById(final UUID invoiceId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoiceModelDao>() {
+            @Override
+            public InvoiceModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
+                final InvoiceModelDao invoice = invoiceDao.getById(invoiceId.toString(), context);
+                if (invoice == null) {
+                    throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
+                }
+                populateChildren(invoice, entitySqlDaoWrapperFactory, context);
+                return invoice;
+            }
+        });
+    }
+
+    @Override
+    public InvoiceModelDao getByNumber(final Integer number, final InternalTenantContext context) throws InvoiceApiException {
+        return getByRecordId(number.longValue(), context);
+    }
+
+    @Override
+    public void createInvoice(final InvoiceModelDao invoice, final List<InvoiceItemModelDao> invoiceItems,
+                              final List<InvoicePaymentModelDao> invoicePayments, final boolean isRealInvoice, final InternalCallContext context) {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
+                final InvoiceModelDao currentInvoice = transactional.getById(invoice.getId().toString(), context);
+                if (currentInvoice == null) {
+                    // We only want to insert that invoice if there are real invoiceItems associated to it -- if not, this is just
+                    // a shell invoice and we only need to insert the invoiceItems -- for the already existing invoices
+                    if (isRealInvoice) {
+                        transactional.create(invoice, context);
+                    }
+
+                    // Create the invoice items
+                    final InvoiceItemSqlDao transInvoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+                    for (final InvoiceItemModelDao invoiceItemModelDao : invoiceItems) {
+                        transInvoiceItemSqlDao.create(invoiceItemModelDao, context);
+                    }
+
+                    // Add entries in the notification queue for recurring items
+                    final List<InvoiceItemModelDao> recurringInvoiceItems = ImmutableList.<InvoiceItemModelDao>copyOf(Collections2.filter(invoiceItems, new Predicate<InvoiceItemModelDao>() {
+                        @Override
+                        public boolean apply(@Nullable final InvoiceItemModelDao item) {
+                            return item.getType() == InvoiceItemType.RECURRING;
+                        }
+                    }));
+                    notifyOfFutureBillingEvents(entitySqlDaoWrapperFactory, invoice.getAccountId(), recurringInvoiceItems);
+
+                    // Create associated payments
+                    final InvoicePaymentSqlDao invoicePaymentSqlDao = entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class);
+                    invoicePaymentSqlDao.batchCreateFromTransaction(invoicePayments, context);
+                }
+
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public List<InvoiceModelDao> getInvoicesBySubscription(final UUID subscriptionId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceModelDao>>() {
+            @Override
+            public List<InvoiceModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
+                final List<InvoiceModelDao> invoices = invoiceDao.getInvoicesBySubscription(subscriptionId.toString(), context);
+                populateChildren(invoices, entitySqlDaoWrapperFactory, context);
+
+                return invoices;
+            }
+        });
+    }
+
+    @Override
+    public BigDecimal getAccountBalance(final UUID accountId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<BigDecimal>() {
+            @Override
+            public BigDecimal inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                BigDecimal cba = BigDecimal.ZERO;
+
+                BigDecimal accountBalance = BigDecimal.ZERO;
+                final List<InvoiceModelDao> invoices = getAllInvoicesByAccountFromTransaction(accountId, entitySqlDaoWrapperFactory, context);
+                for (final InvoiceModelDao cur : invoices) {
+                    accountBalance = accountBalance.add(InvoiceModelDaoHelper.getBalance(cur));
+                    cba = cba.add(InvoiceModelDaoHelper.getCBAAmount(cur));
+                }
+                return accountBalance.subtract(cba);
+            }
+        });
+    }
+
+    @Override
+    public BigDecimal getAccountCBA(final UUID accountId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<BigDecimal>() {
+            @Override
+            public BigDecimal inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return getAccountCBAFromTransaction(accountId, entitySqlDaoWrapperFactory, context);
+            }
+        });
+    }
+
+    @Override
+    public List<InvoiceModelDao> getUnpaidInvoicesByAccountId(final UUID accountId, @Nullable final LocalDate upToDate, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceModelDao>>() {
+            @Override
+            public List<InvoiceModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final List<InvoiceModelDao> invoices = getAllInvoicesByAccountFromTransaction(accountId, entitySqlDaoWrapperFactory, context);
+                final Collection<InvoiceModelDao> unpaidInvoices = Collections2.filter(invoices, new Predicate<InvoiceModelDao>() {
+                    @Override
+                    public boolean apply(final InvoiceModelDao in) {
+                        return (InvoiceModelDaoHelper.getBalance(in).compareTo(BigDecimal.ZERO) >= 1) && (upToDate == null || !in.getTargetDate().isAfter(upToDate));
+                    }
+                });
+                return new ArrayList<InvoiceModelDao>(unpaidInvoices);
+            }
+        });
+    }
+
+    @Override
+    public UUID getInvoiceIdByPaymentId(final UUID paymentId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<UUID>() {
+            @Override
+            public UUID inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class).getInvoiceIdByPaymentId(paymentId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public List<InvoicePaymentModelDao> getInvoicePayments(final UUID paymentId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoicePaymentModelDao>>() {
+            @Override
+            public List<InvoicePaymentModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class).getInvoicePayments(paymentId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+
+    public InvoicePaymentModelDao createRefund(final UUID paymentId, final BigDecimal requestedRefundAmount, final boolean isInvoiceAdjusted,
+                                               final Map<UUID, BigDecimal> invoiceItemIdsWithNullAmounts, final UUID paymentCookieId,
+                                               final InternalCallContext context)
+            throws InvoiceApiException {
+        final boolean isInvoiceItemAdjusted = isInvoiceAdjusted && invoiceItemIdsWithNullAmounts.size() > 0;
+
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoicePaymentModelDao>() {
+            @Override
+            public InvoicePaymentModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoicePaymentSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class);
+
+                final InvoiceSqlDao transInvoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
+                final InvoicePaymentModelDao payment = transactional.getByPaymentId(paymentId.toString(), context);
+                if (payment == null) {
+                    throw new InvoiceApiException(ErrorCode.INVOICE_PAYMENT_BY_ATTEMPT_NOT_FOUND, paymentId);
+                }
+
+                // Retrieve the amounts to adjust, if needed
+                final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts = computeItemAdjustments(payment.getInvoiceId().toString(),
+                                                                                               entitySqlDaoWrapperFactory,
+                                                                                               invoiceItemIdsWithNullAmounts,
+                                                                                               context);
+
+                // Compute the actual amount to refund
+                final BigDecimal requestedPositiveAmount = computePositiveRefundAmount(payment, requestedRefundAmount, invoiceItemIdsWithAmounts);
+
+                // Before we go further, check if that refund already got inserted -- the payment system keeps a state machine
+                // and so this call may be called several time for the same  paymentCookieId (which is really the refundId)
+                final InvoicePaymentModelDao existingRefund = transactional.getPaymentsForCookieId(paymentCookieId.toString(), context);
+                if (existingRefund != null) {
+                    return existingRefund;
+                }
+
+                final InvoicePaymentModelDao refund = new InvoicePaymentModelDao(UUID.randomUUID(), context.getCreatedDate(), InvoicePaymentType.REFUND,
+                                                                                 payment.getInvoiceId(), paymentId,
+                                                                                 context.getCreatedDate(), requestedPositiveAmount.negate(),
+                                                                                 payment.getCurrency(), paymentCookieId, payment.getId());
+                transactional.create(refund, context);
+
+                // Retrieve invoice after the Refund
+                final InvoiceModelDao invoice = transInvoiceDao.getById(payment.getInvoiceId().toString(), context);
+                if (invoice != null) {
+                    populateChildren(invoice, entitySqlDaoWrapperFactory, context);
+                } else {
+                    throw new IllegalStateException("Invoice shouldn't be null for payment " + payment.getId());
+                }
+
+                final BigDecimal invoiceBalanceAfterRefund = InvoiceModelDaoHelper.getBalance(invoice);
+                final InvoiceItemSqlDao transInvoiceItemDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+
+                // At this point, we created the refund which made the invoice balance positive and applied any existing
+                // available CBA to that invoice.
+                // We now need to adjust the invoice and/or invoice items if needed and specified.
+                if (isInvoiceAdjusted && !isInvoiceItemAdjusted) {
+                    // Invoice adjustment
+                    final BigDecimal maxBalanceToAdjust = (invoiceBalanceAfterRefund.compareTo(BigDecimal.ZERO) <= 0) ? BigDecimal.ZERO : invoiceBalanceAfterRefund;
+                    final BigDecimal requestedPositiveAmountToAdjust = requestedPositiveAmount.compareTo(maxBalanceToAdjust) > 0 ? maxBalanceToAdjust : requestedPositiveAmount;
+                    if (requestedPositiveAmountToAdjust.compareTo(BigDecimal.ZERO) > 0) {
+                        final InvoiceItemModelDao adjItem = new InvoiceItemModelDao(context.getCreatedDate(), InvoiceItemType.REFUND_ADJ, invoice.getId(), invoice.getAccountId(),
+                                                                                    null, null, null, null, context.getCreatedDate().toLocalDate(), null,
+                                                                                    requestedPositiveAmountToAdjust.negate(), null, invoice.getCurrency(), null);
+                        transInvoiceItemDao.create(adjItem, context);
+                    }
+                } else if (isInvoiceAdjusted) {
+                    // Invoice item adjustment
+                    for (final UUID invoiceItemId : invoiceItemIdsWithAmounts.keySet()) {
+                        final BigDecimal adjAmount = invoiceItemIdsWithAmounts.get(invoiceItemId);
+                        final InvoiceItemModelDao item = createAdjustmentItem(entitySqlDaoWrapperFactory, invoice.getId(), invoiceItemId, adjAmount,
+                                                                              invoice.getCurrency(), context.getCreatedDate().toLocalDate(),
+                                                                              context);
+                        transInvoiceItemDao.create(item, context);
+                    }
+                }
+
+                // Notify the bus since the balance of the invoice changed
+                notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoice.getId(), invoice.getAccountId(), context.getUserToken(), context);
+
+                return refund;
+            }
+        });
+    }
+
+    /**
+     * Find amounts to adjust for individual items, if not specified.
+     * The user gives us a list of items to adjust associated with a given amount (how much to refund per invoice item).
+     * In case of full adjustments, the amount can be null: in this case, we retrieve the original amount for the invoice
+     * item.
+     *
+     * @param invoiceId                     original invoice id
+     * @param entitySqlDaoWrapperFactory    the EntitySqlDaoWrapperFactory from the current transaction
+     * @param invoiceItemIdsWithNullAmounts the original mapping between invoice item ids and amount to refund (contains null)
+     * @param context                       the tenant context
+     * @return the final mapping between invoice item ids and amount to refund
+     * @throws InvoiceApiException
+     */
+    private Map<UUID, BigDecimal> computeItemAdjustments(final String invoiceId,
+                                                         final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory,
+                                                         final Map<UUID, BigDecimal> invoiceItemIdsWithNullAmounts,
+                                                         final InternalTenantContext context) throws InvoiceApiException {
+        // Populate the missing amounts for individual items, if needed
+        final Builder<UUID, BigDecimal> invoiceItemIdsWithAmountsBuilder = new Builder<UUID, BigDecimal>();
+        if (invoiceItemIdsWithNullAmounts.size() == 0) {
+            return invoiceItemIdsWithAmountsBuilder.build();
+        }
+
+        // Retrieve invoice before the Refund
+        final InvoiceModelDao invoice = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class).getById(invoiceId, context);
+        if (invoice != null) {
+            populateChildren(invoice, entitySqlDaoWrapperFactory, context);
+        } else {
+            throw new IllegalStateException("Invoice shouldn't be null for id " + invoiceId);
+        }
+
+        for (final UUID invoiceItemId : invoiceItemIdsWithNullAmounts.keySet()) {
+            final BigDecimal adjAmount = Objects.firstNonNull(invoiceItemIdsWithNullAmounts.get(invoiceItemId),
+                                                              getInvoiceItemAmountForId(invoice, invoiceItemId));
+            final BigDecimal adjAmountRemainingAfterRepair = computeItemAdjustmentAmount(invoiceItemId, adjAmount, invoice.getInvoiceItems());
+            if (adjAmountRemainingAfterRepair.compareTo(BigDecimal.ZERO) > 0) {
+                invoiceItemIdsWithAmountsBuilder.put(invoiceItemId, adjAmountRemainingAfterRepair);
+            }
+        }
+
+        return invoiceItemIdsWithAmountsBuilder.build();
+    }
+
+    /**
+     * @param invoiceItem  item we are adjusting
+     * @param requestedPositiveAmountToAdjust
+     *                     amount we are adjusting for that item
+     * @param invoiceItems list of all invoice items on this invoice
+     * @return the amount we should really adjust based on whether or not the item got repaired
+     */
+    private BigDecimal computeItemAdjustmentAmount(final UUID invoiceItem, final BigDecimal requestedPositiveAmountToAdjust, final List<InvoiceItemModelDao> invoiceItems) {
+
+        BigDecimal positiveRepairedAmount = BigDecimal.ZERO;
+
+        final Collection<InvoiceItemModelDao> repairedItems = Collections2.filter(invoiceItems, new Predicate<InvoiceItemModelDao>() {
+            @Override
+            public boolean apply(final InvoiceItemModelDao input) {
+                return (input.getType() == InvoiceItemType.REPAIR_ADJ && input.getLinkedItemId().equals(invoiceItem));
+            }
+        });
+        for (final InvoiceItemModelDao cur : repairedItems) {
+            // Repair item are negative so we negate to make it positive
+            positiveRepairedAmount = positiveRepairedAmount.add(cur.getAmount().negate());
+        }
+        return (positiveRepairedAmount.compareTo(requestedPositiveAmountToAdjust) >= 0) ? BigDecimal.ZERO : requestedPositiveAmountToAdjust.subtract(positiveRepairedAmount);
+    }
+
+    private BigDecimal getInvoiceItemAmountForId(final InvoiceModelDao invoice, final UUID invoiceItemId) throws InvoiceApiException {
+        for (final InvoiceItemModelDao invoiceItem : invoice.getInvoiceItems()) {
+            if (invoiceItem.getId().equals(invoiceItemId)) {
+                return invoiceItem.getAmount();
+            }
+        }
+
+        throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_NOT_FOUND, invoiceItemId);
+    }
+
+    @VisibleForTesting
+    BigDecimal computePositiveRefundAmount(final InvoicePaymentModelDao payment, final BigDecimal requestedAmount, final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts) throws InvoiceApiException {
+        final BigDecimal maxRefundAmount = payment.getAmount() == null ? BigDecimal.ZERO : payment.getAmount();
+        final BigDecimal requestedPositiveAmount = requestedAmount == null ? maxRefundAmount : requestedAmount;
+        // This check is good but not enough, we need to also take into account previous refunds
+        // (But that should have been checked in the payment call already)
+        if (requestedPositiveAmount.compareTo(maxRefundAmount) > 0) {
+            throw new InvoiceApiException(ErrorCode.REFUND_AMOUNT_TOO_HIGH, requestedPositiveAmount, maxRefundAmount);
+        }
+
+        // Verify if the requested amount matches the invoice items to adjust, if specified
+        BigDecimal amountFromItems = BigDecimal.ZERO;
+        for (final BigDecimal itemAmount : invoiceItemIdsWithAmounts.values()) {
+            amountFromItems = amountFromItems.add(itemAmount);
+        }
+
+        // Sanity check: if some items were specified, then the sum should be equal to specified refund amount, if specified
+        if (amountFromItems.compareTo(BigDecimal.ZERO) != 0 && requestedPositiveAmount.compareTo(amountFromItems) != 0) {
+            throw new InvoiceApiException(ErrorCode.REFUND_AMOUNT_DONT_MATCH_ITEMS_TO_ADJUST, requestedPositiveAmount, amountFromItems);
+        }
+        return requestedPositiveAmount;
+    }
+
+    @Override
+    public InvoicePaymentModelDao postChargeback(final UUID invoicePaymentId, final BigDecimal amount, final InternalCallContext context) throws InvoiceApiException {
+
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoicePaymentModelDao>() {
+            @Override
+            public InvoicePaymentModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoicePaymentSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class);
+
+                final BigDecimal maxChargedBackAmount = getRemainingAmountPaidFromTransaction(invoicePaymentId, entitySqlDaoWrapperFactory, context);
+                final BigDecimal requestedChargedBackAmout = (amount == null) ? maxChargedBackAmount : amount;
+                if (requestedChargedBackAmout.compareTo(BigDecimal.ZERO) <= 0) {
+                    throw new InvoiceApiException(ErrorCode.CHARGE_BACK_AMOUNT_IS_NEGATIVE);
+                }
+                if (requestedChargedBackAmout.compareTo(maxChargedBackAmount) > 0) {
+                    throw new InvoiceApiException(ErrorCode.CHARGE_BACK_AMOUNT_TOO_HIGH, requestedChargedBackAmout, maxChargedBackAmount);
+                }
+
+                final InvoicePaymentModelDao payment = entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class).getById(invoicePaymentId.toString(), context);
+                if (payment == null) {
+                    throw new InvoiceApiException(ErrorCode.INVOICE_PAYMENT_NOT_FOUND, invoicePaymentId.toString());
+                } else {
+                    final InvoicePaymentModelDao chargeBack = new InvoicePaymentModelDao(UUID.randomUUID(), context.getCreatedDate(), InvoicePaymentType.CHARGED_BACK,
+                                                                                         payment.getInvoiceId(), payment.getPaymentId(), context.getCreatedDate(),
+                                                                                         requestedChargedBackAmout.negate(), payment.getCurrency(), null, payment.getId());
+                    transactional.create(chargeBack, context);
+
+                    // Notify the bus since the balance of the invoice changed
+                    final UUID accountId = transactional.getAccountIdFromInvoicePaymentId(chargeBack.getId().toString(), context);
+                    notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, payment.getInvoiceId(), accountId, context.getUserToken(), context);
+
+                    return chargeBack;
+                }
+            }
+        });
+    }
+
+    @Override
+    public BigDecimal getRemainingAmountPaid(final UUID invoicePaymentId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<BigDecimal>() {
+            @Override
+            public BigDecimal inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return getRemainingAmountPaidFromTransaction(invoicePaymentId, entitySqlDaoWrapperFactory, context);
+            }
+        });
+    }
+
+    @Override
+    public UUID getAccountIdFromInvoicePaymentId(final UUID invoicePaymentId, final InternalTenantContext context) throws InvoiceApiException {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<UUID>() {
+            @Override
+            public UUID inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final UUID accountId = entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class).getAccountIdFromInvoicePaymentId(invoicePaymentId.toString(), context);
+                if (accountId == null) {
+                    throw new InvoiceApiException(ErrorCode.CHARGE_BACK_COULD_NOT_FIND_ACCOUNT_ID, invoicePaymentId);
+                } else {
+                    return accountId;
+                }
+            }
+        });
+    }
+
+    @Override
+    public List<InvoicePaymentModelDao> getChargebacksByAccountId(final UUID accountId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoicePaymentModelDao>>() {
+            @Override
+            public List<InvoicePaymentModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class).getChargeBacksByAccountId(accountId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public List<InvoicePaymentModelDao> getChargebacksByPaymentId(final UUID paymentId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoicePaymentModelDao>>() {
+            @Override
+            public List<InvoicePaymentModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class).getChargebacksByPaymentId(paymentId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public InvoicePaymentModelDao getChargebackById(final UUID chargebackId, final InternalTenantContext context) throws InvoiceApiException {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoicePaymentModelDao>() {
+            @Override
+            public InvoicePaymentModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoicePaymentModelDao chargeback = entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class).getById(chargebackId.toString(), context);
+                if (chargeback == null) {
+                    throw new InvoiceApiException(ErrorCode.CHARGE_BACK_DOES_NOT_EXIST, chargebackId);
+                } else {
+                    return chargeback;
+                }
+            }
+        });
+    }
+
+    @Override
+    public InvoiceItemModelDao getExternalChargeById(final UUID externalChargeId, final InternalTenantContext context) throws InvoiceApiException {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoiceItemModelDao>() {
+            @Override
+            public InvoiceItemModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class).getById(externalChargeId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public void notifyOfPayment(final InvoicePaymentModelDao invoicePayment, final InternalCallContext context) {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class).create(invoicePayment, context);
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public InvoiceItemModelDao insertExternalCharge(final UUID accountId, @Nullable final UUID invoiceId, @Nullable final UUID bundleId, final String description,
+                                                    final BigDecimal amount, final LocalDate effectiveDate, final Currency currency, final InternalCallContext context)
+            throws InvoiceApiException {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoiceItemModelDao>() {
+            @Override
+            public InvoiceItemModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
+                UUID invoiceIdForExternalCharge = invoiceId;
+                // Create an invoice for that external charge if it doesn't exist
+                if (invoiceIdForExternalCharge == null) {
+                    final InvoiceModelDao invoiceForExternalCharge = new InvoiceModelDao(accountId, effectiveDate, effectiveDate, currency);
+                    transactional.create(invoiceForExternalCharge, context);
+                    invoiceIdForExternalCharge = invoiceForExternalCharge.getId();
+                }
+
+                final InvoiceItemModelDao externalCharge = new InvoiceItemModelDao(context.getCreatedDate(), InvoiceItemType.EXTERNAL_CHARGE,
+                                                                                   invoiceIdForExternalCharge, accountId,
+                                                                                   bundleId, null, description, null,
+                                                                                   effectiveDate, null, amount, null,
+                                                                                   currency, null);
+                final InvoiceItemSqlDao transInvoiceItemDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+                transInvoiceItemDao.create(externalCharge, context);
+
+                // At this point, reread the invoice and figure out if we need to consume some of the CBA
+                final InvoiceModelDao invoice = transactional.getById(invoiceIdForExternalCharge.toString(), context);
+                if (invoice == null) {
+                    throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceIdForExternalCharge);
+                }
+                populateChildren(invoice, entitySqlDaoWrapperFactory, context);
+
+                final BigDecimal accountCbaAvailable = getAccountCBAFromTransaction(invoice.getAccountId(), entitySqlDaoWrapperFactory, context);
+                final BigDecimal balance = InvoiceModelDaoHelper.getBalance(invoice);
+                if (accountCbaAvailable.compareTo(BigDecimal.ZERO) > 0 && balance.compareTo(BigDecimal.ZERO) > 0) {
+                    final BigDecimal cbaAmountToConsume = accountCbaAvailable.compareTo(balance) > 0 ? balance.negate() : accountCbaAvailable.negate();
+                    final InvoiceItemModelDao cbaAdjItem = new InvoiceItemModelDao(context.getCreatedDate(), InvoiceItemType.CBA_ADJ,
+                                                                                   invoice.getId(), invoice.getAccountId(),
+                                                                                   null, null, null, null,
+                                                                                   context.getCreatedDate().toLocalDate(),
+                                                                                   null, cbaAmountToConsume, null,
+                                                                                   invoice.getCurrency(), null);
+                    transInvoiceItemDao.create(cbaAdjItem, context);
+                }
+
+                // Notify the bus since the balance of the invoice changed
+                notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoiceId, accountId, context.getUserToken(), context);
+
+                return externalCharge;
+            }
+        });
+    }
+
+    @Override
+    public InvoiceItemModelDao getCreditById(final UUID creditId, final InternalTenantContext context) throws InvoiceApiException {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoiceItemModelDao>() {
+            @Override
+            public InvoiceItemModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class).getById(creditId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public InvoiceItemModelDao insertCredit(final UUID accountId, @Nullable final UUID invoiceId, final BigDecimal positiveCreditAmount,
+                                            final LocalDate effectiveDate, final Currency currency, final InternalCallContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoiceItemModelDao>() {
+            @Override
+            public InvoiceItemModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
+                UUID invoiceIdForCredit = invoiceId;
+                // Create an invoice for that credit if it doesn't exist
+                if (invoiceIdForCredit == null) {
+                    final InvoiceModelDao invoiceForCredit = new InvoiceModelDao(accountId, effectiveDate, effectiveDate, currency);
+                    transactional.create(invoiceForCredit, context);
+                    invoiceIdForCredit = invoiceForCredit.getId();
+                }
+
+                // Note! The amount is negated here!
+                final InvoiceItemModelDao credit = new InvoiceItemModelDao(context.getCreatedDate(), InvoiceItemType.CREDIT_ADJ, invoiceIdForCredit,
+                                                                           accountId, null, null, null, null, effectiveDate,
+                                                                           null, positiveCreditAmount.negate(), null,
+                                                                           currency, null);
+                insertItemAndAddCBAIfNeeded(entitySqlDaoWrapperFactory, credit, context);
+
+                // Notify the bus since the balance of the invoice changed
+                notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoiceId, accountId, context.getUserToken(), context);
+
+                return credit;
+            }
+        });
+    }
+
+    @Override
+    public InvoiceItemModelDao insertInvoiceItemAdjustment(final UUID accountId, final UUID invoiceId, final UUID invoiceItemId,
+                                                           final LocalDate effectiveDate, @Nullable final BigDecimal positiveAdjAmount,
+                                                           @Nullable final Currency currency, final InternalCallContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoiceItemModelDao>() {
+            @Override
+            public InvoiceItemModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceItemModelDao invoiceItemAdjustment = createAdjustmentItem(entitySqlDaoWrapperFactory, invoiceId, invoiceItemId, positiveAdjAmount,
+                                                                                       currency, effectiveDate, context);
+                insertItemAndAddCBAIfNeeded(entitySqlDaoWrapperFactory, invoiceItemAdjustment, context);
+                notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoiceId, accountId, context.getUserToken(), context);
+
+                return invoiceItemAdjustment;
+            }
+        });
+    }
+
+    @Override
+    public void deleteCBA(final UUID accountId, final UUID invoiceId, final UUID invoiceItemId, final InternalCallContext context) throws InvoiceApiException {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
+                // Retrieve the invoice and make sure it belongs to the right account
+                final InvoiceModelDao invoice = transactional.getById(invoiceId.toString(), context);
+                if (invoice == null || !invoice.getAccountId().equals(accountId)) {
+                    throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
+                }
+
+                // Retrieve the invoice item and make sure it belongs to the right invoice
+                final InvoiceItemSqlDao invoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+                final InvoiceItemModelDao cbaItem = invoiceItemSqlDao.getById(invoiceItemId.toString(), context);
+                if (cbaItem == null || !cbaItem.getInvoiceId().equals(invoice.getId())) {
+                    throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_NOT_FOUND, invoiceItemId);
+                }
+
+                // First, adjust the same invoice with the CBA amount to "delete"
+                final InvoiceItemModelDao cbaAdjItem = new InvoiceItemModelDao(context.getCreatedDate(), InvoiceItemType.CBA_ADJ, invoice.getId(), invoice.getAccountId(),
+                                                                               null, null, null, null, context.getCreatedDate().toLocalDate(),
+                                                                               null, cbaItem.getAmount().negate(), null, cbaItem.getCurrency(), cbaItem.getId());
+                invoiceItemSqlDao.create(cbaAdjItem, context);
+
+                // Verify the final invoice balance is not negative
+                populateChildren(invoice, entitySqlDaoWrapperFactory, context);
+                if (InvoiceModelDaoHelper.getBalance(invoice).compareTo(BigDecimal.ZERO) < 0) {
+                    throw new InvoiceApiException(ErrorCode.INVOICE_WOULD_BE_NEGATIVE);
+                }
+
+                // If there is more account credit than CBA we adjusted, we're done.
+                // Otherwise, we need to find further invoices on which this credit was consumed
+                final BigDecimal accountCBA = getAccountCBAFromTransaction(accountId, entitySqlDaoWrapperFactory, context);
+                if (accountCBA.compareTo(BigDecimal.ZERO) < 0) {
+                    if (accountCBA.compareTo(cbaItem.getAmount().negate()) < 0) {
+                        throw new IllegalStateException("The account balance can't be lower than the amount adjusted");
+                    }
+                    final List<InvoiceModelDao> invoicesFollowing = transactional.getInvoicesByAccountAfterDate(accountId.toString(),
+                                                                                                                invoice.getInvoiceDate().toDateTimeAtStartOfDay().toDate(),
+                                                                                                                context);
+                    populateChildren(invoicesFollowing, entitySqlDaoWrapperFactory, context);
+
+                    // The remaining amount to adjust (i.e. the amount of credits used on following invoices)
+                    // is the current account CBA balance (minus the sign)
+                    BigDecimal positiveRemainderToAdjust = accountCBA.negate();
+                    for (final InvoiceModelDao invoiceFollowing : invoicesFollowing) {
+                        if (invoiceFollowing.getId().equals(invoice.getId())) {
+                            continue;
+                        }
+
+                        // Add a single adjustment per invoice
+                        BigDecimal positiveCBAAdjItemAmount = BigDecimal.ZERO;
+
+                        for (final InvoiceItemModelDao cbaUsed : invoiceFollowing.getInvoiceItems()) {
+                            // Ignore non CBA items or credits (CBA >= 0)
+                            if (!InvoiceItemType.CBA_ADJ.equals(cbaUsed.getType()) ||
+                                cbaUsed.getAmount().compareTo(BigDecimal.ZERO) >= 0) {
+                                continue;
+                            }
+
+                            final BigDecimal positiveCBAUsedAmount = cbaUsed.getAmount().negate();
+                            final BigDecimal positiveNextCBAAdjItemAmount;
+                            if (positiveCBAUsedAmount.compareTo(positiveRemainderToAdjust) < 0) {
+                                positiveNextCBAAdjItemAmount = positiveCBAUsedAmount;
+                                positiveRemainderToAdjust = positiveRemainderToAdjust.subtract(positiveNextCBAAdjItemAmount);
+                            } else {
+                                positiveNextCBAAdjItemAmount = positiveRemainderToAdjust;
+                                positiveRemainderToAdjust = BigDecimal.ZERO;
+                            }
+                            positiveCBAAdjItemAmount = positiveCBAAdjItemAmount.add(positiveNextCBAAdjItemAmount);
+
+                            if (positiveRemainderToAdjust.compareTo(BigDecimal.ZERO) == 0) {
+                                break;
+                            }
+                        }
+
+                        // Add the adjustment on that invoice
+                        final InvoiceItemModelDao nextCBAAdjItem = new InvoiceItemModelDao(context.getCreatedDate(), InvoiceItemType.CBA_ADJ, invoiceFollowing.getId(),
+                                                                                           invoice.getAccountId(), null, null, null, null,
+                                                                                           context.getCreatedDate().toLocalDate(), null,
+                                                                                           positiveCBAAdjItemAmount, null, cbaItem.getCurrency(), cbaItem.getId());
+                        invoiceItemSqlDao.create(nextCBAAdjItem, context);
+                        if (positiveRemainderToAdjust.compareTo(BigDecimal.ZERO) == 0) {
+                            break;
+                        }
+                    }
+                }
+
+                return null;
+            }
+        });
+    }
+
+    /**
+     * Create an adjustment for a given invoice item. This just creates the object in memory, it doesn't write it to disk.
+     *
+     * @param invoiceId         the invoice id
+     * @param invoiceItemId     the invoice item id to adjust
+     * @param effectiveDate     adjustment effective date, in the account timezone
+     * @param positiveAdjAmount the amount to adjust. Pass null to adjust the full amount of the original item
+     * @param currency          the currency of the amount. Pass null to default to the original currency used
+     * @return the adjustment item
+     */
+    private InvoiceItemModelDao createAdjustmentItem(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID invoiceId, final UUID invoiceItemId,
+                                                     final BigDecimal positiveAdjAmount, final Currency currency,
+                                                     final LocalDate effectiveDate, final InternalCallContext context) throws InvoiceApiException {
+        // First, retrieve the invoice item in question
+        final InvoiceItemSqlDao invoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+        final InvoiceItemModelDao invoiceItemToBeAdjusted = invoiceItemSqlDao.getById(invoiceItemId.toString(), context);
+        if (invoiceItemToBeAdjusted == null) {
+            throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_NOT_FOUND, invoiceItemId);
+        }
+
+        // Validate the invoice it belongs to
+        if (!invoiceItemToBeAdjusted.getInvoiceId().equals(invoiceId)) {
+            throw new InvoiceApiException(ErrorCode.INVOICE_INVALID_FOR_INVOICE_ITEM_ADJUSTMENT, invoiceItemId, invoiceId);
+        }
+
+        // Retrieve the amount and currency if needed
+        final BigDecimal amountToAdjust = Objects.firstNonNull(positiveAdjAmount, invoiceItemToBeAdjusted.getAmount());
+        // TODO - should we enforce the currency (and respect the original one) here if the amount passed was null?
+        final Currency currencyForAdjustment = Objects.firstNonNull(currency, invoiceItemToBeAdjusted.getCurrency());
+
+        // Finally, create the adjustment
+        // Note! The amount is negated here!
+        return new InvoiceItemModelDao(context.getCreatedDate(), InvoiceItemType.ITEM_ADJ, invoiceItemToBeAdjusted.getInvoiceId(), invoiceItemToBeAdjusted.getAccountId(),
+                                       null, null, null, null, effectiveDate, effectiveDate, amountToAdjust.negate(), null, currencyForAdjustment, invoiceItemToBeAdjusted.getId());
+    }
+
+    /**
+     * Create an invoice item and adjust the invoice with a CBA item if the new invoice balance is negative.
+     *
+     * @param entitySqlDaoWrapperFactory the EntitySqlDaoWrapperFactory from the current transaction
+     * @param item                       the invoice item to create
+     * @param context                    the call context
+     */
+    private void insertItemAndAddCBAIfNeeded(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory,
+                                             final InvoiceItemModelDao item,
+                                             final InternalCallContext context) throws EntityPersistenceException {
+        final InvoiceItemSqlDao transInvoiceItemDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+        transInvoiceItemDao.create(item, context);
+
+        addCBAIfNeeded(entitySqlDaoWrapperFactory, item.getInvoiceId(), context);
+    }
+
+    /**
+     * Adjust the invoice with a CBA item if the new invoice balance is negative.
+     *
+     * @param entitySqlDaoWrapperFactory the EntitySqlDaoWrapperFactory from the current transaction
+     * @param invoiceId                  the invoice id to adjust
+     * @param context                    the call context
+     */
+    private void addCBAIfNeeded(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory,
+                                final UUID invoiceId,
+                                final InternalCallContext context) throws EntityPersistenceException {
+        final InvoiceModelDao invoice = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class).getById(invoiceId.toString(), context);
+        if (invoice != null) {
+            populateChildren(invoice, entitySqlDaoWrapperFactory, context);
+        } else {
+            throw new IllegalStateException("Invoice shouldn't be null for this item at this stage " + invoiceId);
+        }
+
+        // If invoice balance becomes negative we add some CBA item
+        final BigDecimal balance = InvoiceModelDaoHelper.getBalance(invoice);
+        if (balance.compareTo(BigDecimal.ZERO) < 0) {
+            final InvoiceItemSqlDao transInvoiceItemDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+            final InvoiceItemModelDao cbaAdjItem = new InvoiceItemModelDao(context.getCreatedDate(), InvoiceItemType.CBA_ADJ, invoice.getId(), invoice.getAccountId(),
+                                                                           null, null, null, null, context.getCreatedDate().toLocalDate(),
+                                                                           null, balance.negate(), null, invoice.getCurrency(), null);
+            transInvoiceItemDao.create(cbaAdjItem, context);
+        }
+    }
+
+    private BigDecimal getAccountCBAFromTransaction(final UUID accountId,
+                                                    final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory,
+                                                    final InternalTenantContext context) {
+        BigDecimal cba = BigDecimal.ZERO;
+        final List<InvoiceModelDao> invoices = getAllInvoicesByAccountFromTransaction(accountId, entitySqlDaoWrapperFactory, context);
+        for (final InvoiceModelDao cur : invoices) {
+            final InvoiceItemList invoiceItems = new InvoiceItemList(cur.getInvoiceItems());
+            cba = cba.add(invoiceItems.getCBAAmount());
+        }
+        return cba;
+    }
+
+    private void populateChildren(final InvoiceModelDao invoice, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+        getInvoiceItemsWithinTransaction(invoice, entitySqlDaoWrapperFactory, context);
+        getInvoicePaymentsWithinTransaction(invoice, entitySqlDaoWrapperFactory, context);
+    }
+
+    private void populateChildren(final List<InvoiceModelDao> invoices, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+        getInvoiceItemsWithinTransaction(invoices, entitySqlDaoWrapperFactory, context);
+        getInvoicePaymentsWithinTransaction(invoices, entitySqlDaoWrapperFactory, context);
+    }
+
+    private List<InvoiceModelDao> getAllInvoicesByAccountFromTransaction(final UUID accountId, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+        final List<InvoiceModelDao> invoices = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class).getAllInvoicesByAccount(accountId.toString(), context);
+        populateChildren(invoices, entitySqlDaoWrapperFactory, context);
+        return invoices;
+    }
+
+    private BigDecimal getRemainingAmountPaidFromTransaction(final UUID invoicePaymentId, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+        final BigDecimal amount = entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class).getRemainingAmountPaid(invoicePaymentId.toString(), context);
+        return amount == null ? BigDecimal.ZERO : amount;
+    }
+
+    private void getInvoiceItemsWithinTransaction(final List<InvoiceModelDao> invoices, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+        for (final InvoiceModelDao invoice : invoices) {
+            getInvoiceItemsWithinTransaction(invoice, entitySqlDaoWrapperFactory, context);
+        }
+    }
+
+    private void getInvoiceItemsWithinTransaction(final InvoiceModelDao invoice, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+        final String invoiceId = invoice.getId().toString();
+
+        final InvoiceItemSqlDao transInvoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+        final List<InvoiceItemModelDao> items = transInvoiceItemSqlDao.getInvoiceItemsByInvoice(invoiceId, context);
+        invoice.addInvoiceItems(items);
+    }
+
+    private void getInvoicePaymentsWithinTransaction(final List<InvoiceModelDao> invoices, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+        for (final InvoiceModelDao invoice : invoices) {
+            getInvoicePaymentsWithinTransaction(invoice, entitySqlDaoWrapperFactory, context);
+        }
+    }
+
+    private void getInvoicePaymentsWithinTransaction(final InvoiceModelDao invoice, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+        final InvoicePaymentSqlDao invoicePaymentSqlDao = entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class);
+        final String invoiceId = invoice.getId().toString();
+        final List<InvoicePaymentModelDao> invoicePayments = invoicePaymentSqlDao.getPaymentsForInvoice(invoiceId, context);
+        invoice.addPayments(invoicePayments);
+    }
+
+    private void notifyOfFutureBillingEvents(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID accountId, final List<InvoiceItemModelDao> invoiceItems) {
+
+        for (final InvoiceItemModelDao item : invoiceItems) {
+            if (item.getType() == InvoiceItemType.RECURRING) {
+                if ((item.getEndDate() != null) &&
+                    (item.getAmount() == null ||
+                     item.getAmount().compareTo(BigDecimal.ZERO) >= 0)) {
+                    //
+                    // We insert a future notification for each recurring subscription at the end of the service period  = new CTD of the subscription
+                    //
+                    nextBillingDatePoster.insertNextBillingNotification(entitySqlDaoWrapperFactory, accountId, item.getSubscriptionId(),
+                                                                        item.getEndDate().toDateTimeAtCurrentTime());
+                }
+            }
+        }
+    }
+
+    private void notifyBusOfInvoiceAdjustment(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID invoiceId, final UUID accountId,
+                                              final UUID userToken, final InternalCallContext context) {
+        try {
+            eventBus.postFromTransaction(new DefaultInvoiceAdjustmentEvent(invoiceId, accountId, userToken, context.getAccountRecordId(), context.getTenantRecordId()),
+                                         entitySqlDaoWrapperFactory, context);
+        } catch (EventBusException e) {
+            log.warn("Failed to post adjustment event for invoice " + invoiceId, e);
+        }
+    }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
index f9e9b4d..1d51614 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
@@ -26,46 +26,43 @@ import javax.annotation.Nullable;
 import org.joda.time.LocalDate;
 
 import com.ning.billing.catalog.api.Currency;
-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.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 
 public interface InvoiceDao {
 
-    void create(Invoice invoice, int billCycleDayUTC, boolean isRealInvoice, InternalCallContext context);
+    void createInvoice(InvoiceModelDao invoice, List<InvoiceItemModelDao> invoiceItems,
+                       List<InvoicePaymentModelDao> invoicePayments, boolean isRealInvoice, InternalCallContext context);
 
-    Invoice getById(UUID id, InternalTenantContext context) throws InvoiceApiException;
+    InvoiceModelDao getById(UUID id, InternalTenantContext context);
 
-    Invoice getByNumber(Integer number, InternalTenantContext context) throws InvoiceApiException;
+    InvoiceModelDao getByNumber(Integer number, InternalTenantContext context) throws InvoiceApiException;
 
-    List<Invoice> get(InternalTenantContext context);
+    List<InvoiceModelDao> get(InternalTenantContext context);
 
-    List<Invoice> getInvoicesByAccount(UUID accountId, InternalTenantContext context);
+    List<InvoiceModelDao> getInvoicesByAccount(UUID accountId, InternalTenantContext context);
 
-    List<Invoice> getInvoicesByAccount(UUID accountId, LocalDate fromDate, InternalTenantContext context);
+    List<InvoiceModelDao> getInvoicesByAccount(UUID accountId, LocalDate fromDate, InternalTenantContext context);
 
-    List<Invoice> getInvoicesBySubscription(UUID subscriptionId, InternalTenantContext context);
+    List<InvoiceModelDao> getInvoicesBySubscription(UUID subscriptionId, InternalTenantContext context);
 
     UUID getInvoiceIdByPaymentId(UUID paymentId, InternalTenantContext context);
 
-    List<InvoicePayment> getInvoicePayments(UUID paymentId, InternalTenantContext context);
-
-    void notifyOfPayment(InvoicePayment invoicePayment, InternalCallContext context);
+    List<InvoicePaymentModelDao> getInvoicePayments(UUID paymentId, InternalTenantContext context);
 
     BigDecimal getAccountBalance(UUID accountId, InternalTenantContext context);
 
     public BigDecimal getAccountCBA(UUID accountId, InternalTenantContext context);
 
-    List<Invoice> getUnpaidInvoicesByAccountId(UUID accountId, @Nullable LocalDate upToDate, InternalTenantContext context);
+    List<InvoiceModelDao> getUnpaidInvoicesByAccountId(UUID accountId, @Nullable LocalDate upToDate, InternalTenantContext context);
 
     void test(InternalTenantContext context);
 
-    List<Invoice> getAllInvoicesByAccount(UUID accountId, InternalTenantContext context);
+    // Include migrated invoices
+    List<InvoiceModelDao> getAllInvoicesByAccount(UUID accountId, InternalTenantContext context);
 
-    InvoicePayment postChargeback(UUID invoicePaymentId, BigDecimal amount, InternalCallContext context) throws InvoiceApiException;
+    InvoicePaymentModelDao postChargeback(UUID invoicePaymentId, BigDecimal amount, InternalCallContext context) throws InvoiceApiException;
 
     /**
      * Create a refund.
@@ -79,18 +76,18 @@ public interface InvoiceDao {
      * @return the created invoice payment object associated with this refund
      * @throws InvoiceApiException
      */
-    InvoicePayment createRefund(UUID paymentId, BigDecimal amount, boolean isInvoiceAdjusted, Map<UUID, BigDecimal> invoiceItemIdsWithAmounts,
-                                UUID paymentCookieId, InternalCallContext context) throws InvoiceApiException;
+    InvoicePaymentModelDao createRefund(UUID paymentId, BigDecimal amount, boolean isInvoiceAdjusted, Map<UUID, BigDecimal> invoiceItemIdsWithAmounts,
+                                        UUID paymentCookieId, InternalCallContext context) throws InvoiceApiException;
 
     BigDecimal getRemainingAmountPaid(UUID invoicePaymentId, InternalTenantContext context);
 
     UUID getAccountIdFromInvoicePaymentId(UUID invoicePaymentId, InternalTenantContext context) throws InvoiceApiException;
 
-    List<InvoicePayment> getChargebacksByAccountId(UUID accountId, InternalTenantContext context);
+    List<InvoicePaymentModelDao> getChargebacksByAccountId(UUID accountId, InternalTenantContext context);
 
-    List<InvoicePayment> getChargebacksByPaymentId(UUID paymentId, InternalTenantContext context);
+    List<InvoicePaymentModelDao> getChargebacksByPaymentId(UUID paymentId, InternalTenantContext context);
 
-    InvoicePayment getChargebackById(UUID chargebackId, InternalTenantContext context) throws InvoiceApiException;
+    InvoicePaymentModelDao getChargebackById(UUID chargebackId, InternalTenantContext context) throws InvoiceApiException;
 
     /**
      * Retrieve am external charge by id.
@@ -99,7 +96,7 @@ public interface InvoiceDao {
      * @return the external charge invoice item
      * @throws InvoiceApiException
      */
-    InvoiceItem getExternalChargeById(UUID externalChargeId, InternalTenantContext context) throws InvoiceApiException;
+    InvoiceItemModelDao getExternalChargeById(UUID externalChargeId, InternalTenantContext context) throws InvoiceApiException;
 
     /**
      * Add an external charge to a given account and invoice. If invoiceId is null, a new invoice will be created.
@@ -114,8 +111,8 @@ public interface InvoiceDao {
      * @param context       the call context
      * @return the newly created external charge invoice item
      */
-    InvoiceItem insertExternalCharge(UUID accountId, @Nullable UUID invoiceId, @Nullable UUID bundleId, @Nullable String description,
-                                     BigDecimal amount, LocalDate effectiveDate, Currency currency, InternalCallContext context) throws InvoiceApiException;
+    InvoiceItemModelDao insertExternalCharge(UUID accountId, @Nullable UUID invoiceId, @Nullable UUID bundleId, @Nullable String description,
+                                             BigDecimal amount, LocalDate effectiveDate, Currency currency, InternalCallContext context) throws InvoiceApiException;
 
     /**
      * Retrieve a credit by id.
@@ -124,7 +121,7 @@ public interface InvoiceDao {
      * @return the credit invoice item
      * @throws InvoiceApiException
      */
-    InvoiceItem getCreditById(UUID creditId, InternalTenantContext context) throws InvoiceApiException;
+    InvoiceItemModelDao getCreditById(UUID creditId, InternalTenantContext context) throws InvoiceApiException;
 
     /**
      * Add a credit to a given account and invoice. If invoiceId is null, a new invoice will be created.
@@ -137,8 +134,8 @@ public interface InvoiceDao {
      * @param context       the call context
      * @return the newly created credit invoice item
      */
-    InvoiceItem insertCredit(UUID accountId, @Nullable UUID invoiceId, BigDecimal amount,
-                             LocalDate effectiveDate, Currency currency, InternalCallContext context);
+    InvoiceItemModelDao insertCredit(UUID accountId, @Nullable UUID invoiceId, BigDecimal amount,
+                                     LocalDate effectiveDate, Currency currency, InternalCallContext context);
 
     /**
      * Adjust an invoice item.
@@ -152,8 +149,8 @@ public interface InvoiceDao {
      * @param context       the call context
      * @return the newly created adjustment item
      */
-    InvoiceItem insertInvoiceItemAdjustment(UUID accountId, UUID invoiceId, UUID invoiceItemId, LocalDate effectiveDate,
-                                            @Nullable BigDecimal amount, @Nullable Currency currency, InternalCallContext context);
+    InvoiceItemModelDao insertInvoiceItemAdjustment(UUID accountId, UUID invoiceId, UUID invoiceItemId, LocalDate effectiveDate,
+                                                    @Nullable BigDecimal amount, @Nullable Currency currency, InternalCallContext context);
 
     /**
      * Delete a CBA item.
@@ -163,4 +160,6 @@ public interface InvoiceDao {
      * @param invoiceItemId the invoice item id of the cba item to delete
      */
     void deleteCBA(UUID accountId, UUID invoiceId, UUID invoiceItemId, InternalCallContext context) throws InvoiceApiException;
+
+    void notifyOfPayment(InvoicePaymentModelDao invoicePayment, InternalCallContext context);
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemModelDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemModelDao.java
new file mode 100644
index 0000000..6f289c7
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemModelDao.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2010-2012 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.invoice.dao;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
+import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.dao.EntityModelDao;
+
+public class InvoiceItemModelDao extends EntityBase implements EntityModelDao<InvoiceItem> {
+
+    private InvoiceItemType type;
+    private UUID invoiceId;
+    private UUID accountId;
+    private UUID bundleId;
+    private UUID subscriptionId;
+    private String planName;
+    private String phaseName;
+    private LocalDate startDate;
+    private LocalDate endDate;
+    private BigDecimal amount;
+    private BigDecimal rate;
+    private Currency currency;
+    private UUID linkedItemId;
+
+    public InvoiceItemModelDao() { /* For the DAO mapper */ }
+
+    public InvoiceItemModelDao(final UUID id, final DateTime createdDate, final InvoiceItemType type, final UUID invoiceId,
+                               final UUID accountId, final UUID bundleId, final UUID subscriptionId, final String planName,
+                               final String phaseName, final LocalDate startDate, final LocalDate endDate, final BigDecimal amount,
+                               final BigDecimal rate, final Currency currency, final UUID linkedItemId) {
+        super(id, createdDate, createdDate);
+        this.type = type;
+        this.invoiceId = invoiceId;
+        this.accountId = accountId;
+        this.bundleId = bundleId;
+        this.subscriptionId = subscriptionId;
+        this.planName = planName;
+        this.phaseName = phaseName;
+        this.startDate = startDate;
+        this.endDate = endDate;
+        this.amount = amount;
+        this.rate = rate;
+        this.currency = currency;
+        this.linkedItemId = linkedItemId;
+    }
+
+    public InvoiceItemModelDao(final DateTime createdDate, final InvoiceItemType type, final UUID invoiceId, final UUID accountId,
+                               final UUID bundleId, final UUID subscriptionId, final String planName,
+                               final String phaseName, final LocalDate startDate, final LocalDate endDate, final BigDecimal amount,
+                               final BigDecimal rate, final Currency currency, final UUID linkedItemId) {
+        this(UUID.randomUUID(), createdDate, type, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName,
+             startDate, endDate, amount, rate, currency, linkedItemId);
+    }
+
+    public InvoiceItemModelDao(final InvoiceItem invoiceItem) {
+        this(invoiceItem.getId(), invoiceItem.getCreatedDate(), invoiceItem.getInvoiceItemType(), invoiceItem.getInvoiceId(), invoiceItem.getAccountId(), invoiceItem.getBundleId(),
+             invoiceItem.getSubscriptionId(), invoiceItem.getPlanName(), invoiceItem.getPhaseName(), invoiceItem.getStartDate(), invoiceItem.getEndDate(),
+             invoiceItem.getAmount(), invoiceItem.getRate(), invoiceItem.getCurrency(), invoiceItem.getLinkedItemId());
+    }
+
+    public InvoiceItemType getType() {
+        return type;
+    }
+
+    public UUID getInvoiceId() {
+        return invoiceId;
+    }
+
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    public UUID getBundleId() {
+        return bundleId;
+    }
+
+    public UUID getSubscriptionId() {
+        return subscriptionId;
+    }
+
+    public String getPlanName() {
+        return planName;
+    }
+
+    public String getPhaseName() {
+        return phaseName;
+    }
+
+    public LocalDate getStartDate() {
+        return startDate;
+    }
+
+    public LocalDate getEndDate() {
+        return endDate;
+    }
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    public BigDecimal getRate() {
+        return rate;
+    }
+
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    public UUID getLinkedItemId() {
+        return linkedItemId;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("InvoiceItemModelDao");
+        sb.append("{type=").append(type);
+        sb.append(", invoiceId=").append(invoiceId);
+        sb.append(", accountId=").append(accountId);
+        sb.append(", bundleId=").append(bundleId);
+        sb.append(", subscriptionId=").append(subscriptionId);
+        sb.append(", planName='").append(planName).append('\'');
+        sb.append(", phaseName='").append(phaseName).append('\'');
+        sb.append(", startDate=").append(startDate);
+        sb.append(", endDate=").append(endDate);
+        sb.append(", amount=").append(amount);
+        sb.append(", rate=").append(rate);
+        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 InvoiceItemModelDao that = (InvoiceItemModelDao) o;
+
+        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
+            return false;
+        }
+        if (amount != null ? amount.compareTo(that.amount) != 0 : that.amount != null) {
+            return false;
+        }
+        if (bundleId != null ? !bundleId.equals(that.bundleId) : that.bundleId != null) {
+            return false;
+        }
+        if (currency != that.currency) {
+            return false;
+        }
+        if (endDate != null ? !endDate.equals(that.endDate) : that.endDate != null) {
+            return false;
+        }
+        if (invoiceId != null ? !invoiceId.equals(that.invoiceId) : that.invoiceId != null) {
+            return false;
+        }
+        if (linkedItemId != null ? !linkedItemId.equals(that.linkedItemId) : that.linkedItemId != null) {
+            return false;
+        }
+        if (phaseName != null ? !phaseName.equals(that.phaseName) : that.phaseName != null) {
+            return false;
+        }
+        if (planName != null ? !planName.equals(that.planName) : that.planName != null) {
+            return false;
+        }
+        if (rate != null ? rate.compareTo(that.rate) != 0 : that.rate != null) {
+            return false;
+        }
+        if (startDate != null ? !startDate.equals(that.startDate) : that.startDate != null) {
+            return false;
+        }
+        if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
+            return false;
+        }
+        if (type != that.type) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (type != null ? type.hashCode() : 0);
+        result = 31 * result + (invoiceId != null ? invoiceId.hashCode() : 0);
+        result = 31 * result + (accountId != null ? accountId.hashCode() : 0);
+        result = 31 * result + (bundleId != null ? bundleId.hashCode() : 0);
+        result = 31 * result + (subscriptionId != null ? subscriptionId.hashCode() : 0);
+        result = 31 * result + (planName != null ? planName.hashCode() : 0);
+        result = 31 * result + (phaseName != null ? phaseName.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 + (rate != null ? rate.hashCode() : 0);
+        result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (linkedItemId != null ? linkedItemId.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.INVOICE_ITEMS;
+    }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java
index ce7799f..ff72410 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java
@@ -16,160 +16,29 @@
 
 package com.ning.billing.invoice.dao;
 
-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;
-import java.math.BigDecimal;
-import java.sql.ResultSet;
-import java.sql.SQLException;
 import java.util.List;
-import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.joda.time.LocalDate;
-import org.skife.jdbi.v2.SQLStatement;
-import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.Binder;
-import org.skife.jdbi.v2.sqlobject.BinderFactory;
-import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
-import org.skife.jdbi.v2.sqlobject.SqlBatch;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 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;
-import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
-import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoiceItem;
-import com.ning.billing.invoice.api.InvoiceItemType;
-import com.ning.billing.invoice.model.CreditAdjInvoiceItem;
-import com.ning.billing.invoice.model.CreditBalanceAdjInvoiceItem;
-import com.ning.billing.invoice.model.ExternalChargeInvoiceItem;
-import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
-import com.ning.billing.invoice.model.ItemAdjInvoiceItem;
-import com.ning.billing.invoice.model.RecurringInvoiceItem;
-import com.ning.billing.invoice.model.RefundAdjInvoiceItem;
-import com.ning.billing.invoice.model.RepairAdjInvoiceItem;
-import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.MapperBase;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 
-@ExternalizedSqlViaStringTemplate3()
-@RegisterMapper(InvoiceItemSqlDao.InvoiceItemSqlDaoMapper.class)
-public interface InvoiceItemSqlDao extends EntitySqlDao<InvoiceItem> {
+@EntitySqlDaoStringTemplate
+public interface InvoiceItemSqlDao extends EntitySqlDao<InvoiceItemModelDao, InvoiceItem> {
 
     @SqlQuery
-    List<Long> getRecordIds(@Bind("invoiceId") final String invoiceId,
-                            @InternalTenantContextBinder final InternalTenantContext context);
+    List<InvoiceItemModelDao> getInvoiceItemsByInvoice(@Bind("invoiceId") final String invoiceId,
+                                                       @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    List<InvoiceItem> getInvoiceItemsByInvoice(@Bind("invoiceId") final String invoiceId,
-                                               @InternalTenantContextBinder final InternalTenantContext context);
+    List<InvoiceItemModelDao> getInvoiceItemsByAccount(@Bind("accountId") final String accountId,
+                                                       @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    List<InvoiceItem> getInvoiceItemsByAccount(@Bind("accountId") final String accountId,
-                                               @InternalTenantContextBinder final InternalTenantContext context);
-
-    @SqlQuery
-    List<InvoiceItem> getInvoiceItemsBySubscription(@Bind("subscriptionId") final String subscriptionId,
-                                                    @InternalTenantContextBinder final InternalTenantContext context);
-
-    @Override
-    @SqlUpdate
-    void create(@InvoiceItemBinder final InvoiceItem invoiceItem,
-                @InternalTenantContextBinder final InternalCallContext context);
-
-    @SqlBatch(transactional = false)
-    void batchCreateFromTransaction(@InvoiceItemBinder final List<InvoiceItem> items,
-                                    @InternalTenantContextBinder final InternalCallContext context);
-
-    @BindingAnnotation(InvoiceItemBinder.InvoiceItemBinderFactory.class)
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target({ElementType.PARAMETER})
-    public @interface InvoiceItemBinder {
-
-        public static class InvoiceItemBinderFactory implements BinderFactory {
-
-            @Override
-            public Binder build(final Annotation annotation) {
-                return new Binder<InvoiceItemBinder, InvoiceItem>() {
-                    @Override
-                    public void bind(final SQLStatement<?> q, final InvoiceItemBinder bind, final InvoiceItem item) {
-                        q.bind("id", item.getId().toString());
-                        q.bind("type", item.getInvoiceItemType().toString());
-                        q.bind("invoiceId", item.getInvoiceId().toString());
-                        q.bind("accountId", item.getAccountId().toString());
-                        q.bind("bundleId", item.getBundleId() == null ? null : item.getBundleId().toString());
-                        q.bind("subscriptionId", item.getSubscriptionId() == null ? null : item.getSubscriptionId().toString());
-                        q.bind("planName", item.getPlanName() == null ? null : item.getPlanName());
-                        q.bind("phaseName", item.getPhaseName() == null ? item.getPhaseName() : item.getPhaseName());
-                        q.bind("startDate", item.getStartDate().toDate());
-                        q.bind("endDate", item.getEndDate() == null ? null : item.getEndDate().toDate());
-                        q.bind("amount", item.getAmount());
-                        q.bind("rate", (item.getRate() == null) ? null : item.getRate());
-                        q.bind("currency", item.getCurrency().toString());
-                        q.bind("linkedItemId", (item.getLinkedItemId() == null) ? null : item.getLinkedItemId().toString());
-                    }
-                };
-            }
-        }
-    }
-
-    public static class InvoiceItemSqlDaoMapper extends MapperBase implements ResultSetMapper<InvoiceItem> {
-
-        @Override
-        public InvoiceItem map(final int index, final ResultSet result, final StatementContext context) throws SQLException {
-            final UUID id = getUUID(result, "id");
-            final InvoiceItemType type = InvoiceItemType.valueOf(result.getString("type"));
-            final UUID invoiceId = getUUID(result, "invoice_id");
-            final UUID accountId = getUUID(result, "account_id");
-            final UUID subscriptionId = getUUID(result, "subscription_id");
-            final UUID bundleId = getUUID(result, "bundle_id");
-            final String planName = result.getString("plan_name");
-            final String phaseName = result.getString("phase_name");
-            final LocalDate startDate = getDate(result, "start_date");
-            final LocalDate endDate = getDate(result, "end_date");
-            final BigDecimal amount = result.getBigDecimal("amount");
-            final BigDecimal rate = result.getBigDecimal("rate");
-            final Currency currency = Currency.valueOf(result.getString("currency"));
-            final UUID linkedItemId = getUUID(result, "linked_item_id");
-            final DateTime createdDate = getDateTime(result, "created_date");
-
-            final InvoiceItem item;
-            switch (type) {
-                case EXTERNAL_CHARGE:
-                    item = new ExternalChargeInvoiceItem(id, createdDate, invoiceId, accountId, bundleId, planName, startDate, amount, currency);
-                    break;
-                case FIXED:
-                    item = new FixedPriceInvoiceItem(id, createdDate, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, amount, currency);
-                    break;
-                case RECURRING:
-                    item = new RecurringInvoiceItem(id, createdDate, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, rate, currency);
-                    break;
-                case CBA_ADJ:
-                    item = new CreditBalanceAdjInvoiceItem(id, createdDate, invoiceId, accountId, startDate, linkedItemId, amount, currency);
-                    break;
-                case CREDIT_ADJ:
-                    item = new CreditAdjInvoiceItem(id, createdDate, invoiceId, accountId, startDate, amount, currency);
-                    break;
-                case REFUND_ADJ:
-                    item = new RefundAdjInvoiceItem(id, createdDate, invoiceId, accountId, startDate, amount, currency);
-                    break;
-                case REPAIR_ADJ:
-                    item = new RepairAdjInvoiceItem(id, createdDate, invoiceId, accountId, startDate, endDate, amount, currency, linkedItemId);
-                    break;
-                case ITEM_ADJ:
-                    item = new ItemAdjInvoiceItem(id, createdDate, invoiceId, accountId, startDate, amount, currency, linkedItemId);
-                    break;
-                default:
-                    throw new RuntimeException("Unexpected type of event item " + type);
-            }
-            return item;
-        }
-    }
+    List<InvoiceItemModelDao> getInvoiceItemsBySubscription(@Bind("subscriptionId") final String subscriptionId,
+                                                            @BindBean final InternalTenantContext context);
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceModelDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceModelDao.java
new file mode 100644
index 0000000..f28a0ea
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceModelDao.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2010-2012 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.invoice.dao;
+
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.dao.EntityModelDao;
+
+public class InvoiceModelDao extends EntityBase implements EntityModelDao<Invoice> {
+
+    private UUID accountId;
+    private Integer invoiceNumber;
+    private LocalDate invoiceDate;
+    private LocalDate targetDate;
+    private Currency currency;
+    private boolean migrated;
+
+    // Note in the database, for convenience only
+    private List<InvoiceItemModelDao> invoiceItems;
+    private List<InvoicePaymentModelDao> invoicePayments;
+
+    public InvoiceModelDao() { /* For the DAO mapper */ }
+
+    public InvoiceModelDao(final UUID id, @Nullable final DateTime createdDate, final UUID accountId,
+                           @Nullable final Integer invoiceNumber, final LocalDate invoiceDate, final LocalDate targetDate,
+                           final Currency currency, final boolean migrated) {
+        super(id, createdDate, createdDate);
+        this.accountId = accountId;
+        this.invoiceNumber = invoiceNumber;
+        this.invoiceDate = invoiceDate;
+        this.targetDate = targetDate;
+        this.currency = currency;
+        this.migrated = migrated;
+    }
+
+    public InvoiceModelDao(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency, final boolean migrated) {
+        this(UUID.randomUUID(), null, accountId, null, invoiceDate, targetDate, currency, migrated);
+    }
+
+    public InvoiceModelDao(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency) {
+        this(UUID.randomUUID(), null, accountId, null, invoiceDate, targetDate, currency, false);
+    }
+
+    public InvoiceModelDao(final Invoice invoice) {
+        this(invoice.getId(), invoice.getCreatedDate(), invoice.getAccountId(), invoice.getInvoiceNumber(), invoice.getInvoiceDate(),
+             invoice.getTargetDate(), invoice.getCurrency(), invoice.isMigrationInvoice());
+    }
+
+    public void addInvoiceItems(final List<InvoiceItemModelDao> invoiceItems) {
+        this.invoiceItems = invoiceItems;
+    }
+
+    public List<InvoiceItemModelDao> getInvoiceItems() {
+        return invoiceItems;
+    }
+
+    public void addPayments(final List<InvoicePaymentModelDao> invoicePayments) {
+        this.invoicePayments = invoicePayments;
+    }
+
+    public List<InvoicePaymentModelDao> getInvoicePayments() {
+        return invoicePayments;
+    }
+
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    public Integer getInvoiceNumber() {
+        return invoiceNumber;
+    }
+
+    public LocalDate getInvoiceDate() {
+        return invoiceDate;
+    }
+
+    public LocalDate getTargetDate() {
+        return targetDate;
+    }
+
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    public boolean isMigrated() {
+        return migrated;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("InvoiceModelDao");
+        sb.append("{accountId=").append(accountId);
+        sb.append(", invoiceNumber=").append(invoiceNumber);
+        sb.append(", invoiceDate=").append(invoiceDate);
+        sb.append(", targetDate=").append(targetDate);
+        sb.append(", currency=").append(currency);
+        sb.append(", migrated=").append(migrated);
+        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 InvoiceModelDao that = (InvoiceModelDao) o;
+
+        if (migrated != that.migrated) {
+            return false;
+        }
+        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
+            return false;
+        }
+        if (currency != that.currency) {
+            return false;
+        }
+        if (invoiceDate != null ? !invoiceDate.equals(that.invoiceDate) : that.invoiceDate != null) {
+            return false;
+        }
+        if (invoiceNumber != null ? !invoiceNumber.equals(that.invoiceNumber) : that.invoiceNumber != 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 + (accountId != null ? accountId.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 + (migrated ? 1 : 0);
+        return result;
+    }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.INVOICES;
+    }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceModelDaoHelper.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceModelDaoHelper.java
new file mode 100644
index 0000000..d66c36b
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceModelDaoHelper.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2010-2012 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.invoice.dao;
+
+import java.math.BigDecimal;
+
+import com.ning.billing.invoice.model.InvoiceItemList;
+
+public class InvoiceModelDaoHelper {
+
+    private InvoiceModelDaoHelper() {}
+
+    public static BigDecimal getBalance(final InvoiceModelDao invoiceModelDao) {
+        final InvoiceItemList invoiceItems = new InvoiceItemList(invoiceModelDao.getInvoiceItems());
+        return invoiceItems.getBalance(getPaidAmount(invoiceModelDao));
+    }
+
+    public static BigDecimal getCBAAmount(final InvoiceModelDao invoiceModelDao) {
+        final InvoiceItemList invoiceItems = new InvoiceItemList(invoiceModelDao.getInvoiceItems());
+        return invoiceItems.getCBAAmount();
+    }
+
+    public static BigDecimal getPaidAmount(final InvoiceModelDao invoiceModelDao) {
+        // Compute payments
+        BigDecimal amountPaid = BigDecimal.ZERO;
+        for (final InvoicePaymentModelDao payment : invoiceModelDao.getInvoicePayments()) {
+            if (payment.getAmount() != null) {
+                amountPaid = amountPaid.add(payment.getAmount());
+            }
+        }
+
+        return amountPaid;
+    }
+
+    public static BigDecimal getChargedAmount(final InvoiceModelDao invoiceModelDao) {
+        final InvoiceItemList invoiceItems = new InvoiceItemList(invoiceModelDao.getInvoiceItems());
+        return invoiceItems.getChargedAmount();
+    }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentModelDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentModelDao.java
new file mode 100644
index 0000000..b3462bc
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentModelDao.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2010-2012 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.invoice.dao;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.invoice.api.InvoicePayment.InvoicePaymentType;
+import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.dao.EntityModelDao;
+
+public class InvoicePaymentModelDao extends EntityBase implements EntityModelDao<InvoicePayment> {
+
+    private InvoicePaymentType type;
+    private UUID invoiceId;
+    private UUID paymentId;
+    private DateTime paymentDate;
+    private BigDecimal amount;
+    private Currency currency;
+    private UUID paymentCookieId;
+    private UUID linkedInvoicePaymentId;
+
+    public InvoicePaymentModelDao() { /* For the DAO mapper */ }
+
+    public InvoicePaymentModelDao(final UUID id, final DateTime createdDate, final InvoicePaymentType type, final UUID invoiceId,
+                                  final UUID paymentId, final DateTime paymentDate, final BigDecimal amount, final Currency currency,
+                                  final UUID paymentCookieId, final UUID linkedInvoicePaymentId) {
+        super(id, createdDate, createdDate);
+        this.type = type;
+        this.invoiceId = invoiceId;
+        this.paymentId = paymentId;
+        this.paymentDate = paymentDate;
+        this.amount = amount;
+        this.currency = currency;
+        this.paymentCookieId = paymentCookieId;
+        this.linkedInvoicePaymentId = linkedInvoicePaymentId;
+    }
+
+    public InvoicePaymentModelDao(final InvoicePayment invoicePayment) {
+        this(invoicePayment.getId(), invoicePayment.getCreatedDate(), invoicePayment.getType(), invoicePayment.getInvoiceId(), invoicePayment.getPaymentId(),
+             invoicePayment.getPaymentDate(), invoicePayment.getAmount(), invoicePayment.getCurrency(), invoicePayment.getPaymentCookieId(),
+             invoicePayment.getLinkedInvoicePaymentId());
+    }
+
+    public InvoicePaymentType getType() {
+        return type;
+    }
+
+    public UUID getInvoiceId() {
+        return invoiceId;
+    }
+
+    public UUID getPaymentId() {
+        return paymentId;
+    }
+
+    public DateTime getPaymentDate() {
+        return paymentDate;
+    }
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    public UUID getPaymentCookieId() {
+        return paymentCookieId;
+    }
+
+    public UUID getLinkedInvoicePaymentId() {
+        return linkedInvoicePaymentId;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("InvoicePaymentModelDao");
+        sb.append("{type=").append(type);
+        sb.append(", invoiceId=").append(invoiceId);
+        sb.append(", paymentId=").append(paymentId);
+        sb.append(", paymentDate=").append(paymentDate);
+        sb.append(", amount=").append(amount);
+        sb.append(", currency=").append(currency);
+        sb.append(", paymentCookieId=").append(paymentCookieId);
+        sb.append(", linkedInvoicePaymentId=").append(linkedInvoicePaymentId);
+        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 InvoicePaymentModelDao that = (InvoicePaymentModelDao) o;
+
+        if (amount != null ? !amount.equals(that.amount) : that.amount != null) {
+            return false;
+        }
+        if (currency != that.currency) {
+            return false;
+        }
+        if (invoiceId != null ? !invoiceId.equals(that.invoiceId) : that.invoiceId != null) {
+            return false;
+        }
+        if (linkedInvoicePaymentId != null ? !linkedInvoicePaymentId.equals(that.linkedInvoicePaymentId) : that.linkedInvoicePaymentId != null) {
+            return false;
+        }
+        if (paymentCookieId != null ? !paymentCookieId.equals(that.paymentCookieId) : that.paymentCookieId != null) {
+            return false;
+        }
+        if (paymentDate != null ? !paymentDate.equals(that.paymentDate) : that.paymentDate != null) {
+            return false;
+        }
+        if (paymentId != null ? !paymentId.equals(that.paymentId) : that.paymentId != null) {
+            return false;
+        }
+        if (type != that.type) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (type != null ? type.hashCode() : 0);
+        result = 31 * result + (invoiceId != null ? invoiceId.hashCode() : 0);
+        result = 31 * result + (paymentId != null ? paymentId.hashCode() : 0);
+        result = 31 * result + (paymentDate != null ? paymentDate.hashCode() : 0);
+        result = 31 * result + (amount != null ? amount.hashCode() : 0);
+        result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (paymentCookieId != null ? paymentCookieId.hashCode() : 0);
+        result = 31 * result + (linkedInvoicePaymentId != null ? linkedInvoicePaymentId.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.INVOICE_PAYMENTS;
+    }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
index 0a1c533..4aeacf8 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
@@ -16,150 +16,63 @@
 
 package com.ning.billing.invoice.dao;
 
-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;
 import java.math.BigDecimal;
-import java.sql.ResultSet;
-import java.sql.SQLException;
 import java.util.List;
 import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.skife.jdbi.v2.SQLStatement;
-import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.Binder;
-import org.skife.jdbi.v2.sqlobject.BinderFactory;
-import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlBatch;
 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.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
-import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
-import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoicePayment;
-import com.ning.billing.invoice.api.InvoicePayment.InvoicePaymentType;
-import com.ning.billing.invoice.model.DefaultInvoicePayment;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.AuditSqlDao;
-import com.ning.billing.util.dao.BinderBase;
-import com.ning.billing.util.dao.MapperBase;
 import com.ning.billing.util.dao.UuidMapper;
+import com.ning.billing.util.entity.dao.Audited;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 
-@ExternalizedSqlViaStringTemplate3
-@RegisterMapper(InvoicePaymentSqlDao.InvoicePaymentMapper.class)
-public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePayment>, Transactional<InvoicePaymentSqlDao>, AuditSqlDao, Transmogrifier {
+@EntitySqlDaoStringTemplate
+public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePaymentModelDao, InvoicePayment> {
 
     @SqlQuery
-    List<Long> getRecordIds(@Bind("invoiceId") final String invoiceId,
-                            @InternalTenantContextBinder final InternalTenantContext context);
-
-    @SqlQuery
-    public InvoicePayment getByPaymentId(@Bind("paymentId") final String paymentId,
-                                         @InternalTenantContextBinder final InternalTenantContext context);
-
-    @Override
-    @SqlQuery
-    public List<InvoicePayment> get(@InternalTenantContextBinder final InternalTenantContext context);
-
-    @Override
-    @SqlUpdate
-    public void create(@InvoicePaymentBinder final InvoicePayment invoicePayment,
-                       @InternalTenantContextBinder final InternalCallContext context);
+    public InvoicePaymentModelDao getByPaymentId(@Bind("paymentId") final String paymentId,
+                                                 @BindBean final InternalTenantContext context);
 
     @SqlBatch(transactional = false)
-    void batchCreateFromTransaction(@InvoicePaymentBinder final List<InvoicePayment> items,
-                                    @InternalTenantContextBinder final InternalCallContext context);
+    @Audited(ChangeType.INSERT)
+    void batchCreateFromTransaction(@BindBean final List<InvoicePaymentModelDao> items,
+                                    @BindBean final InternalCallContext context);
 
     @SqlQuery
-    public List<InvoicePayment> getPaymentsForInvoice(@Bind("invoiceId") final String invoiceId,
-                                                      @InternalTenantContextBinder final InternalTenantContext context);
+    public List<InvoicePaymentModelDao> getPaymentsForInvoice(@Bind("invoiceId") final String invoiceId,
+                                                              @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    List<InvoicePayment> getInvoicePayments(@Bind("paymentId") final String paymentId,
-                                            @InternalTenantContextBinder final InternalTenantContext context);
+    List<InvoicePaymentModelDao> getInvoicePayments(@Bind("paymentId") final String paymentId,
+                                                    @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    InvoicePayment getPaymentsForCookieId(@Bind("paymentCookieId") final String paymentCookieId,
-                                          @InternalTenantContextBinder final InternalTenantContext context);
-
-    @SqlUpdate
-    void notifyOfPayment(@InvoicePaymentBinder final InvoicePayment invoicePayment,
-                         @InternalTenantContextBinder final InternalCallContext context);
+    InvoicePaymentModelDao getPaymentsForCookieId(@Bind("paymentCookieId") final String paymentCookieId,
+                                                  @BindBean final InternalTenantContext context);
 
     @SqlQuery
     BigDecimal getRemainingAmountPaid(@Bind("invoicePaymentId") final String invoicePaymentId,
-                                      @InternalTenantContextBinder final InternalTenantContext context);
+                                      @BindBean final InternalTenantContext context);
 
     @SqlQuery
     @RegisterMapper(UuidMapper.class)
     UUID getAccountIdFromInvoicePaymentId(@Bind("invoicePaymentId") final String invoicePaymentId,
-                                          @InternalTenantContextBinder final InternalTenantContext context);
+                                          @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    List<InvoicePayment> getChargeBacksByAccountId(@Bind("accountId") final String accountId,
-                                                   @InternalTenantContextBinder final InternalTenantContext context);
+    List<InvoicePaymentModelDao> getChargeBacksByAccountId(@Bind("accountId") final String accountId,
+                                                           @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    List<InvoicePayment> getChargebacksByPaymentId(@Bind("paymentId") final String paymentId,
-                                                   @InternalTenantContextBinder final InternalTenantContext context);
-
-    public static class InvoicePaymentMapper extends MapperBase implements ResultSetMapper<InvoicePayment> {
-
-        @Override
-        public InvoicePayment map(final int index, final ResultSet result, final StatementContext context) throws SQLException {
-            final UUID id = getUUID(result, "id");
-            final InvoicePaymentType type = InvoicePaymentType.valueOf(result.getString("type"));
-            final UUID paymentId = getUUID(result, "payment_id");
-            final UUID invoiceId = getUUID(result, "invoice_id");
-            final DateTime paymentDate = getDateTime(result, "payment_date");
-            final BigDecimal amount = result.getBigDecimal("amount");
-            final String currencyString = result.getString("currency");
-            final Currency currency = (currencyString == null) ? null : Currency.valueOf(currencyString);
-            final UUID paymentCookieId = getUUID(result, "payment_cookie_id");
-            final UUID linkedInvoicePaymentId = getUUID(result, "linked_invoice_payment_id");
-            final DateTime createdDate = getDateTime(result, "created_date");
-
-            return new DefaultInvoicePayment(id, createdDate, type, paymentId, invoiceId, paymentDate,
-                                             amount, currency, paymentCookieId, linkedInvoicePaymentId);
-        }
-    }
-
-    @BindingAnnotation(InvoicePaymentBinder.InvoicePaymentBinderFactory.class)
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target({ElementType.PARAMETER})
-    public @interface InvoicePaymentBinder {
-
-        public static class InvoicePaymentBinderFactory extends BinderBase implements BinderFactory {
-
-            @Override
-            public Binder build(final Annotation annotation) {
-                return new Binder<InvoicePaymentBinder, InvoicePayment>() {
-                    @Override
-                    public void bind(final SQLStatement q, final InvoicePaymentBinder bind, final InvoicePayment payment) {
-                        q.bind("id", payment.getId().toString());
-                        q.bind("type", payment.getType().toString());
-                        q.bind("invoiceId", payment.getInvoiceId().toString());
-                        q.bind("paymentId", uuidToString(payment.getPaymentId()));
-                        q.bind("paymentDate", payment.getPaymentDate().toDate());
-                        q.bind("amount", payment.getAmount());
-                        final Currency currency = payment.getCurrency();
-                        q.bind("currency", (currency == null) ? null : currency.toString());
-                        q.bind("paymentCookieId", uuidToString(payment.getPaymentCookieId()));
-                        q.bind("linkedInvoicePaymentId", uuidToString(payment.getLinkedInvoicePaymentId()));
-                    }
-                };
-            }
-        }
-    }
+    List<InvoicePaymentModelDao> getChargebacksByPaymentId(@Bind("paymentId") final String paymentId,
+                                                           @BindBean final InternalTenantContext context);
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
index 6358101..943f909 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
@@ -16,115 +16,41 @@
 
 package com.ning.billing.invoice.dao;
 
-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;
-import java.sql.ResultSet;
-import java.sql.SQLException;
 import java.util.Date;
 import java.util.List;
 import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.joda.time.LocalDate;
-import org.skife.jdbi.v2.SQLStatement;
-import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.Binder;
-import org.skife.jdbi.v2.sqlobject.BinderFactory;
-import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 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.mixins.CloseMe;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
-import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
-import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.invoice.model.DefaultInvoice;
-import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.AuditSqlDao;
-import com.ning.billing.util.dao.MapperBase;
-import com.ning.billing.util.dao.UuidMapper;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 
-@ExternalizedSqlViaStringTemplate3()
-@RegisterMapper(InvoiceSqlDao.InvoiceMapper.class)
-public interface InvoiceSqlDao extends EntitySqlDao<Invoice>, AuditSqlDao, Transactional<InvoiceSqlDao>, Transmogrifier, CloseMe {
-
-    @Override
-    @SqlUpdate
-    void create(@InvoiceBinder Invoice invoice,
-                @InternalTenantContextBinder final InternalCallContext context);
+@EntitySqlDaoStringTemplate
+public interface InvoiceSqlDao extends EntitySqlDao<InvoiceModelDao, Invoice> {
 
     @SqlQuery
-    List<Invoice> getInvoicesByAccount(@Bind("accountId") final String accountId,
-                                       @InternalTenantContextBinder final InternalTenantContext context);
+    List<InvoiceModelDao> getInvoicesByAccount(@Bind("accountId") final String accountId,
+                                               @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    List<Invoice> getAllInvoicesByAccount(@Bind("accountId") final String string,
-                                          @InternalTenantContextBinder final InternalTenantContext context);
+    List<InvoiceModelDao> getAllInvoicesByAccount(@Bind("accountId") final String string,
+                                                  @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    List<Invoice> getInvoicesByAccountAfterDate(@Bind("accountId") final String accountId,
-                                                @Bind("fromDate") final Date fromDate,
-                                                @InternalTenantContextBinder final InternalTenantContext context);
+    List<InvoiceModelDao> getInvoicesByAccountAfterDate(@Bind("accountId") final String accountId,
+                                                        @Bind("fromDate") final Date fromDate,
+                                                        @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    List<Invoice> getInvoicesBySubscription(@Bind("subscriptionId") final String subscriptionId,
-                                            @InternalTenantContextBinder final InternalTenantContext context);
+    List<InvoiceModelDao> getInvoicesBySubscription(@Bind("subscriptionId") final String subscriptionId,
+                                                    @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    @RegisterMapper(UuidMapper.class)
     UUID getInvoiceIdByPaymentId(@Bind("paymentId") final String paymentId,
-                                 @InternalTenantContextBinder final InternalTenantContext context);
-
-    @BindingAnnotation(InvoiceBinder.InvoiceBinderFactory.class)
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target({ElementType.PARAMETER})
-    public @interface InvoiceBinder {
-
-        public static class InvoiceBinderFactory implements BinderFactory {
-
-            @Override
-            public Binder<InvoiceBinder, Invoice> build(final Annotation annotation) {
-                return new Binder<InvoiceBinder, Invoice>() {
-                    @Override
-                    public void bind(@SuppressWarnings("rawtypes") final SQLStatement q, final InvoiceBinder bind, final Invoice invoice) {
-                        q.bind("id", invoice.getId().toString());
-                        q.bind("accountId", invoice.getAccountId().toString());
-                        q.bind("invoiceDate", invoice.getInvoiceDate().toDate());
-                        q.bind("targetDate", invoice.getTargetDate().toDate());
-                        q.bind("currency", invoice.getCurrency().toString());
-                        q.bind("migrated", invoice.isMigrationInvoice());
-                    }
-                };
-            }
-        }
-    }
-
-    public static class InvoiceMapper extends MapperBase implements ResultSetMapper<Invoice> {
-
-        @Override
-        public Invoice map(final int index, final ResultSet result, final StatementContext context) throws SQLException {
-            final UUID id = UUID.fromString(result.getString("id"));
-            final UUID accountId = UUID.fromString(result.getString("account_id"));
-            final int invoiceNumber = result.getInt("invoice_number");
-            final LocalDate invoiceDate = getDate(result, "invoice_date");
-            final LocalDate targetDate = getDate(result, "target_date");
-            final Currency currency = Currency.valueOf(result.getString("currency"));
-            final boolean isMigrationInvoice = result.getBoolean("migrated");
-            final DateTime createdDate = getDateTime(result, "created_date");
-
-            return new DefaultInvoice(id, createdDate, accountId, invoiceNumber, invoiceDate, targetDate, currency, isMigrationInvoice);
-        }
-    }
+                                 @BindBean final InternalTenantContext context);
 }
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/generator/DefaultInvoiceGenerator.java b/invoice/src/main/java/com/ning/billing/invoice/generator/DefaultInvoiceGenerator.java
index 06cf2e0..af67913 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/generator/DefaultInvoiceGenerator.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/generator/DefaultInvoiceGenerator.java
@@ -37,7 +37,6 @@ import org.slf4j.LoggerFactory;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.util.config.InvoiceConfig;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoiceItem;
@@ -53,6 +52,7 @@ import com.ning.billing.invoice.model.RecurringInvoiceItem;
 import com.ning.billing.invoice.model.RecurringInvoiceItemData;
 import com.ning.billing.invoice.model.RepairAdjInvoiceItem;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.config.InvoiceConfig;
 import com.ning.billing.util.svcapi.junction.BillingEvent;
 import com.ning.billing.util.svcapi.junction.BillingEventSet;
 import com.ning.billing.util.svcapi.junction.BillingModeType;
@@ -182,21 +182,16 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
     //
     BigDecimal getAdjustedPositiveAmount(final List<InvoiceItem> existingItems, final UUID linkedItemId) {
         BigDecimal totalAdjustedOnItem = BigDecimal.ZERO;
-        final Collection<InvoiceItem> c = Collections2.filter(existingItems, new Predicate<InvoiceItem>() {
+        final Collection<InvoiceItem> invoiceItems = Collections2.filter(existingItems, new Predicate<InvoiceItem>() {
             @Override
-            public boolean apply(InvoiceItem item) {
-                if (item.getInvoiceItemType() == InvoiceItemType.ITEM_ADJ &&
-                    item.getLinkedItemId() != null && item.getLinkedItemId().equals(linkedItemId)) {
-                    return true;
-                } else {
-                    return false;
-                }
+            public boolean apply(final InvoiceItem item) {
+                return item.getInvoiceItemType() == InvoiceItemType.ITEM_ADJ &&
+                       item.getLinkedItemId() != null && item.getLinkedItemId().equals(linkedItemId);
             }
         });
 
-        final Iterator<InvoiceItem> it = c.iterator();
-        while (it.hasNext()) {
-            totalAdjustedOnItem = totalAdjustedOnItem.add(it.next().getAmount());
+        for (final InvoiceItem invoiceItem : invoiceItems) {
+            totalAdjustedOnItem = totalAdjustedOnItem.add(invoiceItem.getAmount());
         }
         return totalAdjustedOnItem.negate();
     }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/generator/InvoiceDateUtils.java b/invoice/src/main/java/com/ning/billing/invoice/generator/InvoiceDateUtils.java
index e9f14b9..0a0aeb9 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/generator/InvoiceDateUtils.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/generator/InvoiceDateUtils.java
@@ -156,7 +156,7 @@ public class InvoiceDateUtils {
         return new LocalDate(proposedDateTime, accountTimeZone);
     }
 
-    public static DateTime calculateBillingCycleDateOnOrAfter(final DateTime date, final int billingCycleDayLocal) {
+    private static DateTime calculateBillingCycleDateOnOrAfter(final DateTime date, final int billingCycleDayLocal) {
         final int lastDayOfMonth = date.dayOfMonth().getMaximumValue();
 
         final MutableDateTime tmp = date.toMutableDateTime();
@@ -173,7 +173,7 @@ public class InvoiceDateUtils {
         return proposedDate;
     }
 
-    public static DateTime calculateBillingCycleDateAfter(final DateTime date, final int billingCycleDayLocal) {
+    private static DateTime calculateBillingCycleDateAfter(final DateTime date, final int billingCycleDayLocal) {
         DateTime proposedDate = calculateBillingCycleDateOnOrAfter(date, billingCycleDayLocal);
         if (date.compareTo(proposedDate) == 0) {
             proposedDate = proposedDate.plusMonths(1);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java b/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java
index a163515..6651d23 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java
@@ -18,7 +18,6 @@ package com.ning.billing.invoice.glue;
 
 import org.skife.config.ConfigurationObjectFactory;
 
-import com.ning.billing.util.config.InvoiceConfig;
 import com.ning.billing.glue.InvoiceModule;
 import com.ning.billing.invoice.InvoiceListener;
 import com.ning.billing.invoice.TagHandler;
@@ -33,7 +32,7 @@ import com.ning.billing.invoice.api.invoice.DefaultInvoicePaymentApi;
 import com.ning.billing.invoice.api.migration.DefaultInvoiceMigrationApi;
 import com.ning.billing.invoice.api.svcs.DefaultInvoiceInternalApi;
 import com.ning.billing.invoice.api.user.DefaultInvoiceUserApi;
-import com.ning.billing.invoice.dao.AuditedInvoiceDao;
+import com.ning.billing.invoice.dao.DefaultInvoiceDao;
 import com.ning.billing.invoice.dao.InvoiceDao;
 import com.ning.billing.invoice.generator.DefaultInvoiceGenerator;
 import com.ning.billing.invoice.generator.InvoiceGenerator;
@@ -43,6 +42,7 @@ import com.ning.billing.invoice.notification.EmailInvoiceNotifier;
 import com.ning.billing.invoice.notification.NextBillingDateNotifier;
 import com.ning.billing.invoice.notification.NextBillingDatePoster;
 import com.ning.billing.invoice.notification.NullInvoiceNotifier;
+import com.ning.billing.util.config.InvoiceConfig;
 import com.ning.billing.util.svcapi.invoice.InvoiceInternalApi;
 import com.ning.billing.util.template.translation.TranslatorConfig;
 
@@ -53,7 +53,7 @@ public class DefaultInvoiceModule extends AbstractModule implements InvoiceModul
     InvoiceConfig config;
 
     protected void installInvoiceDao() {
-        bind(InvoiceDao.class).to(AuditedInvoiceDao.class).asEagerSingleton();
+        bind(InvoiceDao.class).to(DefaultInvoiceDao.class).asEagerSingleton();
     }
 
     @Override
@@ -109,7 +109,6 @@ public class DefaultInvoiceModule extends AbstractModule implements InvoiceModul
         bind(TagHandler.class).asEagerSingleton();
     }
 
-
     protected void installInvoiceGenerator() {
         bind(InvoiceGenerator.class).to(DefaultInvoiceGenerator.class).asEagerSingleton();
     }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
index 47b5edb..b7aefc8 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
@@ -39,15 +39,19 @@ 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.InvoiceNotifier;
+import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.invoice.api.user.DefaultInvoiceCreationEvent;
 import com.ning.billing.invoice.api.user.DefaultNullInvoiceEvent;
 import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.invoice.dao.InvoiceItemModelDao;
+import com.ning.billing.invoice.dao.InvoiceModelDao;
+import com.ning.billing.invoice.dao.InvoicePaymentModelDao;
 import com.ning.billing.invoice.generator.InvoiceDateUtils;
 import com.ning.billing.invoice.generator.InvoiceGenerator;
+import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
 import com.ning.billing.util.callcontext.InternalCallContext;
-import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.events.BusInternalEvent;
 import com.ning.billing.util.events.EffectiveSubscriptionInternalEvent;
@@ -63,8 +67,10 @@ import com.ning.billing.util.svcapi.junction.BillingInternalApi;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
 import com.ning.billing.util.svcsapi.bus.InternalBus.EventBusException;
 
+import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
 public class InvoiceDispatcher {
@@ -81,7 +87,6 @@ public class InvoiceDispatcher {
     private final GlobalLocker locker;
     private final InternalBus eventBus;
     private final Clock clock;
-    private final InternalCallContextFactory internalCallContextFactory;
 
     @Inject
     public InvoiceDispatcher(final InvoiceGenerator generator, final AccountInternalApi accountApi,
@@ -91,8 +96,7 @@ public class InvoiceDispatcher {
                              final InvoiceNotifier invoiceNotifier,
                              final GlobalLocker locker,
                              final InternalBus eventBus,
-                             final Clock clock,
-                             final InternalCallContextFactory internalCallContextFactory) {
+                             final Clock clock) {
         this.generator = generator;
         this.billingApi = billingApi;
         this.entitlementApi = entitlementApi;
@@ -102,7 +106,6 @@ public class InvoiceDispatcher {
         this.locker = locker;
         this.eventBus = eventBus;
         this.clock = clock;
-        this.internalCallContextFactory = internalCallContextFactory;
     }
 
     public void processSubscription(final EffectiveSubscriptionInternalEvent transition,
@@ -153,10 +156,16 @@ public class InvoiceDispatcher {
             // Make sure to first set the BCD if needed then get the account object (to have the BCD set)
             final BillingEventSet billingEvents = billingApi.getBillingEventsForAccountAndUpdateAccountBCD(accountId, context);
 
-            final Account account = accountApi.getAccountById(accountId,  context);
+            final Account account = accountApi.getAccountById(accountId, context);
             List<Invoice> invoices = new ArrayList<Invoice>();
             if (!billingEvents.isAccountAutoInvoiceOff()) {
-                invoices = invoiceDao.getInvoicesByAccount(accountId, context); //no need to fetch, invoicing is off on this account
+                invoices = ImmutableList.<Invoice>copyOf(Collections2.transform(invoiceDao.getInvoicesByAccount(accountId, context),
+                                                                                new Function<InvoiceModelDao, Invoice>() {
+                                                                                    @Override
+                                                                                    public Invoice apply(final InvoiceModelDao input) {
+                                                                                        return new DefaultInvoice(input);
+                                                                                    }
+                                                                                })); //no need to fetch, invoicing is off on this account
             }
 
             final Currency targetCurrency = account.getCurrency();
@@ -169,7 +178,7 @@ public class InvoiceDispatcher {
                 log.info("Generated null invoice.");
                 if (!dryRun) {
                     final BusInternalEvent event = new DefaultNullInvoiceEvent(accountId, clock.getUTCToday(), context.getUserToken(),
-                            context.getAccountRecordId(), context.getTenantRecordId());
+                                                                               context.getAccountRecordId(), context.getTenantRecordId());
                     postEvent(event, accountId, context);
                 }
             } else {
@@ -178,22 +187,38 @@ public class InvoiceDispatcher {
                     // We need to check whether this is just a 'shell' invoice or a real invoice with items on it
                     final boolean isRealInvoiceWithItems = Collections2.filter(invoice.getInvoiceItems(), new Predicate<InvoiceItem>() {
                         @Override
-                        public boolean apply(InvoiceItem input) {
+                        public boolean apply(final InvoiceItem input) {
                             return input.getInvoiceId().equals(invoice.getId());
                         }
                     }).size() > 0;
 
-                    invoiceDao.create(invoice, account.getBillCycleDay().getDayOfMonthUTC(), isRealInvoiceWithItems, context);
+                    final InvoiceModelDao invoiceModelDao = new InvoiceModelDao(invoice);
+                    final List<InvoiceItemModelDao> invoiceItemModelDaos = ImmutableList.<InvoiceItemModelDao>copyOf(Collections2.transform(invoice.getInvoiceItems(),
+                                                                                                                                            new Function<InvoiceItem, InvoiceItemModelDao>() {
+                                                                                                                                                @Override
+                                                                                                                                                public InvoiceItemModelDao apply(final InvoiceItem input) {
+                                                                                                                                                    return new InvoiceItemModelDao(input);
+                                                                                                                                                }
+                                                                                                                                            }));
+                    // Not really needed, there shouldn't be any payment at this stage
+                    final List<InvoicePaymentModelDao> invoicePaymentModelDaos = ImmutableList.<InvoicePaymentModelDao>copyOf(Collections2.transform(invoice.getPayments(),
+                                                                                                                                                     new Function<InvoicePayment, InvoicePaymentModelDao>() {
+                                                                                                                                                         @Override
+                                                                                                                                                         public InvoicePaymentModelDao apply(final InvoicePayment input) {
+                                                                                                                                                             return new InvoicePaymentModelDao(input);
+                                                                                                                                                         }
+                                                                                                                                                     }));
+                    invoiceDao.createInvoice(invoiceModelDao, invoiceItemModelDaos, invoicePaymentModelDaos, isRealInvoiceWithItems, context);
 
                     final List<InvoiceItem> fixedPriceInvoiceItems = invoice.getInvoiceItems(FixedPriceInvoiceItem.class);
                     final List<InvoiceItem> recurringInvoiceItems = invoice.getInvoiceItems(RecurringInvoiceItem.class);
                     setChargedThroughDates(account.getBillCycleDay(), account.getTimeZone(), fixedPriceInvoiceItems, recurringInvoiceItems, context);
 
                     final InvoiceCreationInternalEvent event = new DefaultInvoiceCreationEvent(invoice.getId(), invoice.getAccountId(),
-                                                                                       invoice.getBalance(), invoice.getCurrency(),
-                                                                                       context.getUserToken(),
-                                                                                       context.getAccountRecordId(),
-                                                                                       context.getTenantRecordId());
+                                                                                               invoice.getBalance(), invoice.getCurrency(),
+                                                                                               context.getUserToken(),
+                                                                                               context.getAccountRecordId(),
+                                                                                               context.getTenantRecordId());
 
                     if (isRealInvoiceWithItems) {
                         postEvent(event, accountId, context);
@@ -204,7 +229,7 @@ public class InvoiceDispatcher {
             if (account.isNotifiedForInvoices() && invoice != null && !dryRun) {
                 // Need to re-hydrate the invoice object to get the invoice number (record id)
                 // API_FIX InvoiceNotifier public API?
-                invoiceNotifier.notify(account, invoiceDao.getById(invoice.getId(), context), context.toTenantContext());
+                invoiceNotifier.notify(account, new DefaultInvoice(invoiceDao.getById(invoice.getId(), context)), context.toTenantContext());
             }
 
             return invoice;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
index e101e47..25bb819 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
@@ -66,7 +66,7 @@ public class InvoiceListener {
                 || transition.getRemainingEventsForUserOperation() > 0) {
                 return;
             }
-            final InternalCallContext context = internalCallContextFactory.createInternalCallContext(transition.getTenantRecordId(), transition.getAccountRecordId(), "Transition", CallOrigin.INTERNAL, UserType.SYSTEM, transition.getUserToken());
+            final InternalCallContext context = internalCallContextFactory.createInternalCallContext(transition.getTenantRecordId(), transition.getAccountRecordId(), "SubscriptionTransition", CallOrigin.INTERNAL, UserType.SYSTEM, transition.getUserToken());
             dispatcher.processSubscription(transition, context);
         } catch (InvoiceApiException e) {
             log.error(e.getMessage());
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/AdjInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/AdjInvoiceItem.java
index c191bca..58d372c 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/AdjInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/AdjInvoiceItem.java
@@ -30,13 +30,13 @@ import com.ning.billing.invoice.api.InvoiceItemType;
 
 public abstract class AdjInvoiceItem extends InvoiceItemBase {
 
-    public AdjInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId,
-                          final LocalDate startDate, final LocalDate endDate, final BigDecimal amount, final Currency currency) {
+    AdjInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId,
+                   final LocalDate startDate, final LocalDate endDate, final BigDecimal amount, final Currency currency) {
         this(id, createdDate, invoiceId, accountId, startDate, endDate, amount, currency, null);
     }
 
-    public AdjInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId,
-                          final LocalDate startDate, final LocalDate endDate, final BigDecimal amount, final Currency currency, @Nullable final UUID reversingId) {
+    AdjInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId,
+                   final LocalDate startDate, final LocalDate endDate, final BigDecimal amount, final Currency currency, @Nullable final UUID reversingId) {
         super(id, createdDate, invoiceId, accountId, null, null, null, null, startDate, endDate, amount, currency, reversingId);
     }
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/CreditBalanceAdjInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/CreditBalanceAdjInvoiceItem.java
index e8db0c7..2e12d79 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/CreditBalanceAdjInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/CreditBalanceAdjInvoiceItem.java
@@ -34,11 +34,6 @@ public class CreditBalanceAdjInvoiceItem extends AdjInvoiceItem {
         this(UUID.randomUUID(), null, invoiceId, accountId, date, null, amount, currency);
     }
 
-    public CreditBalanceAdjInvoiceItem(final UUID invoiceId, final UUID accountId, final LocalDate date, @Nullable final UUID linkedInvoiceItemId,
-                                       final BigDecimal amount, final Currency currency) {
-        this(UUID.randomUUID(), null, invoiceId, accountId, date, linkedInvoiceItemId, amount, currency);
-    }
-
     public CreditBalanceAdjInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId,
                                        final LocalDate date, final UUID linkedInvoiceItemId,
                                        final BigDecimal amount, final Currency currency) {
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
index 8231cf3..cf3a349 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
@@ -18,6 +18,7 @@ package com.ning.billing.invoice.model;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.UUID;
 
@@ -30,8 +31,14 @@ import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.invoice.dao.InvoiceItemModelDao;
+import com.ning.billing.invoice.dao.InvoiceModelDao;
+import com.ning.billing.invoice.dao.InvoicePaymentModelDao;
 import com.ning.billing.util.entity.EntityBase;
 
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+
 public class DefaultInvoice extends EntityBase implements Invoice {
 
     private final InvoiceItemList invoiceItems = new InvoiceItemList();
@@ -54,9 +61,9 @@ public class DefaultInvoice extends EntityBase implements Invoice {
     }
 
     // Used to hydrate invoice from persistence layer
-    public DefaultInvoice(final UUID invoiceId, @Nullable final DateTime createdDate, final UUID accountId,
-                          @Nullable final Integer invoiceNumber, final LocalDate invoiceDate,
-                          final LocalDate targetDate, final Currency currency, final boolean isMigrationInvoice) {
+    private DefaultInvoice(final UUID invoiceId, @Nullable final DateTime createdDate, final UUID accountId,
+                           @Nullable final Integer invoiceNumber, final LocalDate invoiceDate,
+                           final LocalDate targetDate, final Currency currency, final boolean isMigrationInvoice) {
         super(invoiceId, createdDate, createdDate);
         this.accountId = accountId;
         this.invoiceNumber = invoiceNumber;
@@ -66,13 +73,31 @@ public class DefaultInvoice extends EntityBase implements Invoice {
         this.migrationInvoice = isMigrationInvoice;
     }
 
+    public DefaultInvoice(final InvoiceModelDao invoiceModelDao) {
+        this(invoiceModelDao.getId(), invoiceModelDao.getCreatedDate(), invoiceModelDao.getAccountId(),
+             invoiceModelDao.getInvoiceNumber(), invoiceModelDao.getInvoiceDate(), invoiceModelDao.getTargetDate(),
+             invoiceModelDao.getCurrency(), invoiceModelDao.isMigrated());
+        addInvoiceItems(Collections2.transform(invoiceModelDao.getInvoiceItems(), new Function<InvoiceItemModelDao, InvoiceItem>() {
+            @Override
+            public InvoiceItem apply(final InvoiceItemModelDao input) {
+                return InvoiceItemFactory.fromModelDao(input);
+            }
+        }));
+        addPayments(Collections2.transform(invoiceModelDao.getInvoicePayments(), new Function<InvoicePaymentModelDao, InvoicePayment>() {
+            @Override
+            public InvoicePayment apply(final InvoicePaymentModelDao input) {
+                return new DefaultInvoicePayment(input);
+            }
+        }));
+    }
+
     @Override
     public boolean addInvoiceItem(final InvoiceItem item) {
         return invoiceItems.add(item);
     }
 
     @Override
-    public boolean addInvoiceItems(final List<InvoiceItem> items) {
+    public boolean addInvoiceItems(final Collection<InvoiceItem> items) {
         return this.invoiceItems.addAll(items);
     }
 
@@ -103,7 +128,7 @@ public class DefaultInvoice extends EntityBase implements Invoice {
     }
 
     @Override
-    public boolean addPayments(final List<InvoicePayment> payments) {
+    public boolean addPayments(final Collection<InvoicePayment> payments) {
         return this.payments.addAll(payments);
     }
 
@@ -190,7 +215,7 @@ public class DefaultInvoice extends EntityBase implements Invoice {
 
     @Override
     public BigDecimal getBalance() {
-        return getChargedAmount().add(getTotalAdjAmount()).add(getCBAAmount()).subtract(getPaidAmount());
+        return invoiceItems.getBalance(getPaidAmount());
     }
 
     @Override
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoicePayment.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoicePayment.java
index 2805276..ff8c157 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoicePayment.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoicePayment.java
@@ -25,6 +25,7 @@ import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.invoice.dao.InvoicePaymentModelDao;
 import com.ning.billing.util.entity.EntityBase;
 
 public class DefaultInvoicePayment extends EntityBase implements InvoicePayment {
@@ -63,6 +64,13 @@ public class DefaultInvoicePayment extends EntityBase implements InvoicePayment 
         this.linkedInvoicePaymentId = linkedInvoicePaymentId;
     }
 
+    public DefaultInvoicePayment(final InvoicePaymentModelDao invoicePaymentModelDao) {
+        this(invoicePaymentModelDao.getId(), invoicePaymentModelDao.getCreatedDate(), invoicePaymentModelDao.getType(),
+             invoicePaymentModelDao.getPaymentId(), invoicePaymentModelDao.getInvoiceId(), invoicePaymentModelDao.getPaymentDate(),
+             invoicePaymentModelDao.getAmount(), invoicePaymentModelDao.getCurrency(), invoicePaymentModelDao.getPaymentCookieId(),
+             invoicePaymentModelDao.getLinkedInvoicePaymentId());
+    }
+
     @Override
     public InvoicePaymentType getType() {
         return type;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/ExternalChargeInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/ExternalChargeInvoiceItem.java
index f46c29d..3763053 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/ExternalChargeInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/ExternalChargeInvoiceItem.java
@@ -42,7 +42,7 @@ public class ExternalChargeInvoiceItem extends InvoiceItemBase {
 
     public ExternalChargeInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, @Nullable final UUID bundleId,
                                      @Nullable final String description, final LocalDate date, final BigDecimal amount, final Currency currency) {
-        super(id, createdDate, invoiceId, accountId, bundleId, (UUID) null, description, null, date, null, amount, currency);
+        super(id, createdDate, invoiceId, accountId, bundleId, null, description, null, date, null, amount, currency);
     }
 
     @Override
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemFactory.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemFactory.java
new file mode 100644
index 0000000..a8b3708
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemFactory.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2010-2012 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.invoice.model;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
+import com.ning.billing.invoice.dao.InvoiceItemModelDao;
+
+public class InvoiceItemFactory {
+
+    private InvoiceItemFactory() {}
+
+    public static InvoiceItem fromModelDao(final InvoiceItemModelDao invoiceItemModelDao) {
+        if (invoiceItemModelDao == null) {
+            return null;
+        }
+
+        final UUID id = invoiceItemModelDao.getId();
+        final DateTime createdDate = invoiceItemModelDao.getCreatedDate();
+        final UUID invoiceId = invoiceItemModelDao.getInvoiceId();
+        final UUID accountId = invoiceItemModelDao.getAccountId();
+        final UUID bundleId = invoiceItemModelDao.getBundleId();
+        final UUID subscriptionId = invoiceItemModelDao.getSubscriptionId();
+        final String planName = invoiceItemModelDao.getPlanName();
+        final String phaseName = invoiceItemModelDao.getPhaseName();
+        final LocalDate startDate = invoiceItemModelDao.getStartDate();
+        final LocalDate endDate = invoiceItemModelDao.getEndDate();
+        final BigDecimal amount = invoiceItemModelDao.getAmount();
+        final BigDecimal rate = invoiceItemModelDao.getRate();
+        final Currency currency = invoiceItemModelDao.getCurrency();
+        final UUID linkedItemId = invoiceItemModelDao.getLinkedItemId();
+
+        final InvoiceItem item;
+        final InvoiceItemType type = invoiceItemModelDao.getType();
+        switch (type) {
+            case EXTERNAL_CHARGE:
+                item = new ExternalChargeInvoiceItem(id, createdDate, invoiceId, accountId, bundleId, planName, startDate, amount, currency);
+                break;
+            case FIXED:
+                item = new FixedPriceInvoiceItem(id, createdDate, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, amount, currency);
+                break;
+            case RECURRING:
+                item = new RecurringInvoiceItem(id, createdDate, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, rate, currency);
+                break;
+            case CBA_ADJ:
+                item = new CreditBalanceAdjInvoiceItem(id, createdDate, invoiceId, accountId, startDate, linkedItemId, amount, currency);
+                break;
+            case CREDIT_ADJ:
+                item = new CreditAdjInvoiceItem(id, createdDate, invoiceId, accountId, startDate, amount, currency);
+                break;
+            case REFUND_ADJ:
+                item = new RefundAdjInvoiceItem(id, createdDate, invoiceId, accountId, startDate, amount, currency);
+                break;
+            case REPAIR_ADJ:
+                item = new RepairAdjInvoiceItem(id, createdDate, invoiceId, accountId, startDate, endDate, amount, currency, linkedItemId);
+                break;
+            case ITEM_ADJ:
+                item = new ItemAdjInvoiceItem(id, createdDate, invoiceId, accountId, startDate, amount, currency, linkedItemId);
+                break;
+            default:
+                throw new RuntimeException("Unexpected type of event item " + type);
+        }
+
+        return item;
+    }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java
index e505d9e..a46bc37 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java
@@ -18,11 +18,11 @@ package com.ning.billing.invoice.model;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoiceItemType;
+import com.ning.billing.invoice.dao.InvoiceItemModelDao;
 
 public class InvoiceItemList extends ArrayList<InvoiceItem> {
 
@@ -35,9 +35,15 @@ public class InvoiceItemList extends ArrayList<InvoiceItem> {
         super();
     }
 
-    public InvoiceItemList(final List<InvoiceItem> invoiceItems) {
+    public InvoiceItemList(final List<InvoiceItemModelDao> invoiceItems) {
         super();
-        this.addAll(invoiceItems);
+        for (final InvoiceItemModelDao invoiceItemModelDao : invoiceItems) {
+            this.add(InvoiceItemFactory.fromModelDao(invoiceItemModelDao));
+        }
+    }
+
+    public BigDecimal getBalance(final BigDecimal paidAmount) {
+        return getChargedAmount().add(getTotalAdjAmount()).add(getCBAAmount()).subtract(paidAmount);
     }
 
     public BigDecimal getTotalAdjAmount() {
@@ -60,8 +66,7 @@ public class InvoiceItemList extends ArrayList<InvoiceItem> {
         return getAmoutForItems(InvoiceItemType.CBA_ADJ);
     }
 
-
-    private BigDecimal getAmoutForItems(InvoiceItemType...types) {
+    private BigDecimal getAmoutForItems(final InvoiceItemType... types) {
         BigDecimal total = BigDecimal.ZERO.setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
         for (final InvoiceItem item : this) {
             if (isFromType(item, types)) {
@@ -73,8 +78,8 @@ public class InvoiceItemList extends ArrayList<InvoiceItem> {
         return total.setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
     }
 
-    private boolean isFromType(InvoiceItem item, InvoiceItemType...types) {
-        for (InvoiceItemType cur : types) {
+    private boolean isFromType(final InvoiceItem item, final InvoiceItemType... types) {
+        for (final InvoiceItemType cur : types) {
             if (item.getInvoiceItemType() == cur) {
                 return true;
             }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoicingConfiguration.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoicingConfiguration.java
index 1651c13..9362592 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InvoicingConfiguration.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoicingConfiguration.java
@@ -19,6 +19,7 @@ package com.ning.billing.invoice.model;
 import java.math.BigDecimal;
 
 public class InvoicingConfiguration {
+
     private static final int roundingMethod = BigDecimal.ROUND_HALF_UP;
     private static final int numberOfDecimals = 2;
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/MigrationInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/MigrationInvoiceItem.java
index 013eb92..e02ee65 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/MigrationInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/MigrationInvoiceItem.java
@@ -25,6 +25,7 @@ import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.api.MigrationPlan;
 
 public class MigrationInvoiceItem extends FixedPriceInvoiceItem {
+
     public MigrationInvoiceItem(final UUID invoiceId, final UUID accountId, final LocalDate startDate,
                                 final BigDecimal amount, final Currency currency) {
         super(invoiceId, accountId, null, null, MigrationPlan.MIGRATION_PLAN_NAME, MigrationPlan.MIGRATION_PLAN_PHASE_NAME,
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItemData.java b/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItemData.java
index 90ae6be..0442866 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItemData.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItemData.java
@@ -57,15 +57,24 @@ public class RecurringInvoiceItemData {
 
     @Override
     public boolean equals(final Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
 
         final RecurringInvoiceItemData that = (RecurringInvoiceItemData) o;
 
-        if (endDate != null ? !endDate.equals(that.endDate) : that.endDate != null) return false;
-        if (numberOfCycles != null ? !numberOfCycles.equals(that.numberOfCycles) : that.numberOfCycles != null)
+        if (endDate != null ? !endDate.equals(that.endDate) : that.endDate != null) {
+            return false;
+        }
+        if (numberOfCycles != null ? !numberOfCycles.equals(that.numberOfCycles) : that.numberOfCycles != null) {
+            return false;
+        }
+        if (startDate != null ? !startDate.equals(that.startDate) : that.startDate != null) {
             return false;
-        if (startDate != null ? !startDate.equals(that.startDate) : that.startDate != null) return false;
+        }
 
         return true;
     }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDateNotifier.java b/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDateNotifier.java
index 13cca7a..6e3a8d7 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDateNotifier.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDateNotifier.java
@@ -22,13 +22,13 @@ import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.ning.billing.util.config.InvoiceConfig;
-import com.ning.billing.util.config.NotificationConfig;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.invoice.InvoiceListener;
 import com.ning.billing.invoice.api.DefaultInvoiceService;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.config.InvoiceConfig;
+import com.ning.billing.util.config.NotificationConfig;
 import com.ning.billing.util.notificationq.NotificationKey;
 import com.ning.billing.util.notificationq.NotificationQueue;
 import com.ning.billing.util.notificationq.NotificationQueueService;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java b/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java
index 54448e3..4e8faf6 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java
@@ -20,7 +20,6 @@ import java.io.IOException;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -29,6 +28,8 @@ 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.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.notificationq.NotificationQueue;
 import com.ning.billing.util.notificationq.NotificationQueueService;
 import com.ning.billing.util.notificationq.NotificationQueueService.NoSuchNotificationQueue;
@@ -50,7 +51,7 @@ public class DefaultNextBillingDatePoster implements NextBillingDatePoster {
     }
 
     @Override
-    public void insertNextBillingNotification(final Transmogrifier transactionalDao, final UUID accountId,
+    public void insertNextBillingNotification(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID accountId,
                                               final UUID subscriptionId, final DateTime futureNotificationTime) {
         final InternalCallContext context = createCallContext(accountId);
 
@@ -60,7 +61,7 @@ public class DefaultNextBillingDatePoster implements NextBillingDatePoster {
                                                                              DefaultNextBillingDateNotifier.NEXT_BILLING_DATE_NOTIFIER_QUEUE);
             log.info("Queuing next billing date notification. id: {}, timestamp: {}", subscriptionId.toString(), futureNotificationTime.toString());
 
-            nextBillingQueue.recordFutureNotificationFromTransaction(transactionalDao, futureNotificationTime, accountId,
+            nextBillingQueue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, futureNotificationTime, accountId,
                                                                      new NextBillingDateNotificationKey(subscriptionId), context);
         } catch (NoSuchNotificationQueue e) {
             log.error("Attempting to put items on a non-existent queue (NextBillingDateNotifier).", e);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/notification/EmailInvoiceNotifier.java b/invoice/src/main/java/com/ning/billing/invoice/notification/EmailInvoiceNotifier.java
index befb7fd..052e5ea 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/notification/EmailInvoiceNotifier.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/EmailInvoiceNotifier.java
@@ -19,7 +19,6 @@ package com.ning.billing.invoice.notification;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 
 import com.ning.billing.ErrorCode;
 import com.ning.billing.ObjectType;
@@ -44,6 +43,7 @@ import com.ning.billing.util.tag.Tag;
 import com.google.inject.Inject;
 
 public class EmailInvoiceNotifier implements InvoiceNotifier {
+
     private final AccountInternalApi accountApi;
     private final TagInternalApi tagUserApi;
     private final HtmlInvoiceGenerator generator;
@@ -77,8 +77,8 @@ public class EmailInvoiceNotifier implements InvoiceNotifier {
 
         // Check if this account has the MANUAL_PAY system tag
         boolean manualPay = false;
-        final Map<String, Tag> accountTags = tagUserApi.getTags(account.getId(), ObjectType.ACCOUNT, internalTenantContext);
-        for (final Tag tag : accountTags.values()) {
+        final List<Tag> accountTags = tagUserApi.getTags(account.getId(), ObjectType.ACCOUNT, internalTenantContext);
+        for (final Tag tag : accountTags) {
             if (ControlTagType.MANUAL_PAY.getId().equals(tag.getTagDefinitionId())) {
                 manualPay = true;
                 break;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDateNotificationKey.java b/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDateNotificationKey.java
index 33131c8..085b462 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDateNotificationKey.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDateNotificationKey.java
@@ -13,15 +13,18 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
+
 package com.ning.billing.invoice.notification;
 
 import java.util.UUID;
 
+import com.ning.billing.util.notificationq.DefaultUUIDNotificationKey;
+
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.ning.billing.util.notificationq.DefaultUUIDNotificationKey;
 
 public class NextBillingDateNotificationKey extends DefaultUUIDNotificationKey {
+
     @JsonCreator
     public NextBillingDateNotificationKey(@JsonProperty("uuidKey") final UUID uuidKey) {
         super(uuidKey);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDateNotifier.java b/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDateNotifier.java
index 4f4c70a..a67a6c2 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDateNotifier.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDateNotifier.java
@@ -19,7 +19,6 @@ package com.ning.billing.invoice.notification;
 import com.ning.billing.util.notificationq.NotificationQueueService.NoSuchNotificationQueue;
 import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueAlreadyExists;
 
-
 public interface NextBillingDateNotifier {
 
     public void initialize() throws NotificationQueueAlreadyExists;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDatePoster.java b/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDatePoster.java
index 60f8ec4..95955f7 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDatePoster.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDatePoster.java
@@ -19,11 +19,13 @@ package com.ning.billing.invoice.notification;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 
 public interface NextBillingDatePoster {
 
-    void insertNextBillingNotification(Transmogrifier transactionalDao, UUID accountId,
+    void insertNextBillingNotification(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, UUID accountId,
                                        UUID subscriptionId, DateTime futureNotificationTime);
 
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/TagHandler.java b/invoice/src/main/java/com/ning/billing/invoice/TagHandler.java
index a4d70d6..35c46ea 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/TagHandler.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/TagHandler.java
@@ -13,6 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
+
 package com.ning.billing.invoice;
 
 import java.util.UUID;
@@ -43,8 +44,8 @@ public class TagHandler {
 
     @Inject
     public TagHandler(final Clock clock,
-            final InvoiceDispatcher dispatcher,
-            final InternalCallContextFactory internalCallContextFactory) {
+                      final InvoiceDispatcher dispatcher,
+                      final InternalCallContextFactory internalCallContextFactory) {
         this.clock = clock;
         this.dispatcher = dispatcher;
         this.internalCallContextFactory = internalCallContextFactory;
@@ -52,7 +53,7 @@ public class TagHandler {
 
     @Subscribe
     public void process_AUTO_INVOICING_OFF_removal(final ControlTagDeletionInternalEvent event) {
-        if (event.getTagDefinition().getName().equals(ControlTagType.AUTO_INVOICING_OFF.toString()) && event.getObjectType() ==  ObjectType.ACCOUNT) {
+        if (event.getTagDefinition().getName().equals(ControlTagType.AUTO_INVOICING_OFF.toString()) && event.getObjectType() == ObjectType.ACCOUNT) {
             final UUID accountId = event.getObjectId();
             final InternalCallContext context = internalCallContextFactory.createInternalCallContext(event.getTenantRecordId(), event.getAccountRecordId(), "InvoiceTagHandler", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
             processUnpaid_AUTO_INVOICING_OFF_invoices(accountId, context);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
index 7c22a77..12cbef1 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
@@ -35,6 +35,7 @@ package com.ning.billing.invoice.template.formatters;
 import java.math.BigDecimal;
 import java.text.NumberFormat;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Locale;
 import java.util.UUID;
@@ -153,7 +154,7 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
     }
 
     @Override
-    public boolean addInvoiceItems(final List<InvoiceItem> items) {
+    public boolean addInvoiceItems(final Collection<InvoiceItem> items) {
         return invoice.addInvoiceItems(items);
     }
 
@@ -173,7 +174,7 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
     }
 
     @Override
-    public boolean addPayments(final List<InvoicePayment> payments) {
+    public boolean addPayments(final Collection<InvoicePayment> payments) {
         return invoice.addPayments(payments);
     }
 
@@ -277,14 +278,17 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
 
     // Expose the fields for children classes. This is useful for further customization of the invoices
 
+    @SuppressWarnings("UnusedDeclaration")
     protected TranslatorConfig getConfig() {
         return config;
     }
 
+    @SuppressWarnings("UnusedDeclaration")
     protected DateTimeFormatter getDateFormatter() {
         return dateFormatter;
     }
 
+    @SuppressWarnings("UnusedDeclaration")
     protected Locale getLocale() {
         return locale;
     }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatterFactory.java b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatterFactory.java
index 99e62e9..876646b 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatterFactory.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatterFactory.java
@@ -24,6 +24,7 @@ import com.ning.billing.invoice.api.formatters.InvoiceFormatterFactory;
 import com.ning.billing.util.template.translation.TranslatorConfig;
 
 public class DefaultInvoiceFormatterFactory implements InvoiceFormatterFactory {
+
     @Override
     public InvoiceFormatter createInvoiceFormatter(final TranslatorConfig config, final Invoice invoice, final Locale locale) {
         return new DefaultInvoiceFormatter(config, invoice, locale);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/template/HtmlInvoiceGenerator.java b/invoice/src/main/java/com/ning/billing/invoice/template/HtmlInvoiceGenerator.java
index 9d105e5..f86bfeb 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/template/HtmlInvoiceGenerator.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/template/HtmlInvoiceGenerator.java
@@ -16,13 +16,13 @@
 
 package com.ning.billing.invoice.template;
 
-import javax.annotation.Nullable;
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
 
-import com.google.inject.Inject;
+import javax.annotation.Nullable;
+
 import com.ning.billing.account.api.Account;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.formatters.InvoiceFormatter;
@@ -32,7 +32,10 @@ import com.ning.billing.util.LocaleUtils;
 import com.ning.billing.util.email.templates.TemplateEngine;
 import com.ning.billing.util.template.translation.TranslatorConfig;
 
+import com.google.inject.Inject;
+
 public class HtmlInvoiceGenerator {
+
     private final InvoiceFormatterFactory factory;
     private final TemplateEngine templateEngine;
     private final TranslatorConfig config;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/template/translator/DefaultInvoiceTranslator.java b/invoice/src/main/java/com/ning/billing/invoice/template/translator/DefaultInvoiceTranslator.java
index 3e00120..6da618a 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/template/translator/DefaultInvoiceTranslator.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/template/translator/DefaultInvoiceTranslator.java
@@ -18,11 +18,13 @@ package com.ning.billing.invoice.template.translator;
 
 import java.util.Locale;
 
-import com.google.inject.Inject;
 import com.ning.billing.util.template.translation.DefaultTranslatorBase;
 import com.ning.billing.util.template.translation.TranslatorConfig;
 
+import com.google.inject.Inject;
+
 public class DefaultInvoiceTranslator extends DefaultTranslatorBase implements InvoiceStrings {
+
     private Locale locale;
 
     @Inject
diff --git a/invoice/src/main/java/com/ning/billing/invoice/template/translator/InvoiceStrings.java b/invoice/src/main/java/com/ning/billing/invoice/template/translator/InvoiceStrings.java
index b76322b..a5df6c5 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/template/translator/InvoiceStrings.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/template/translator/InvoiceStrings.java
@@ -17,6 +17,7 @@
 package com.ning.billing.invoice.template.translator;
 
 public interface InvoiceStrings {
+
     String getInvoiceTitle();
 
     String getInvoiceDate();
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
index ec2bcd4..5c762b9 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
@@ -1,119 +1,64 @@
-group InvoiceItemSqlDao;
+group InvoiceItemSqlDao: EntitySqlDao;
 
-CHECK_TENANT() ::= "tenant_record_id = :tenantRecordId"
-AND_CHECK_TENANT() ::= "AND <CHECK_TENANT()>"
+tableName() ::= "invoice_items"
 
-fields(prefix) ::= <<
-  <prefix>id,
-  <prefix>type,  
-  <prefix>invoice_id,
-  <prefix>account_id,
-  <prefix>bundle_id,
-  <prefix>subscription_id,
-  <prefix>plan_name,
-  <prefix>phase_name,
-  <prefix>start_date,
-  <prefix>end_date,
-  <prefix>amount,
-  <prefix>rate,
-  <prefix>currency,
-  <prefix>linked_item_id,
-  <prefix>created_by,
-  <prefix>created_date,
-  <prefix>account_record_id,
-  <prefix>tenant_record_id
+tableFields(prefix) ::= <<
+  <prefix>type
+, <prefix>invoice_id
+, <prefix>account_id
+, <prefix>bundle_id
+, <prefix>subscription_id
+, <prefix>plan_name
+, <prefix>phase_name
+, <prefix>start_date
+, <prefix>end_date
+, <prefix>amount
+, <prefix>rate
+, <prefix>currency
+, <prefix>linked_item_id
+, <prefix>created_by
+, <prefix>created_date
 >>
 
-getById() ::= <<
-  SELECT <fields()>
-  FROM invoice_items
-  WHERE id = :id
-  <AND_CHECK_TENANT()>
-  ;
+tableValues() ::= <<
+  :type
+, :invoiceId
+, :accountId
+, :bundleId
+, :subscriptionId
+, :planName
+, :phaseName
+, :startDate
+, :endDate
+, :amount
+, :rate
+, :currency
+, :linkedItemId
+, :createdBy
+, :createdDate
 >>
 
-getByRecordId() ::= <<
-  SELECT <fields()>
-  FROM invoice_items
-  WHERE record_id = :recordId
-  <AND_CHECK_TENANT()>
-  ;
->>
-
-getRecordId() ::= <<
-  SELECT record_id
-  FROM invoice_items
-  WHERE id = :id
-  <AND_CHECK_TENANT()>
-  ;
->>
 
 getInvoiceItemsByInvoice() ::= <<
-  SELECT <fields()>
-  FROM invoice_items
+  SELECT <allTableFields()>
+  FROM <tableName()>
   WHERE invoice_id = :invoiceId
   <AND_CHECK_TENANT()>
   ;
 >>
 
 getInvoiceItemsByAccount() ::= <<
-  SELECT <fields()>
-  FROM invoice_items
+  SELECT <allTableFields()>
+  FROM <tableName()>
   WHERE account_id = :accountId
   <AND_CHECK_TENANT()>
   ;
 >>
 
 getInvoiceItemsBySubscription() ::= <<
-  SELECT <fields()>
-  FROM invoice_items
+  SELECT <allTableFields()>
+  FROM <tableName()>
   WHERE subscription_id = :subscriptionId
   <AND_CHECK_TENANT()>
   ;
 >>
-
-create() ::= <<
-  INSERT INTO invoice_items(<fields()>)
-  VALUES(:id, :type, :invoiceId, :accountId, :bundleId, :subscriptionId, :planName, :phaseName, :startDate, :endDate,
-         :amount, :rate, :currency, :linkedItemId, :userName, :createdDate, :accountRecordId, :tenantRecordId);
->>
-
-batchCreateFromTransaction() ::= <<
-  INSERT INTO invoice_items(<fields()>)
-  VALUES(:id, :type,:invoiceId, :accountId, :bundleId, :subscriptionId, :planName, :phaseName, :startDate, :endDate,
-         :amount, :rate, :currency, :linkedItemId, :userName, :createdDate, :accountRecordId, :tenantRecordId);
->>
-
-getRecordIds() ::= <<
-    SELECT record_id, id
-    FROM invoice_items
-    WHERE invoice_id = :invoiceId
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-auditFields(prefix) ::= <<
-    <prefix>table_name,
-    <prefix>record_id,
-    <prefix>change_type,
-    <prefix>change_date,
-    <prefix>changed_by,
-    <prefix>reason_code,
-    <prefix>comments,
-    <prefix>user_token,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
-
-insertAuditFromTransaction() ::= <<
-    INSERT INTO audit_log(<auditFields()>)
-    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken, :accountRecordId, :tenantRecordId);
->>
-
-test() ::= <<
-  SELECT 1
-  FROM invoice_items
-  WHERE <CHECK_TENANT()>
-  ;
->>
-;
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
index 77139a9..6428f7c 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
@@ -1,138 +1,76 @@
-group InvoicePayment;
-
-CHECK_TENANT(prefix) ::= "<prefix>tenant_record_id = :tenantRecordId"
-AND_CHECK_TENANT(prefix) ::= "AND <CHECK_TENANT(prefix)>"
-
-invoicePaymentFields(prefix) ::= <<
-  <prefix>id,
-  <prefix>type,
-  <prefix>invoice_id,
-  <prefix>payment_id,
-  <prefix>payment_date,
-  <prefix>amount,
-  <prefix>currency,
-  <prefix>payment_cookie_id,
-  <prefix>linked_invoice_payment_id,
-  <prefix>created_by,
-  <prefix>created_date,
-  <prefix>account_record_id,
-  <prefix>tenant_record_id
->>
-
-create() ::= <<
-  INSERT INTO invoice_payments(<invoicePaymentFields()>)
-  VALUES(:id, :type, :invoiceId, :paymentId, :paymentDate, :amount, :currency,
-         :paymentCookieId, :linkedInvoicePaymentId, :userName, :createdDate, :accountRecordId, :tenantRecordId);
->>
-
-batchCreateFromTransaction() ::= <<
-  INSERT INTO invoice_payments(<invoicePaymentFields()>)
-  VALUES(:id, :type, :invoiceId, :paymentId, :paymentDate, :amount, :currency,
-         :paymentCookieId, :linkedInvoicePaymentId, :userName, :createdDate, :accountRecordId, :tenantRecordId);
+group InvoicePayment: EntitySqlDao;
+
+tableName() ::= "invoice_payments"
+
+tableFields(prefix) ::= <<
+  <prefix>type
+, <prefix>invoice_id
+, <prefix>payment_id
+, <prefix>payment_date
+, <prefix>amount
+, <prefix>currency
+, <prefix>payment_cookie_id
+, <prefix>linked_invoice_payment_id
+, <prefix>created_by
+, <prefix>created_date
+>>
+
+tableValues() ::= <<
+  :type
+, :invoiceId
+, :paymentId
+, :paymentDate
+, :amount
+, :currency
+, :paymentCookieId
+, :linkedInvoicePaymentId
+, :createdBy
+, :createdDate
 >>
 
 getByPaymentId() ::= <<
-  SELECT <invoicePaymentFields()>
-  FROM invoice_payments
+  SELECT <allTableFields()>
+  FROM <tableName()>
   WHERE payment_id = :paymentId
   <AND_CHECK_TENANT()>
   ;
 >>
 
-get() ::= <<
-  SELECT <invoicePaymentFields()>
-  FROM invoice_payments
-  <AND_CHECK_TENANT()>
-  ;
->>
-
-getById() ::= <<
-  SELECT <invoicePaymentFields()>
-  FROM invoice_payments
-  WHERE id = :id
-  <AND_CHECK_TENANT()>
-  ;
->>
-
 getPaymentsForCookieId() ::= <<
-  SELECT <invoicePaymentFields()>
-  FROM invoice_payments
+  SELECT <allTableFields()>
+  FROM <tableName()>
   WHERE payment_cookie_id = :paymentCookieId
   <AND_CHECK_TENANT()>
   ;
 >>
 
 getPaymentsForInvoice() ::= <<
-  SELECT <invoicePaymentFields()>
-  FROM invoice_payments
+  SELECT <allTableFields()>
+  FROM <tableName()>
   WHERE invoice_id = :invoiceId
   <AND_CHECK_TENANT()>
   ;
 >>
 
-notifyOfPayment() ::= <<
-  INSERT INTO invoice_payments(<invoicePaymentFields()>)
-  VALUES(:id, :type, :invoiceId, :paymentId, :paymentDate, :amount, :currency,
-         :paymentCookieId, :linkedInvoicePaymentId, :userName, :createdDate, :accountRecordId, :tenantRecordId);
->>
-
 getInvoicePayments() ::= <<
-    SELECT <invoicePaymentFields()>
-    FROM invoice_payments
+    SELECT <allTableFields()>
+    FROM <tableName()>
     WHERE payment_id = :paymentId
     <AND_CHECK_TENANT()>
     ;
 >>
 
-getRecordId() ::= <<
-    SELECT record_id
-    FROM invoice_payments
-    WHERE id = :id
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-getRecordIds() ::= <<
-    SELECT record_id, id
-    FROM invoice_payments
-    WHERE invoice_id = :invoiceId
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-auditFields(prefix) ::= <<
-    <prefix>table_name,
-    <prefix>record_id,
-    <prefix>change_type,
-    <prefix>change_date,
-    <prefix>changed_by,
-    <prefix>reason_code,
-    <prefix>comments,
-    <prefix>user_token,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
-
-insertAuditFromTransaction() ::= <<
-    INSERT INTO audit_log(<auditFields()>)
-    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken, :accountRecordId, :tenantRecordId);
->>
-
-test() ::= <<
-    SELECT 1 FROM invoice_payments where <CHECK_TENANT()>;
->>
-
 getRemainingAmountPaid() ::= <<
     SELECT SUM(amount)
-    FROM invoice_payments
+    FROM <tableName()>
     WHERE (id = :invoicePaymentId OR linked_invoice_payment_id = :invoicePaymentId)
     <AND_CHECK_TENANT()>
     ;
 >>
 
 getAccountIdFromInvoicePaymentId() ::= <<
-    SELECT account_id
-    FROM invoice_payments ip
+    SELECT i.account_id
+    FROM <tableName()> ip
     INNER JOIN invoices i ON i.id = ip.invoice_id
     WHERE ip.id = :invoicePaymentId
     <AND_CHECK_TENANT("i.")>
@@ -141,8 +79,8 @@ getAccountIdFromInvoicePaymentId() ::= <<
 >>
 
 getChargeBacksByAccountId() ::= <<
-    SELECT <invoicePaymentFields("ip.")>
-    FROM invoice_payments ip
+    SELECT <allTableFields("ip.")>
+    FROM <tableName()> ip
     INNER JOIN invoices i ON i.id = ip.invoice_id
     WHERE ip.type = 'CHARGED_BACK' AND i.account_id = :accountId
     <AND_CHECK_TENANT("i.")>
@@ -151,11 +89,11 @@ getChargeBacksByAccountId() ::= <<
 >>
 
 getChargebacksByPaymentId() ::= <<
-    SELECT <invoicePaymentFields()>
-    FROM invoice_payments
+    SELECT <allTableFields()>
+    FROM <tableName()>
     WHERE type = 'CHARGED_BACK'
     AND linked_invoice_payment_id IN (SELECT id FROM invoice_payments WHERE payment_id = :paymentId)
     <AND_CHECK_TENANT()>
     ;
 >>
-;
+
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
index 1cba076..cd845e7 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
@@ -1,55 +1,58 @@
-group InvoiceDao;
-
-CHECK_TENANT(prefix) ::= "<prefix>tenant_record_id = :tenantRecordId"
-AND_CHECK_TENANT(prefix) ::= "AND <CHECK_TENANT(prefix)>"
+group InvoiceDao: EntitySqlDao;
+
+tableName() ::= "invoices"
+
+tableFields(prefix) ::= <<
+  <prefix>account_id
+, <prefix>invoice_date
+, <prefix>target_date
+, <prefix>currency
+, <prefix>migrated
+, <prefix>created_by
+, <prefix>created_date
+>>
 
-invoiceFields(prefix) ::= <<
-    <prefix>id,
-    <prefix>account_id,
-    <prefix>invoice_date,
-    <prefix>target_date,
-    <prefix>currency,
-    <prefix>migrated,
-    <prefix>created_by,
-    <prefix>created_date,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
+tableValues() ::= <<
+  :accountId
+, :invoiceDate
+, :targetDate
+, :currency
+, :migrated
+, :createdBy
+, :createdDate
 >>
 
-get() ::= <<
-  SELECT record_id as invoice_number, <invoiceFields()>
-  FROM invoices
-  WHERE <CHECK_TENANT()>
-  ORDER BY target_date ASC;
+extraTableFieldsWithComma(prefix) ::= <<
+, <prefix>record_id as invoice_number
 >>
 
 getInvoicesByAccount() ::= <<
-  SELECT record_id as invoice_number, <invoiceFields()>
-  FROM invoices
+  SELECT <allTableFields()>
+  FROM <tableName()>
   WHERE account_id = :accountId AND migrated = '0'
   <AND_CHECK_TENANT()>
   ORDER BY target_date ASC;
 >>
 
 getAllInvoicesByAccount() ::= <<
-  SELECT record_id as invoice_number, <invoiceFields()>
-  FROM invoices
+  SELECT <allTableFields()>
+  FROM <tableName()>
   WHERE account_id = :accountId
   <AND_CHECK_TENANT()>
   ORDER BY target_date ASC;
 >>
 
 getInvoicesByAccountAfterDate() ::= <<
-  SELECT record_id as invoice_number, <invoiceFields()>
-  FROM invoices
+  SELECT <allTableFields()>
+  FROM <tableName()>
   WHERE account_id = :accountId AND target_date >= :fromDate AND migrated = '0'
   <AND_CHECK_TENANT()>
   ORDER BY target_date ASC;
 >>
 
 getInvoicesBySubscription() ::= <<
-  SELECT i.record_id as invoice_number, <invoiceFields("i.")>
-  FROM invoices i
+  SELECT <allTableFields("i.")>
+  FROM <tableName()> i
   JOIN invoice_items ii ON i.id = ii.invoice_id
   WHERE ii.subscription_id = :subscriptionId AND i.migrated = '0'
   <AND_CHECK_TENANT("i.")>
@@ -57,67 +60,11 @@ getInvoicesBySubscription() ::= <<
   ;
 >>
 
-getById() ::= <<
-  SELECT record_id as invoice_number, <invoiceFields()>
-  FROM invoices
-  WHERE id = :id
-  <AND_CHECK_TENANT()>
-  ;
->>
-
-getByRecordId() ::= <<
-  SELECT record_id as invoice_number, <invoiceFields()>
-  FROM invoices
-  WHERE record_id = :recordId
-  <AND_CHECK_TENANT()>
-  ;
->>
-
-create() ::= <<
-  INSERT INTO invoices(<invoiceFields()>)
-  VALUES (:id, :accountId, :invoiceDate, :targetDate, :currency, :migrated, :userName, :createdDate, :accountRecordId, :tenantRecordId);
->>
-
 getInvoiceIdByPaymentId() ::= <<
   SELECT i.id
-    FROM invoices i, invoice_payments ip
+    FROM <tableName()> i, invoice_payments ip
    WHERE ip.invoice_id = i.id
      AND ip.payment_id = :paymentId
    <AND_CHECK_TENANT("i.")>
    <AND_CHECK_TENANT("ip.")>
 >>
-
-
-getRecordId() ::= <<
-    SELECT record_id
-    FROM invoices
-    WHERE id = :id
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-auditFields(prefix) ::= <<
-    <prefix>table_name,
-    <prefix>record_id,
-    <prefix>change_type,
-    <prefix>change_date,
-    <prefix>changed_by,
-    <prefix>reason_code,
-    <prefix>comments,
-    <prefix>user_token,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
-
-insertAuditFromTransaction() ::= <<
-    INSERT INTO audit_log(<auditFields()>)
-    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken, :accountRecordId, :tenantRecordId);
->>
-
-test() ::= <<
-  SELECT 1
-  FROM invoices
-  WHERE <CHECK_TENANT()>
-  ;
->>
-;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/invoice/TestDefaultInvoicePaymentApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/invoice/TestDefaultInvoicePaymentApi.java
index ccd01ae..4d2ee81 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/invoice/TestDefaultInvoicePaymentApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/invoice/TestDefaultInvoicePaymentApi.java
@@ -39,7 +39,7 @@ import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.invoice.api.InvoicePayment.InvoicePaymentType;
 import com.ning.billing.invoice.api.InvoicePaymentApi;
 import com.ning.billing.invoice.api.svcs.DefaultInvoiceInternalApi;
-import com.ning.billing.invoice.dao.AuditedInvoiceDao;
+import com.ning.billing.invoice.dao.DefaultInvoiceDao;
 import com.ning.billing.invoice.dao.InvoiceDao;
 import com.ning.billing.invoice.dao.InvoiceItemSqlDao;
 import com.ning.billing.invoice.dao.InvoiceSqlDao;
@@ -83,7 +83,7 @@ public class TestDefaultInvoicePaymentApi extends InvoiceTestSuiteWithEmbeddedDB
 
         final NextBillingDatePoster nextBillingDatePoster = new MockNextBillingDatePoster();
         internalCallContextFactory = new InternalCallContextFactory(dbi, clock);
-        final InvoiceDao invoiceDao = new AuditedInvoiceDao(dbi, nextBillingDatePoster, clock, Mockito.mock(InternalBus.class));
+        final InvoiceDao invoiceDao = new DefaultInvoiceDao(dbi, nextBillingDatePoster, Mockito.mock(InternalBus.class));
         invoicePaymentApi = new DefaultInvoicePaymentApi(invoiceDao, internalCallContextFactory);
         invoiceInternalApi = new DefaultInvoiceInternalApi(invoiceDao);
     }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/migration/InvoiceApiTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/api/migration/InvoiceApiTestBase.java
index 6f6d465..42518d7 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/migration/InvoiceApiTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/migration/InvoiceApiTestBase.java
@@ -45,6 +45,7 @@ import com.ning.billing.invoice.api.InvoiceNotifier;
 import com.ning.billing.invoice.api.InvoicePaymentApi;
 import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.invoice.dao.InvoiceModelDao;
 import com.ning.billing.invoice.generator.InvoiceGenerator;
 import com.ning.billing.invoice.notification.NullInvoiceNotifier;
 import com.ning.billing.invoice.tests.InvoicingTestBase;
@@ -141,12 +142,12 @@ public abstract class InvoiceApiTestBase extends InvoicingTestBase {
         final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
         final InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountApi, billingApi, entitlementApi,
                                                                    invoiceDao, invoiceNotifier, locker, busService.getBus(),
-                                                                   clock, internalCallContextFactory);
+                                                                   clock);
 
         Invoice invoice = dispatcher.processAccount(account.getId(), targetDate, true, internalCallContext);
         Assert.assertNotNull(invoice);
 
-        List<Invoice> invoices = invoiceDao.getInvoicesByAccount(account.getId(), internalCallContext);
+        List<InvoiceModelDao> invoices = invoiceDao.getInvoicesByAccount(account.getId(), internalCallContext);
         Assert.assertEquals(invoices.size(), 0);
 
         invoice = dispatcher.processAccount(account.getId(), targetDate, false, internalCallContext);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/migration/MockModuleNoEntitlement.java b/invoice/src/test/java/com/ning/billing/invoice/api/migration/MockModuleNoEntitlement.java
index ecae293..3266965 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/migration/MockModuleNoEntitlement.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/migration/MockModuleNoEntitlement.java
@@ -29,6 +29,7 @@ import com.ning.billing.util.email.templates.TemplateModule;
 import com.ning.billing.util.template.translation.TranslatorConfig;
 
 public class MockModuleNoEntitlement extends MockModule {
+
     @Override
     protected void installInvoiceModule() {
         install(new DefaultInvoiceModule() {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
index ab8afe5..6aaa407 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
@@ -33,6 +33,8 @@ import com.ning.billing.account.api.Account;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.dao.InvoiceModelDao;
+import com.ning.billing.invoice.dao.InvoiceModelDaoHelper;
 
 public class TestDefaultInvoiceMigrationApi extends InvoiceApiTestBase {
 
@@ -65,17 +67,17 @@ public class TestDefaultInvoiceMigrationApi extends InvoiceApiTestBase {
         Assert.assertNotNull(migrationInvoiceId);
         //Double check it was created and values are correct
 
-        final Invoice invoice = invoiceDao.getById(migrationInvoiceId, internalCallContext);
+        final InvoiceModelDao invoice = invoiceDao.getById(migrationInvoiceId, internalCallContext);
         Assert.assertNotNull(invoice);
 
         Assert.assertEquals(invoice.getAccountId(), accountId);
         Assert.assertEquals(invoice.getTargetDate().compareTo(date_migrated), 0); //temp to avoid tz test artifact
         //		Assert.assertEquals(invoice.getTargetDate(),now);
-        Assert.assertEquals(invoice.getNumberOfItems(), 1);
+        Assert.assertEquals(invoice.getInvoiceItems().size(), 1);
         Assert.assertEquals(invoice.getInvoiceItems().get(0).getAmount().compareTo(MIGRATION_INVOICE_AMOUNT), 0);
-        Assert.assertEquals(invoice.getBalance().compareTo(MIGRATION_INVOICE_AMOUNT), 0);
+        Assert.assertEquals(InvoiceModelDaoHelper.getBalance(invoice).compareTo(MIGRATION_INVOICE_AMOUNT), 0);
         Assert.assertEquals(invoice.getCurrency(), MIGRATION_INVOICE_CURRENCY);
-        Assert.assertTrue(invoice.isMigrationInvoice());
+        Assert.assertTrue(invoice.isMigrated());
 
         return migrationInvoiceId;
     }
@@ -105,10 +107,10 @@ public class TestDefaultInvoiceMigrationApi extends InvoiceApiTestBase {
 
     // ACCOUNT balance should reflect total of migration and non-migration invoices
     @Test(groups = "slow")
-    public void testBalance() throws InvoiceApiException{
-        final Invoice migrationInvoice = invoiceDao.getById(migrationInvoiceId, internalCallContext);
-        final Invoice regularInvoice = invoiceDao.getById(regularInvoiceId, internalCallContext);
-        final BigDecimal balanceOfAllInvoices = migrationInvoice.getBalance().add(regularInvoice.getBalance());
+    public void testBalance() throws InvoiceApiException {
+        final InvoiceModelDao migrationInvoice = invoiceDao.getById(migrationInvoiceId, internalCallContext);
+        final InvoiceModelDao regularInvoice = invoiceDao.getById(regularInvoiceId, internalCallContext);
+        final BigDecimal balanceOfAllInvoices = InvoiceModelDaoHelper.getBalance(migrationInvoice).add(InvoiceModelDaoHelper.getBalance(regularInvoice));
 
         final BigDecimal accountBalance = invoiceUserApi.getAccountBalance(accountId, callContext);
         log.info("ACCOUNT balance: " + accountBalance + " should equal the Balance Of All Invoices: " + balanceOfAllInvoices);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java
index ecd23bd..8358a57 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java
@@ -63,7 +63,6 @@ public class MockInvoicePaymentApi implements InvoicePaymentApi {
         return null;
     }
 
-
     @Override
     public List<InvoicePayment> getInvoicePayments(final UUID paymentId, final TenantContext context) {
         final List<InvoicePayment> result = new LinkedList<InvoicePayment>();
@@ -85,7 +84,6 @@ public class MockInvoicePaymentApi implements InvoicePaymentApi {
         return null;
     }
 
-
     @Override
     public InvoicePayment createChargeback(final UUID invoicePaymentId, final BigDecimal amount, final CallContext context) throws InvoiceApiException {
         InvoicePayment existingPayment = null;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
index 97606ba..b576cc3 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
@@ -17,7 +17,7 @@
 package com.ning.billing.invoice.api.user;
 
 import java.math.BigDecimal;
-import java.util.Map;
+import java.util.List;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
@@ -328,9 +328,9 @@ public class TestDefaultInvoiceUserApi extends InvoiceApiTestBase {
     public void testAddRemoveWrittenOffTag() throws InvoiceApiException, TagApiException {
         invoiceUserApi.tagInvoiceAsWrittenOff(invoiceId, callContext);
 
-        Map<String, Tag> tags = tagUserApi.getTags(invoiceId, ObjectType.INVOICE, tenantContext);
+        List<Tag> tags = tagUserApi.getTags(invoiceId, ObjectType.INVOICE, tenantContext);
         assertEquals(tags.size(), 1);
-        assertEquals(tags.values().iterator().next().getTagDefinitionId(), ControlTagType.WRITTEN_OFF.getId());
+        assertEquals(tags.get(0).getTagDefinitionId(), ControlTagType.WRITTEN_OFF.getId());
 
         invoiceUserApi.tagInvoiceAsNotWrittenOff(invoiceId, callContext);
         tags = tagUserApi.getTags(invoiceId, ObjectType.INVOICE, tenantContext);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
index 75919ac..c4dd373 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
@@ -16,19 +16,22 @@
 
 package com.ning.billing.invoice.dao;
 
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
-
 import java.io.IOException;
 import java.net.URL;
+import java.util.List;
+import java.util.UUID;
 
 import org.skife.jdbi.v2.IDBI;
+import org.testng.Assert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 
 import com.ning.billing.KillbillTestSuiteWithEmbeddedDB;
-import com.ning.billing.util.config.InvoiceConfig;
 import com.ning.billing.dbi.MysqlTestingHelper;
+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.generator.DefaultInvoiceGenerator;
 import com.ning.billing.invoice.generator.InvoiceGenerator;
 import com.ning.billing.invoice.glue.InvoiceModuleWithEmbeddedDb;
@@ -36,13 +39,21 @@ import com.ning.billing.invoice.notification.MockNextBillingDatePoster;
 import com.ning.billing.invoice.notification.NextBillingDatePoster;
 import com.ning.billing.invoice.tests.InvoicingTestBase;
 import com.ning.billing.util.bus.InMemoryInternalBus;
+import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.config.InvoiceConfig;
+import com.ning.billing.util.entity.EntityPersistenceException;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
-import com.ning.billing.util.tag.api.user.TagEventBuilder;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
 
 public class InvoiceDaoTestBase extends InvoicingTestBase {
-    protected final TagEventBuilder tagEventBuilder = new TagEventBuilder();
 
     protected IDBI dbi;
     protected InvoiceDao invoiceDao;
@@ -98,7 +109,7 @@ public class InvoiceDaoTestBase extends InvoicingTestBase {
         bus.start();
 
         final NextBillingDatePoster nextBillingDatePoster = new MockNextBillingDatePoster();
-        invoiceDao = new AuditedInvoiceDao(dbi, nextBillingDatePoster, clock, bus);
+        invoiceDao = new DefaultInvoiceDao(dbi, nextBillingDatePoster, bus);
         invoiceDao.test(internalCallContext);
 
         invoiceItemSqlDao = dbi.onDemand(InvoiceItemSqlDao.class);
@@ -114,4 +125,43 @@ public class InvoiceDaoTestBase extends InvoicingTestBase {
         bus.stop();
         assertTrue(true);
     }
+
+    protected void createPayment(final InvoicePayment invoicePayment, final InternalCallContext internalCallContext) {
+        try {
+            invoicePaymentDao.create(new InvoicePaymentModelDao(invoicePayment), internalCallContext);
+        } catch (EntityPersistenceException e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+
+    protected void createInvoiceItem(final InvoiceItem invoiceItem, final InternalCallContext internalCallContext) throws EntityPersistenceException {
+        invoiceItemSqlDao.create(new InvoiceItemModelDao(invoiceItem), internalCallContext);
+    }
+
+    protected void createInvoice(final Invoice invoice, final boolean isRealInvoiceWithItems, final InternalCallContext internalCallContext) {
+        final InvoiceModelDao invoiceModelDao = new InvoiceModelDao(invoice);
+        final List<InvoiceItemModelDao> invoiceItemModelDaos = ImmutableList.<InvoiceItemModelDao>copyOf(Collections2.transform(invoice.getInvoiceItems(),
+                                                                                                                                new Function<InvoiceItem, InvoiceItemModelDao>() {
+                                                                                                                                    @Override
+                                                                                                                                    public InvoiceItemModelDao apply(final InvoiceItem input) {
+                                                                                                                                        return new InvoiceItemModelDao(input);
+                                                                                                                                    }
+                                                                                                                                }));
+        // Not really needed, there shouldn't be any payment at this stage
+        final List<InvoicePaymentModelDao> invoicePaymentModelDaos = ImmutableList.<InvoicePaymentModelDao>copyOf(Collections2.transform(invoice.getPayments(),
+                                                                                                                                         new Function<InvoicePayment, InvoicePaymentModelDao>() {
+                                                                                                                                             @Override
+                                                                                                                                             public InvoicePaymentModelDao apply(final InvoicePayment input) {
+                                                                                                                                                 return new InvoicePaymentModelDao(input);
+                                                                                                                                             }
+                                                                                                                                         }));
+
+        invoiceDao.createInvoice(invoiceModelDao, invoiceItemModelDaos, invoicePaymentModelDaos, isRealInvoiceWithItems, internalCallContext);
+    }
+
+    protected void verifyInvoice(final UUID invoiceId, final double balance, final double cbaAmount) throws InvoiceApiException {
+        final InvoiceModelDao invoice = invoiceDao.getById(invoiceId, internalCallContext);
+        Assert.assertEquals(InvoiceModelDaoHelper.getBalance(invoice).doubleValue(), balance);
+        Assert.assertEquals(InvoiceModelDaoHelper.getCBAAmount(invoice).doubleValue(), cbaAmount);
+    }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java b/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
index 3f67067..cdcc683 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
@@ -29,10 +29,7 @@ import javax.annotation.Nullable;
 import org.joda.time.LocalDate;
 
 import com.ning.billing.catalog.api.Currency;
-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.user.DefaultInvoiceCreationEvent;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
@@ -44,7 +41,9 @@ public class MockInvoiceDao implements InvoiceDao {
 
     private final InternalBus eventBus;
     private final Object monitor = new Object();
-    private final Map<UUID, Invoice> invoices = new LinkedHashMap<UUID, Invoice>();
+    private final Map<UUID, InvoiceModelDao> invoices = new LinkedHashMap<UUID, InvoiceModelDao>();
+    private final Map<UUID, InvoiceItemModelDao> items = new LinkedHashMap<UUID, InvoiceItemModelDao>();
+    private final Map<UUID, InvoicePaymentModelDao> payments = new LinkedHashMap<UUID, InvoicePaymentModelDao>();
 
     @Inject
     public MockInvoiceDao(final InternalBus eventBus) {
@@ -52,13 +51,20 @@ public class MockInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public void create(final Invoice invoice, final int billCycleDay, final boolean isRealInvoice, final InternalCallContext context) {
+    public void createInvoice(final InvoiceModelDao invoice, final List<InvoiceItemModelDao> invoiceItems,
+                              final List<InvoicePaymentModelDao> invoicePayments, final boolean isRealInvoice, final InternalCallContext context) {
         synchronized (monitor) {
             invoices.put(invoice.getId(), invoice);
+            for (final InvoiceItemModelDao invoiceItemModelDao : invoiceItems) {
+                items.put(invoiceItemModelDao.getId(), invoiceItemModelDao);
+            }
+            for (final InvoicePaymentModelDao paymentModelDao : invoicePayments) {
+                payments.put(paymentModelDao.getId(), paymentModelDao);
+            }
         }
         try {
             eventBus.post(new DefaultInvoiceCreationEvent(invoice.getId(), invoice.getAccountId(),
-                                                          invoice.getBalance(), invoice.getCurrency(),
+                                                          InvoiceModelDaoHelper.getBalance(invoice), invoice.getCurrency(),
                                                           null, 1L, 1L), context);
         } catch (InternalBus.EventBusException ex) {
             throw new RuntimeException(ex);
@@ -66,16 +72,16 @@ public class MockInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public Invoice getById(final UUID id, final InternalTenantContext context) {
+    public InvoiceModelDao getById(final UUID id, final InternalTenantContext context) {
         synchronized (monitor) {
             return invoices.get(id);
         }
     }
 
     @Override
-    public Invoice getByNumber(final Integer number, final InternalTenantContext context) {
+    public InvoiceModelDao getByNumber(final Integer number, final InternalTenantContext context) {
         synchronized (monitor) {
-            for (final Invoice invoice : invoices.values()) {
+            for (final InvoiceModelDao invoice : invoices.values()) {
                 if (invoice.getInvoiceNumber().equals(number)) {
                     return invoice;
                 }
@@ -86,19 +92,19 @@ public class MockInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public List<Invoice> get(final InternalTenantContext context) {
+    public List<InvoiceModelDao> get(final InternalTenantContext context) {
         synchronized (monitor) {
-            return new ArrayList<Invoice>(invoices.values());
+            return new ArrayList<InvoiceModelDao>(invoices.values());
         }
     }
 
     @Override
-    public List<Invoice> getInvoicesByAccount(final UUID accountId, final InternalTenantContext context) {
-        final List<Invoice> result = new ArrayList<Invoice>();
+    public List<InvoiceModelDao> getInvoicesByAccount(final UUID accountId, final InternalTenantContext context) {
+        final List<InvoiceModelDao> result = new ArrayList<InvoiceModelDao>();
 
         synchronized (monitor) {
-            for (final Invoice invoice : invoices.values()) {
-                if (accountId.equals(invoice.getAccountId()) && !invoice.isMigrationInvoice()) {
+            for (final InvoiceModelDao invoice : invoices.values()) {
+                if (accountId.equals(invoice.getAccountId()) && !invoice.isMigrated()) {
                     result.add(invoice);
                 }
             }
@@ -107,12 +113,12 @@ public class MockInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public List<Invoice> getInvoicesByAccount(final UUID accountId, final LocalDate fromDate, final InternalTenantContext context) {
-        final List<Invoice> invoicesForAccount = new ArrayList<Invoice>();
+    public List<InvoiceModelDao> getInvoicesByAccount(final UUID accountId, final LocalDate fromDate, final InternalTenantContext context) {
+        final List<InvoiceModelDao> invoicesForAccount = new ArrayList<InvoiceModelDao>();
 
         synchronized (monitor) {
-            for (final Invoice invoice : get(context)) {
-                if (accountId.equals(invoice.getAccountId()) && !invoice.getTargetDate().isBefore(fromDate) && !invoice.isMigrationInvoice()) {
+            for (final InvoiceModelDao invoice : get(context)) {
+                if (accountId.equals(invoice.getAccountId()) && !invoice.getTargetDate().isBefore(fromDate) && !invoice.isMigrated()) {
                     invoicesForAccount.add(invoice);
                 }
             }
@@ -122,13 +128,13 @@ public class MockInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public List<Invoice> getInvoicesBySubscription(final UUID subscriptionId, final InternalTenantContext context) {
-        final List<Invoice> result = new ArrayList<Invoice>();
+    public List<InvoiceModelDao> getInvoicesBySubscription(final UUID subscriptionId, final InternalTenantContext context) {
+        final List<InvoiceModelDao> result = new ArrayList<InvoiceModelDao>();
 
         synchronized (monitor) {
-            for (final Invoice invoice : invoices.values()) {
-                for (final InvoiceItem item : invoice.getInvoiceItems()) {
-                    if (subscriptionId.equals(item.getSubscriptionId()) && !invoice.isMigrationInvoice()) {
+            for (final InvoiceModelDao invoice : invoices.values()) {
+                for (final InvoiceItemModelDao item : invoice.getInvoiceItems()) {
+                    if (subscriptionId.equals(item.getSubscriptionId()) && !invoice.isMigrated()) {
                         result.add(invoice);
                         break;
                     }
@@ -145,11 +151,9 @@ public class MockInvoiceDao implements InvoiceDao {
     @Override
     public UUID getInvoiceIdByPaymentId(final UUID paymentId, final InternalTenantContext context) {
         synchronized (monitor) {
-            for (final Invoice invoice : invoices.values()) {
-                for (final InvoicePayment payment : invoice.getPayments()) {
-                    if (paymentId.equals(payment.getPaymentId())) {
-                        return invoice.getId();
-                    }
+            for (final InvoicePaymentModelDao payment : payments.values()) {
+                if (paymentId.equals(payment.getPaymentId())) {
+                    return payment.getInvoiceId();
                 }
             }
         }
@@ -157,14 +161,12 @@ public class MockInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public List<InvoicePayment> getInvoicePayments(final UUID paymentId, final InternalTenantContext context) {
-        final List<InvoicePayment> result = new LinkedList<InvoicePayment>();
+    public List<InvoicePaymentModelDao> getInvoicePayments(final UUID paymentId, final InternalTenantContext context) {
+        final List<InvoicePaymentModelDao> result = new LinkedList<InvoicePaymentModelDao>();
         synchronized (monitor) {
-            for (final Invoice invoice : invoices.values()) {
-                for (final InvoicePayment payment : invoice.getPayments()) {
-                    if (paymentId.equals(payment.getPaymentId())) {
-                        result.add(payment);
-                    }
+            for (final InvoicePaymentModelDao payment : payments.values()) {
+                if (paymentId.equals(payment.getPaymentId())) {
+                    result.add(payment);
                 }
             }
         }
@@ -172,12 +174,9 @@ public class MockInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public void notifyOfPayment(final InvoicePayment invoicePayment, final InternalCallContext context) {
+    public void notifyOfPayment(final InvoicePaymentModelDao invoicePayment, final InternalCallContext context) {
         synchronized (monitor) {
-            final Invoice invoice = invoices.get(invoicePayment.getInvoiceId());
-            if (invoice != null) {
-                invoice.addPayment(invoicePayment);
-            }
+            payments.put(invoicePayment.getId(), invoicePayment);
         }
     }
 
@@ -185,9 +184,9 @@ public class MockInvoiceDao implements InvoiceDao {
     public BigDecimal getAccountBalance(final UUID accountId, final InternalTenantContext context) {
         BigDecimal balance = BigDecimal.ZERO;
 
-        for (final Invoice invoice : get(context)) {
+        for (final InvoiceModelDao invoice : get(context)) {
             if (accountId.equals(invoice.getAccountId())) {
-                balance = balance.add(invoice.getBalance());
+                balance = balance.add(InvoiceModelDaoHelper.getBalance(invoice));
             }
         }
 
@@ -195,11 +194,11 @@ public class MockInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public List<Invoice> getUnpaidInvoicesByAccountId(final UUID accountId, final LocalDate upToDate, final InternalTenantContext context) {
-        final List<Invoice> unpaidInvoices = new ArrayList<Invoice>();
+    public List<InvoiceModelDao> getUnpaidInvoicesByAccountId(final UUID accountId, final LocalDate upToDate, final InternalTenantContext context) {
+        final List<InvoiceModelDao> unpaidInvoices = new ArrayList<InvoiceModelDao>();
 
-        for (final Invoice invoice : get(context)) {
-            if (accountId.equals(invoice.getAccountId()) && (invoice.getBalance().compareTo(BigDecimal.ZERO) > 0) && !invoice.isMigrationInvoice()) {
+        for (final InvoiceModelDao invoice : get(context)) {
+            if (accountId.equals(invoice.getAccountId()) && (InvoiceModelDaoHelper.getBalance(invoice).compareTo(BigDecimal.ZERO) > 0) && !invoice.isMigrated()) {
                 unpaidInvoices.add(invoice);
             }
         }
@@ -208,11 +207,11 @@ public class MockInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public List<Invoice> getAllInvoicesByAccount(final UUID accountId, final InternalTenantContext context) {
-        final List<Invoice> result = new ArrayList<Invoice>();
+    public List<InvoiceModelDao> getAllInvoicesByAccount(final UUID accountId, final InternalTenantContext context) {
+        final List<InvoiceModelDao> result = new ArrayList<InvoiceModelDao>();
 
         synchronized (monitor) {
-            for (final Invoice invoice : invoices.values()) {
+            for (final InvoiceModelDao invoice : invoices.values()) {
                 if (accountId.equals(invoice.getAccountId())) {
                     result.add(invoice);
                 }
@@ -222,7 +221,7 @@ public class MockInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public InvoicePayment postChargeback(final UUID invoicePaymentId, final BigDecimal amount, final InternalCallContext context) throws InvoiceApiException {
+    public InvoicePaymentModelDao postChargeback(final UUID invoicePaymentId, final BigDecimal amount, final InternalCallContext context) throws InvoiceApiException {
         throw new UnsupportedOperationException();
     }
 
@@ -237,47 +236,47 @@ public class MockInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public List<InvoicePayment> getChargebacksByAccountId(final UUID accountId, final InternalTenantContext context) {
+    public List<InvoicePaymentModelDao> getChargebacksByAccountId(final UUID accountId, final InternalTenantContext context) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public List<InvoicePayment> getChargebacksByPaymentId(final UUID paymentId, final InternalTenantContext context) {
+    public List<InvoicePaymentModelDao> getChargebacksByPaymentId(final UUID paymentId, final InternalTenantContext context) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public InvoicePayment getChargebackById(final UUID chargebackId, final InternalTenantContext context) throws InvoiceApiException {
+    public InvoicePaymentModelDao getChargebackById(final UUID chargebackId, final InternalTenantContext context) throws InvoiceApiException {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public InvoiceItem getExternalChargeById(final UUID externalChargeId, final InternalTenantContext context) throws InvoiceApiException {
+    public InvoiceItemModelDao getExternalChargeById(final UUID externalChargeId, final InternalTenantContext context) throws InvoiceApiException {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public InvoiceItem insertExternalCharge(final UUID accountId, @Nullable final UUID invoiceId, @Nullable final UUID bundleId,
-                                            @Nullable final String description, final BigDecimal amount, final LocalDate effectiveDate,
-                                            final Currency currency, final InternalCallContext context) {
+    public InvoiceItemModelDao insertExternalCharge(final UUID accountId, @Nullable final UUID invoiceId, @Nullable final UUID bundleId,
+                                                    @Nullable final String description, final BigDecimal amount, final LocalDate effectiveDate,
+                                                    final Currency currency, final InternalCallContext context) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public InvoiceItem getCreditById(final UUID creditId, final InternalTenantContext context) throws InvoiceApiException {
+    public InvoiceItemModelDao getCreditById(final UUID creditId, final InternalTenantContext context) throws InvoiceApiException {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public InvoiceItem insertCredit(final UUID accountId, final UUID invoiceId, final BigDecimal amount, final LocalDate effectiveDate,
-                                    final Currency currency, final InternalCallContext context) {
+    public InvoiceItemModelDao insertCredit(final UUID accountId, final UUID invoiceId, final BigDecimal amount, final LocalDate effectiveDate,
+                                            final Currency currency, final InternalCallContext context) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public InvoiceItem insertInvoiceItemAdjustment(final UUID accountId, final UUID invoiceId, final UUID invoiceItemId,
-                                                   final LocalDate effectiveDate, @Nullable final BigDecimal amount,
-                                                   @Nullable final Currency currency, final InternalCallContext context) {
+    public InvoiceItemModelDao insertInvoiceItemAdjustment(final UUID accountId, final UUID invoiceId, final UUID invoiceItemId,
+                                                           final LocalDate effectiveDate, @Nullable final BigDecimal amount,
+                                                           @Nullable final Currency currency, final InternalCallContext context) {
         throw new UnsupportedOperationException();
     }
 
@@ -287,9 +286,9 @@ public class MockInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public InvoicePayment createRefund(final UUID paymentId, final BigDecimal amount, final boolean isInvoiceAdjusted,
-                                       final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts, final UUID paymentCookieId,
-                                       final InternalCallContext context)
+    public InvoicePaymentModelDao createRefund(final UUID paymentId, final BigDecimal amount, final boolean isInvoiceAdjusted,
+                                               final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts, final UUID paymentCookieId,
+                                               final InternalCallContext context)
             throws InvoiceApiException {
         return null;
     }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/TestDefaultInvoiceDao.java b/invoice/src/test/java/com/ning/billing/invoice/dao/TestDefaultInvoiceDao.java
index b1c6ddc..e3bb748 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/TestDefaultInvoiceDao.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/TestDefaultInvoiceDao.java
@@ -33,30 +33,25 @@ import com.ning.billing.ErrorCode;
 import com.ning.billing.invoice.InvoiceTestSuite;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceApiException;
-import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.invoice.notification.NextBillingDatePoster;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
-import com.ning.billing.util.tag.dao.MockTagDao;
-import com.ning.billing.util.tag.dao.MockTagDefinitionDao;
-import com.ning.billing.util.tag.dao.TagDao;
-import com.ning.billing.util.tag.dao.TagDefinitionDao;
 
 import com.google.common.collect.ImmutableMap;
 
 public class TestDefaultInvoiceDao extends InvoiceTestSuite {
 
     private InvoiceSqlDao invoiceSqlDao;
-    private AuditedInvoiceDao dao;
+    private DefaultInvoiceDao dao;
 
     @BeforeMethod(groups = "fast")
     public void setUp() throws Exception {
         final IDBI idbi = Mockito.mock(IDBI.class);
         invoiceSqlDao = Mockito.mock(InvoiceSqlDao.class);
         Mockito.when(idbi.onDemand(InvoiceSqlDao.class)).thenReturn(invoiceSqlDao);
-        Mockito.when(invoiceSqlDao.getById(Mockito.anyString(), Mockito.<InternalTenantContext>any())).thenReturn(Mockito.mock(Invoice.class));
-        Mockito.when(invoiceSqlDao.inTransaction(Mockito.<Transaction<Void, InvoiceSqlDao>>any())).thenAnswer(new Answer() {
+        Mockito.when(invoiceSqlDao.getById(Mockito.anyString(), Mockito.<InternalTenantContext>any())).thenReturn(Mockito.mock(InvoiceModelDao.class));
+        Mockito.when(invoiceSqlDao.inTransaction(Mockito.<Transaction<Void, EntitySqlDao<InvoiceModelDao, Invoice>>>any())).thenAnswer(new Answer() {
             @Override
             public Object answer(final InvocationOnMock invocation) {
                 final Object[] args = invocation.getArguments();
@@ -70,9 +65,7 @@ public class TestDefaultInvoiceDao extends InvoiceTestSuite {
         });
 
         final NextBillingDatePoster poster = Mockito.mock(NextBillingDatePoster.class);
-        final TagDefinitionDao tagDefinitionDao = new MockTagDefinitionDao();
-        final TagDao tagDao = new MockTagDao();
-        dao = new AuditedInvoiceDao(idbi, poster, Mockito.mock(Clock.class), Mockito.mock(InternalBus.class));
+        dao = new DefaultInvoiceDao(idbi, poster, Mockito.mock(InternalBus.class));
     }
 
     @Test(groups = "fast")
@@ -105,24 +98,10 @@ public class TestDefaultInvoiceDao extends InvoiceTestSuite {
 
     private void verifyComputedRefundAmount(final BigDecimal paymentAmount, final BigDecimal requestedAmount,
                                             final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts, final BigDecimal expectedRefundAmount) throws InvoiceApiException {
-        final InvoicePayment invoicePayment = Mockito.mock(InvoicePayment.class);
+        final InvoicePaymentModelDao invoicePayment = Mockito.mock(InvoicePaymentModelDao.class);
         Mockito.when(invoicePayment.getAmount()).thenReturn(paymentAmount);
 
         final BigDecimal actualRefundAmount = dao.computePositiveRefundAmount(invoicePayment, requestedAmount, invoiceItemIdsWithAmounts);
         Assert.assertEquals(actualRefundAmount, expectedRefundAmount);
     }
-
-    @Test(groups = "fast")
-    public void testFindByNumber() throws Exception {
-        final Integer number = Integer.MAX_VALUE;
-        final Invoice invoice = Mockito.mock(Invoice.class);
-        Mockito.when(invoiceSqlDao.getByRecordId(number.longValue(), internalCallContext)).thenReturn(invoice);
-
-        Assert.assertEquals(dao.getByNumber(number, internalCallContext), invoice);
-        try {
-            dao.getByNumber(Integer.MIN_VALUE, internalCallContext);
-            Assert.fail();
-        } catch (InvoiceApiException e) {
-        }
-    }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java b/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java
index dd8c0ce..2b668f8 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java
@@ -16,11 +16,6 @@
 
 package com.ning.billing.invoice.dao;
 
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
-
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -72,6 +67,11 @@ import com.ning.billing.util.svcapi.junction.BillingModeType;
 
 import com.google.common.collect.ImmutableMap;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
 public class TestInvoiceDao extends InvoiceDaoTestBase {
 
     @Test(groups = "slow")
@@ -80,17 +80,17 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final Invoice invoice = new DefaultInvoice(accountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD);
         final LocalDate invoiceDate = invoice.getInvoiceDate();
 
-        invoiceDao.create(invoice, invoice.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice, true, internalCallContext);
 
-        final List<Invoice> invoices = invoiceDao.getInvoicesByAccount(accountId, internalCallContext);
+        final List<InvoiceModelDao> invoices = invoiceDao.getInvoicesByAccount(accountId, internalCallContext);
         assertNotNull(invoices);
         assertEquals(invoices.size(), 1);
-        final Invoice thisInvoice = invoices.get(0);
+        final InvoiceModelDao thisInvoice = invoices.get(0);
         assertEquals(invoice.getAccountId(), accountId);
         assertTrue(thisInvoice.getInvoiceDate().compareTo(invoiceDate) == 0);
         assertEquals(thisInvoice.getCurrency(), Currency.USD);
-        assertEquals(thisInvoice.getNumberOfItems(), 0);
-        assertTrue(thisInvoice.getBalance().compareTo(BigDecimal.ZERO) == 0);
+        assertEquals(thisInvoice.getInvoiceItems().size(), 0);
+        assertTrue(InvoiceModelDaoHelper.getBalance(thisInvoice).compareTo(BigDecimal.ZERO) == 0);
     }
 
     @Test(groups = "slow")
@@ -106,25 +106,26 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
                                                                  new BigDecimal("21.00"), new BigDecimal("7.00"), Currency.USD);
 
         invoice.addInvoiceItem(invoiceItem);
-        invoiceDao.create(invoice, invoice.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice, true, internalCallContext);
 
-        final Invoice savedInvoice = invoiceDao.getById(invoiceId, internalCallContext);
+        final InvoiceModelDao savedInvoice = invoiceDao.getById(invoiceId, internalCallContext);
         assertNotNull(savedInvoice);
-        assertEquals(savedInvoice.getBalance().compareTo(new BigDecimal("21.00")), 0);
-        assertEquals(savedInvoice.getPaidAmount(), BigDecimal.ZERO);
+        assertEquals(InvoiceModelDaoHelper.getBalance(savedInvoice).compareTo(new BigDecimal("21.00")), 0);
+        assertEquals(InvoiceModelDaoHelper.getPaidAmount(savedInvoice), BigDecimal.ZERO);
         assertEquals(savedInvoice.getInvoiceItems().size(), 1);
 
         final BigDecimal paymentAmount = new BigDecimal("11.00");
         final UUID paymentId = UUID.randomUUID();
 
-        invoiceDao.notifyOfPayment(new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoiceId, clock.getUTCNow().plusDays(12), paymentAmount, Currency.USD), internalCallContext);
+        final DefaultInvoicePayment defaultInvoicePayment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoiceId, clock.getUTCNow().plusDays(12), paymentAmount, Currency.USD);
+        invoiceDao.notifyOfPayment(new InvoicePaymentModelDao(defaultInvoicePayment), internalCallContext);
 
-        final Invoice retrievedInvoice = invoiceDao.getById(invoiceId, internalCallContext);
+        final InvoiceModelDao retrievedInvoice = invoiceDao.getById(invoiceId, internalCallContext);
         assertNotNull(retrievedInvoice);
         assertEquals(retrievedInvoice.getInvoiceItems().size(), 1);
-        assertEquals(retrievedInvoice.getChargedAmount().compareTo(new BigDecimal("21.00")), 0);
-        assertEquals(retrievedInvoice.getBalance().compareTo(new BigDecimal("10.00")), 0);
-        assertEquals(retrievedInvoice.getPaidAmount().compareTo(new BigDecimal("11.00")), 0);
+        assertEquals(InvoiceModelDaoHelper.getChargedAmount(retrievedInvoice).compareTo(new BigDecimal("21.00")), 0);
+        assertEquals(InvoiceModelDaoHelper.getBalance(retrievedInvoice).compareTo(new BigDecimal("10.00")), 0);
+        assertEquals(InvoiceModelDaoHelper.getPaidAmount(retrievedInvoice).compareTo(new BigDecimal("11.00")), 0);
     }
 
     @Test(groups = "slow")
@@ -132,10 +133,9 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         try {
             invoiceDao.getById(UUID.randomUUID(), internalCallContext);
             Assert.fail();
-        } catch (InvoiceApiException e) {
-            if (e.getCode() != ErrorCode.INVOICE_NOT_FOUND.getCode()) {
-                Assert.fail();
-            }
+        } catch (TransactionFailedException e) {
+            Assert.assertTrue(e.getCause() instanceof InvoiceApiException);
+            Assert.assertEquals(((InvoiceApiException) e.getCause()).getCode(), ErrorCode.INVOICE_NOT_FOUND.getCode());
         }
     }
 
@@ -157,7 +157,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         // Create invoice 1 (subscriptions 1-4)
         final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate, Currency.USD);
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
 
         final UUID invoiceId1 = invoice1.getId();
 
@@ -166,23 +166,23 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         final RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId1, "test plan", "test A", startDate, endDate,
                                                                     rate1, rate1, Currency.USD);
-        invoiceItemSqlDao.create(item1, internalCallContext);
+        createInvoiceItem(item1, internalCallContext);
 
         final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId2, "test plan", "test B", startDate, endDate,
                                                                     rate2, rate2, Currency.USD);
-        invoiceItemSqlDao.create(item2, internalCallContext);
+        createInvoiceItem(item2, internalCallContext);
 
         final RecurringInvoiceItem item3 = new RecurringInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId3, "test plan", "test C", startDate, endDate,
                                                                     rate3, rate3, Currency.USD);
-        invoiceItemSqlDao.create(item3, internalCallContext);
+        createInvoiceItem(item3, internalCallContext);
 
         final RecurringInvoiceItem item4 = new RecurringInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId4, "test plan", "test D", startDate, endDate,
                                                                     rate4, rate4, Currency.USD);
-        invoiceItemSqlDao.create(item4, internalCallContext);
+        createInvoiceItem(item4, internalCallContext);
 
         // Create invoice 2 (subscriptions 1-3)
         final DefaultInvoice invoice2 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate, Currency.USD);
-        invoiceDao.create(invoice2, invoice2.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice2, true, internalCallContext);
 
         final UUID invoiceId2 = invoice2.getId();
 
@@ -191,27 +191,27 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         final RecurringInvoiceItem item5 = new RecurringInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId1, "test plan", "test phase A", startDate, endDate,
                                                                     rate1, rate1, Currency.USD);
-        invoiceItemSqlDao.create(item5, internalCallContext);
+        createInvoiceItem(item5, internalCallContext);
 
         final RecurringInvoiceItem item6 = new RecurringInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId2, "test plan", "test phase B", startDate, endDate,
                                                                     rate2, rate2, Currency.USD);
-        invoiceItemSqlDao.create(item6, internalCallContext);
+        createInvoiceItem(item6, internalCallContext);
 
         final RecurringInvoiceItem item7 = new RecurringInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId3, "test plan", "test phase C", startDate, endDate,
                                                                     rate3, rate3, Currency.USD);
-        invoiceItemSqlDao.create(item7, internalCallContext);
+        createInvoiceItem(item7, internalCallContext);
 
         // Check that each subscription returns the correct number of invoices
-        final List<Invoice> items1 = invoiceDao.getInvoicesBySubscription(subscriptionId1, internalCallContext);
+        final List<InvoiceModelDao> items1 = invoiceDao.getInvoicesBySubscription(subscriptionId1, internalCallContext);
         assertEquals(items1.size(), 2);
 
-        final List<Invoice> items2 = invoiceDao.getInvoicesBySubscription(subscriptionId2, internalCallContext);
+        final List<InvoiceModelDao> items2 = invoiceDao.getInvoicesBySubscription(subscriptionId2, internalCallContext);
         assertEquals(items2.size(), 2);
 
-        final List<Invoice> items3 = invoiceDao.getInvoicesBySubscription(subscriptionId3, internalCallContext);
+        final List<InvoiceModelDao> items3 = invoiceDao.getInvoicesBySubscription(subscriptionId3, internalCallContext);
         assertEquals(items3.size(), 2);
 
-        final List<Invoice> items4 = invoiceDao.getInvoicesBySubscription(subscriptionId4, internalCallContext);
+        final List<InvoiceModelDao> items4 = invoiceDao.getInvoicesBySubscription(subscriptionId4, internalCallContext);
         assertEquals(items4.size(), 1);
     }
 
@@ -233,7 +233,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         // Create invoice 1 (subscriptions 1-4)
         final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate, Currency.USD);
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
 
         final UUID invoiceId1 = invoice1.getId();
 
@@ -242,52 +242,51 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         final FixedPriceInvoiceItem item1 = new FixedPriceInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId1, "test plan", "test A", startDate,
                                                                       rate1, Currency.USD);
-        invoiceItemSqlDao.create(item1, internalCallContext);
+        createInvoiceItem(item1, internalCallContext);
 
         final FixedPriceInvoiceItem item2 = new FixedPriceInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId2, "test plan", "test B", startDate,
                                                                       rate2, Currency.USD);
-        invoiceItemSqlDao.create(item2, internalCallContext);
+        createInvoiceItem(item2, internalCallContext);
 
         final FixedPriceInvoiceItem item3 = new FixedPriceInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId3, "test plan", "test C", startDate,
                                                                       rate3, Currency.USD);
-        invoiceItemSqlDao.create(item3, internalCallContext);
+        createInvoiceItem(item3, internalCallContext);
 
         final FixedPriceInvoiceItem item4 = new FixedPriceInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId4, "test plan", "test D", startDate,
                                                                       rate4, Currency.USD);
-        invoiceItemSqlDao.create(item4, internalCallContext);
+        createInvoiceItem(item4, internalCallContext);
 
         // create invoice 2 (subscriptions 1-3)
         final DefaultInvoice invoice2 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate, Currency.USD);
-        invoiceDao.create(invoice2, invoice2.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice2, true, internalCallContext);
 
         final UUID invoiceId2 = invoice2.getId();
 
         startDate = endDate;
-        endDate = startDate.plusMonths(1);
 
         final FixedPriceInvoiceItem item5 = new FixedPriceInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId1, "test plan", "test phase A", startDate,
                                                                       rate1, Currency.USD);
-        invoiceItemSqlDao.create(item5, internalCallContext);
+        createInvoiceItem(item5, internalCallContext);
 
         final FixedPriceInvoiceItem item6 = new FixedPriceInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId2, "test plan", "test phase B", startDate,
                                                                       rate2, Currency.USD);
-        invoiceItemSqlDao.create(item6, internalCallContext);
+        createInvoiceItem(item6, internalCallContext);
 
         final FixedPriceInvoiceItem item7 = new FixedPriceInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId3, "test plan", "test phase C", startDate,
                                                                       rate3, Currency.USD);
-        invoiceItemSqlDao.create(item7, internalCallContext);
+        createInvoiceItem(item7, internalCallContext);
 
         // check that each subscription returns the correct number of invoices
-        final List<Invoice> items1 = invoiceDao.getInvoicesBySubscription(subscriptionId1, internalCallContext);
+        final List<InvoiceModelDao> items1 = invoiceDao.getInvoicesBySubscription(subscriptionId1, internalCallContext);
         assertEquals(items1.size(), 2);
 
-        final List<Invoice> items2 = invoiceDao.getInvoicesBySubscription(subscriptionId2, internalCallContext);
+        final List<InvoiceModelDao> items2 = invoiceDao.getInvoicesBySubscription(subscriptionId2, internalCallContext);
         assertEquals(items2.size(), 2);
 
-        final List<Invoice> items3 = invoiceDao.getInvoicesBySubscription(subscriptionId3, internalCallContext);
+        final List<InvoiceModelDao> items3 = invoiceDao.getInvoicesBySubscription(subscriptionId3, internalCallContext);
         assertEquals(items3.size(), 2);
 
-        final List<Invoice> items4 = invoiceDao.getInvoicesBySubscription(subscriptionId4, internalCallContext);
+        final List<InvoiceModelDao> items4 = invoiceDao.getInvoicesBySubscription(subscriptionId4, internalCallContext);
         assertEquals(items4.size(), 1);
     }
 
@@ -309,7 +308,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         // Create invoice 1 (subscriptions 1-4)
         final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate, Currency.USD);
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
 
         final UUID invoiceId1 = invoice1.getId();
 
@@ -318,39 +317,39 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         final RecurringInvoiceItem recurringItem1 = new RecurringInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId1, "test plan", "test A", startDate, endDate,
                                                                              rate1, rate1, Currency.USD);
-        invoiceItemSqlDao.create(recurringItem1, internalCallContext);
+        createInvoiceItem(recurringItem1, internalCallContext);
 
         final RecurringInvoiceItem recurringItem2 = new RecurringInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId2, "test plan", "test B", startDate, endDate,
                                                                              rate2, rate2, Currency.USD);
-        invoiceItemSqlDao.create(recurringItem2, internalCallContext);
+        createInvoiceItem(recurringItem2, internalCallContext);
 
         final RecurringInvoiceItem recurringItem3 = new RecurringInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId3, "test plan", "test C", startDate, endDate,
                                                                              rate3, rate3, Currency.USD);
-        invoiceItemSqlDao.create(recurringItem3, internalCallContext);
+        createInvoiceItem(recurringItem3, internalCallContext);
 
         final RecurringInvoiceItem recurringItem4 = new RecurringInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId4, "test plan", "test D", startDate, endDate,
                                                                              rate4, rate4, Currency.USD);
-        invoiceItemSqlDao.create(recurringItem4, internalCallContext);
+        createInvoiceItem(recurringItem4, internalCallContext);
 
         final FixedPriceInvoiceItem fixedItem1 = new FixedPriceInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId1, "test plan", "test A", startDate,
                                                                            rate1, Currency.USD);
-        invoiceItemSqlDao.create(fixedItem1, internalCallContext);
+        createInvoiceItem(fixedItem1, internalCallContext);
 
         final FixedPriceInvoiceItem fixedItem2 = new FixedPriceInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId2, "test plan", "test B", startDate,
                                                                            rate2, Currency.USD);
-        invoiceItemSqlDao.create(fixedItem2, internalCallContext);
+        createInvoiceItem(fixedItem2, internalCallContext);
 
         final FixedPriceInvoiceItem fixedItem3 = new FixedPriceInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId3, "test plan", "test C", startDate,
                                                                            rate3, Currency.USD);
-        invoiceItemSqlDao.create(fixedItem3, internalCallContext);
+        createInvoiceItem(fixedItem3, internalCallContext);
 
         final FixedPriceInvoiceItem fixedItem4 = new FixedPriceInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId4, "test plan", "test D", startDate,
                                                                            rate4, Currency.USD);
-        invoiceItemSqlDao.create(fixedItem4, internalCallContext);
+        createInvoiceItem(fixedItem4, internalCallContext);
 
         // create invoice 2 (subscriptions 1-3)
         final DefaultInvoice invoice2 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate, Currency.USD);
-        invoiceDao.create(invoice2, invoice2.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice2, true, internalCallContext);
 
         final UUID invoiceId2 = invoice2.getId();
 
@@ -359,38 +358,38 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         final RecurringInvoiceItem recurringItem5 = new RecurringInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId1, "test plan", "test phase A", startDate, endDate,
                                                                              rate1, rate1, Currency.USD);
-        invoiceItemSqlDao.create(recurringItem5, internalCallContext);
+        createInvoiceItem(recurringItem5, internalCallContext);
 
         final RecurringInvoiceItem recurringItem6 = new RecurringInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId2, "test plan", "test phase B", startDate, endDate,
                                                                              rate2, rate2, Currency.USD);
-        invoiceItemSqlDao.create(recurringItem6, internalCallContext);
+        createInvoiceItem(recurringItem6, internalCallContext);
 
         final RecurringInvoiceItem recurringItem7 = new RecurringInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId3, "test plan", "test phase C", startDate, endDate,
                                                                              rate3, rate3, Currency.USD);
-        invoiceItemSqlDao.create(recurringItem7, internalCallContext);
+        createInvoiceItem(recurringItem7, internalCallContext);
         final FixedPriceInvoiceItem fixedItem5 = new FixedPriceInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId1, "test plan", "test phase A", startDate,
                                                                            rate1, Currency.USD);
-        invoiceItemSqlDao.create(fixedItem5, internalCallContext);
+        createInvoiceItem(fixedItem5, internalCallContext);
 
         final FixedPriceInvoiceItem fixedItem6 = new FixedPriceInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId2, "test plan", "test phase B", startDate,
                                                                            rate2, Currency.USD);
-        invoiceItemSqlDao.create(fixedItem6, internalCallContext);
+        createInvoiceItem(fixedItem6, internalCallContext);
 
         final FixedPriceInvoiceItem fixedItem7 = new FixedPriceInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId3, "test plan", "test phase C", startDate,
                                                                            rate3, Currency.USD);
-        invoiceItemSqlDao.create(fixedItem7, internalCallContext);
+        createInvoiceItem(fixedItem7, internalCallContext);
 
         // check that each subscription returns the correct number of invoices
-        final List<Invoice> items1 = invoiceDao.getInvoicesBySubscription(subscriptionId1, internalCallContext);
+        final List<InvoiceModelDao> items1 = invoiceDao.getInvoicesBySubscription(subscriptionId1, internalCallContext);
         assertEquals(items1.size(), 4);
 
-        final List<Invoice> items2 = invoiceDao.getInvoicesBySubscription(subscriptionId2, internalCallContext);
+        final List<InvoiceModelDao> items2 = invoiceDao.getInvoicesBySubscription(subscriptionId2, internalCallContext);
         assertEquals(items2.size(), 4);
 
-        final List<Invoice> items3 = invoiceDao.getInvoicesBySubscription(subscriptionId3, internalCallContext);
+        final List<InvoiceModelDao> items3 = invoiceDao.getInvoicesBySubscription(subscriptionId3, internalCallContext);
         assertEquals(items3.size(), 4);
 
-        final List<Invoice> items4 = invoiceDao.getInvoicesBySubscription(subscriptionId4, internalCallContext);
+        final List<InvoiceModelDao> items4 = invoiceDao.getInvoicesBySubscription(subscriptionId4, internalCallContext);
         assertEquals(items4.size(), 2);
     }
 
@@ -399,13 +398,13 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final UUID accountId = UUID.randomUUID();
         final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
         final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
 
         final LocalDate targetDate2 = new LocalDate(2011, 12, 6);
         final Invoice invoice2 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate2, Currency.USD);
-        invoiceDao.create(invoice2, invoice2.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice2, true, internalCallContext);
 
-        List<Invoice> invoices;
+        List<InvoiceModelDao> invoices;
         invoices = invoiceDao.getInvoicesByAccount(accountId, new LocalDate(2011, 1, 1), internalCallContext);
         assertEquals(invoices.size(), 2);
 
@@ -428,7 +427,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final UUID bundleId = UUID.randomUUID();
         final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
         final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
 
         final LocalDate startDate = new LocalDate(2011, 3, 1);
         final LocalDate endDate = startDate.plusMonths(1);
@@ -438,15 +437,15 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         final RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase A", startDate,
                                                                     endDate, rate1, rate1, Currency.USD);
-        invoiceItemSqlDao.create(item1, internalCallContext);
+        createInvoiceItem(item1, internalCallContext);
 
         final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate,
                                                                     endDate, rate2, rate2, Currency.USD);
-        invoiceItemSqlDao.create(item2, internalCallContext);
+        createInvoiceItem(item2, internalCallContext);
 
         final BigDecimal payment1 = new BigDecimal("48.0");
         final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, UUID.randomUUID(), invoice1.getId(), new DateTime(), payment1, Currency.USD);
-        invoicePaymentDao.create(payment, internalCallContext);
+        createPayment(payment, internalCallContext);
 
         final BigDecimal balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(rate1.add(rate2).subtract(payment1)), 0);
@@ -458,7 +457,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final UUID bundleId = UUID.randomUUID();
         final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
         final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
 
         final LocalDate startDate = new LocalDate(2011, 3, 1);
         final LocalDate endDate = startDate.plusMonths(1);
@@ -467,10 +466,10 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         final RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase A", startDate,
                                                                     endDate, rate1, rate1, Currency.USD);
-        invoiceItemSqlDao.create(item1, internalCallContext);
+        createInvoiceItem(item1, internalCallContext);
 
         final CreditAdjInvoiceItem creditItem = new CreditAdjInvoiceItem(invoice1.getId(), accountId, new LocalDate(), rate1.negate(), Currency.USD);
-        invoiceItemSqlDao.create(creditItem, internalCallContext);
+        createInvoiceItem(creditItem, internalCallContext);
 
         final BigDecimal balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(BigDecimal.ZERO), 0);
@@ -482,7 +481,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final UUID bundleId = UUID.randomUUID();
         final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
         final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
 
         final LocalDate startDate = new LocalDate(2011, 3, 1);
         final LocalDate endDate = startDate.plusMonths(1);
@@ -492,11 +491,11 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         final RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase A", startDate, endDate,
                                                                     rate1, rate1, Currency.USD);
-        invoiceItemSqlDao.create(item1, internalCallContext);
+        createInvoiceItem(item1, internalCallContext);
 
         final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate, endDate,
                                                                     rate2, rate2, Currency.USD);
-        invoiceItemSqlDao.create(item2, internalCallContext);
+        createInvoiceItem(item2, internalCallContext);
 
         final BigDecimal balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(rate1.add(rate2)), 0);
@@ -507,11 +506,11 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final UUID accountId = UUID.randomUUID();
         final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
         final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
 
         final BigDecimal payment1 = new BigDecimal("48.0");
         final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, UUID.randomUUID(), invoice1.getId(), new DateTime(), payment1, Currency.USD);
-        invoicePaymentDao.create(payment, internalCallContext);
+        createPayment(payment, internalCallContext);
 
         final BigDecimal balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(BigDecimal.ZERO.subtract(payment1)), 0);
@@ -533,19 +532,18 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final UUID bundleId = UUID.randomUUID();
         final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
         final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
 
         final LocalDate startDate = new LocalDate(2011, 3, 1);
         final LocalDate endDate = startDate.plusMonths(1);
 
         final BigDecimal rate1 = new BigDecimal("20.0");
         final BigDecimal refund1 = new BigDecimal("7.00");
-        final BigDecimal rate2 = new BigDecimal("10.0");
 
         // Recurring item
         final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate,
                                                                     endDate, rate1, rate1, Currency.USD);
-        invoiceItemSqlDao.create(item2, internalCallContext);
+        createInvoiceItem(item2, internalCallContext);
         BigDecimal balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(new BigDecimal("20.00")), 0);
 
@@ -553,7 +551,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final UUID paymentId = UUID.randomUUID();
         final BigDecimal payment1 = rate1;
         final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoice1.getId(), new DateTime(), payment1, Currency.USD);
-        invoicePaymentDao.create(payment, internalCallContext);
+        createPayment(payment, internalCallContext);
         balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(new BigDecimal("0.00")), 0);
 
@@ -567,24 +565,24 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
     }
 
     @Test(groups = "slow")
-    private void testFullRefundWithRepairAndInvoiceItemAdjustment() throws InvoiceApiException {
+    public void testFullRefundWithRepairAndInvoiceItemAdjustment() throws InvoiceApiException, EntityPersistenceException {
         final BigDecimal refundAmount = new BigDecimal("20.00");
         testRefundWithRepairAndInvoiceItemAdjustmentInternal(refundAmount);
     }
 
     @Test(groups = "slow")
-    private void testPartialRefundWithRepairAndInvoiceItemAdjustment() throws InvoiceApiException {
+    public void testPartialRefundWithRepairAndInvoiceItemAdjustment() throws InvoiceApiException, EntityPersistenceException {
         final BigDecimal refundAmount = new BigDecimal("7.00");
         testRefundWithRepairAndInvoiceItemAdjustmentInternal(refundAmount);
     }
 
-    private void testRefundWithRepairAndInvoiceItemAdjustmentInternal(final BigDecimal refundAmount) throws InvoiceApiException {
+    private void testRefundWithRepairAndInvoiceItemAdjustmentInternal(final BigDecimal refundAmount) throws InvoiceApiException, EntityPersistenceException {
         final UUID accountId = UUID.randomUUID();
         final UUID bundleId = UUID.randomUUID();
         final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
 
         final Invoice invoice = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
-        invoiceDao.create(invoice, invoice.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice, true, internalCallContext);
 
         final LocalDate startDate = new LocalDate(2011, 3, 1);
         final LocalDate endDate = startDate.plusMonths(1);
@@ -594,7 +592,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         // Recurring item
         final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate,
                                                                     endDate, amount, amount, Currency.USD);
-        invoiceItemSqlDao.create(item2, internalCallContext);
+        createInvoiceItem(item2, internalCallContext);
         BigDecimal balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(new BigDecimal("20.00")), 0);
 
@@ -602,18 +600,18 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final UUID paymentId = UUID.randomUUID();
         final BigDecimal payment1 = amount;
         final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoice.getId(), new DateTime(), payment1, Currency.USD);
-        invoicePaymentDao.create(payment, internalCallContext);
+        createPayment(payment, internalCallContext);
         balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(new BigDecimal("0.00")), 0);
 
         // Repair the item (And add CBA item that should be generated)
         final InvoiceItem repairItem = new RepairAdjInvoiceItem(invoice.getId(), accountId, startDate, endDate, amount.negate(), Currency.USD, item2.getId());
-        invoiceItemSqlDao.create(repairItem, internalCallContext);
+        createInvoiceItem(repairItem, internalCallContext);
 
         final InvoiceItem cbaItem = new CreditBalanceAdjInvoiceItem(invoice.getId(), accountId, startDate, amount, Currency.USD);
-        invoiceItemSqlDao.create(cbaItem, internalCallContext);
+        createInvoiceItem(cbaItem, internalCallContext);
 
-        Map<UUID, BigDecimal> itemAdjustment = new HashMap<UUID, BigDecimal>();
+        final Map<UUID, BigDecimal> itemAdjustment = new HashMap<UUID, BigDecimal>();
         itemAdjustment.put(item2.getId(), refundAmount);
 
         invoiceDao.createRefund(paymentId, refundAmount, true, itemAdjustment, UUID.randomUUID(), internalCallContext);
@@ -621,7 +619,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         final boolean partialRefund = refundAmount.compareTo(amount) < 0;
         final BigDecimal cba = invoiceDao.getAccountCBA(accountId, internalCallContext);
-        final Invoice savedInvoice = invoiceDao.getById(invoice.getId(), internalCallContext);
+        final InvoiceModelDao savedInvoice = invoiceDao.getById(invoice.getId(), internalCallContext);
         assertEquals(cba.compareTo(new BigDecimal("20.0")), 0);
         if (partialRefund) {
             // IB = 20 (rec) - 20 (repair) + 20 (cba) - (20 -7) = 7;  AB = IB - CBA = 7 - 20 = -13
@@ -635,29 +633,29 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
     @Test(groups = "slow")
     public void testAccountBalanceWithSmallRefundAndCBANoAdj() throws InvoiceApiException, EntityPersistenceException {
-        BigDecimal refundAmount = new BigDecimal("7.00");
-        BigDecimal expectedBalance = new BigDecimal("-3.00");
+        final BigDecimal refundAmount = new BigDecimal("7.00");
+        final BigDecimal expectedBalance = new BigDecimal("-3.00");
         testAccountBalanceWithRefundAndCBAInternal(false, refundAmount, expectedBalance);
     }
 
     @Test(groups = "slow")
     public void testAccountBalanceWithSmallRefundAndCBAWithAdj() throws InvoiceApiException, EntityPersistenceException {
-        BigDecimal refundAmount = new BigDecimal("7.00");
-        BigDecimal expectedBalance = new BigDecimal("-10.00");
+        final BigDecimal refundAmount = new BigDecimal("7.00");
+        final BigDecimal expectedBalance = new BigDecimal("-10.00");
         testAccountBalanceWithRefundAndCBAInternal(true, refundAmount, expectedBalance);
     }
 
     @Test(groups = "slow")
     public void testAccountBalanceWithLargeRefundAndCBANoAdj() throws InvoiceApiException, EntityPersistenceException {
-        BigDecimal refundAmount = new BigDecimal("20.00");
-        BigDecimal expectedBalance = new BigDecimal("10.00");
+        final BigDecimal refundAmount = new BigDecimal("20.00");
+        final BigDecimal expectedBalance = new BigDecimal("10.00");
         testAccountBalanceWithRefundAndCBAInternal(false, refundAmount, expectedBalance);
     }
 
     @Test(groups = "slow")
     public void testAccountBalanceWithLargeRefundAndCBAWithAdj() throws InvoiceApiException, EntityPersistenceException {
-        BigDecimal refundAmount = new BigDecimal("20.00");
-        BigDecimal expectedBalance = new BigDecimal("-10.00");
+        final BigDecimal refundAmount = new BigDecimal("20.00");
+        final BigDecimal expectedBalance = new BigDecimal("-10.00");
         testAccountBalanceWithRefundAndCBAInternal(true, refundAmount, expectedBalance);
     }
 
@@ -666,7 +664,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final UUID bundleId = UUID.randomUUID();
         final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
         final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
 
         final LocalDate startDate = new LocalDate(2011, 3, 1);
         final LocalDate endDate = startDate.plusMonths(1);
@@ -678,7 +676,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         // Fixed Item
         final FixedPriceInvoiceItem item1 = new FixedPriceInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase A", startDate,
                                                                       amount1, Currency.USD);
-        invoiceItemSqlDao.create(item1, internalCallContext);
+        createInvoiceItem(item1, internalCallContext);
 
         BigDecimal balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(new BigDecimal("5.00")), 0);
@@ -686,7 +684,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         // Recurring item
         final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate,
                                                                     endDate, rate1, rate1, Currency.USD);
-        invoiceItemSqlDao.create(item2, internalCallContext);
+        createInvoiceItem(item2, internalCallContext);
         balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(new BigDecimal("25.00")), 0);
 
@@ -694,7 +692,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final UUID paymentId = UUID.randomUUID();
         final BigDecimal payment1 = amount1.add(rate1);
         final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoice1.getId(), new DateTime(), payment1, Currency.USD);
-        invoicePaymentDao.create(payment, internalCallContext);
+        createPayment(payment, internalCallContext);
         balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(new BigDecimal("0.00")), 0);
 
@@ -702,14 +700,14 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final RepairAdjInvoiceItem item2Repair = new RepairAdjInvoiceItem(invoice1.getId(), accountId, startDate, endDate, rate1.negate(), Currency.USD, item2.getId());
         final RecurringInvoiceItem item2Replace = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate,
                                                                            endDate, rate2, rate2, Currency.USD);
-        invoiceItemSqlDao.create(item2Repair, internalCallContext);
-        invoiceItemSqlDao.create(item2Replace, internalCallContext);
+        createInvoiceItem(item2Repair, internalCallContext);
+        createInvoiceItem(item2Replace, internalCallContext);
         balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(new BigDecimal("-10.00")), 0);
 
         // CBA
         final CreditBalanceAdjInvoiceItem cbaItem = new CreditBalanceAdjInvoiceItem(invoice1.getId(), accountId, new LocalDate(), balance.negate(), Currency.USD);
-        invoiceItemSqlDao.create(cbaItem, internalCallContext);
+        createInvoiceItem(cbaItem, internalCallContext);
         balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(new BigDecimal("-10.00")), 0);
         BigDecimal cba = invoiceDao.getAccountCBA(accountId, internalCallContext);
@@ -731,20 +729,20 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final UUID bundleId = UUID.randomUUID();
         final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
         final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
 
         // CREATE INVOICE WITH A (just) CBA. Should not happen, but that does not matter for that test
         final CreditBalanceAdjInvoiceItem cbaItem = new CreditBalanceAdjInvoiceItem(invoice1.getId(), accountId, new LocalDate(), new BigDecimal("20.0"), Currency.USD);
-        invoiceItemSqlDao.create(cbaItem, internalCallContext);
+        createInvoiceItem(cbaItem, internalCallContext);
 
-        final InvoiceItem charge = invoiceDao.insertExternalCharge(accountId, null, bundleId, "bla", new BigDecimal("15.0"), clock.getUTCNow().toLocalDate(), Currency.USD, internalCallContext);
+        final InvoiceItemModelDao charge = invoiceDao.insertExternalCharge(accountId, null, bundleId, "bla", new BigDecimal("15.0"), clock.getUTCNow().toLocalDate(), Currency.USD, internalCallContext);
 
-        final Invoice newInvoice = invoiceDao.getById(charge.getInvoiceId(), internalCallContext);
-        List<InvoiceItem> items = newInvoice.getInvoiceItems();
+        final InvoiceModelDao newInvoice = invoiceDao.getById(charge.getInvoiceId(), internalCallContext);
+        final List<InvoiceItemModelDao> items = newInvoice.getInvoiceItems();
         assertEquals(items.size(), 2);
-        for (InvoiceItem cur : items) {
+        for (final InvoiceItemModelDao cur : items) {
             if (!cur.getId().equals(charge.getId())) {
-                assertEquals(cur.getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
+                assertEquals(cur.getType(), InvoiceItemType.CBA_ADJ);
                 assertTrue(cur.getAmount().compareTo(new BigDecimal("-15.00")) == 0);
                 break;
             }
@@ -757,7 +755,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final UUID bundleId = UUID.randomUUID();
         final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
         final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
 
         final LocalDate startDate = new LocalDate(2011, 3, 1);
         final LocalDate endDate = startDate.plusMonths(1);
@@ -769,7 +767,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         // Fixed Item
         final FixedPriceInvoiceItem item1 = new FixedPriceInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase A", startDate,
                                                                       amount1, Currency.USD);
-        invoiceItemSqlDao.create(item1, internalCallContext);
+        createInvoiceItem(item1, internalCallContext);
 
         BigDecimal balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(new BigDecimal("5.00")), 0);
@@ -777,14 +775,14 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         // Recurring item
         final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate,
                                                                     endDate, rate1, rate1, Currency.USD);
-        invoiceItemSqlDao.create(item2, internalCallContext);
+        createInvoiceItem(item2, internalCallContext);
         balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(new BigDecimal("25.00")), 0);
 
         // Pay the whole thing
         final BigDecimal payment1 = amount1.add(rate1);
         final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, UUID.randomUUID(), invoice1.getId(), new DateTime(), payment1, Currency.USD);
-        invoicePaymentDao.create(payment, internalCallContext);
+        createPayment(payment, internalCallContext);
         balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(new BigDecimal("0.00")), 0);
 
@@ -792,14 +790,14 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final RepairAdjInvoiceItem item2Repair = new RepairAdjInvoiceItem(invoice1.getId(), accountId, startDate, endDate, rate1.negate(), Currency.USD, item2.getId());
         final RecurringInvoiceItem item2Replace = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate,
                                                                            endDate, rate2, rate2, Currency.USD);
-        invoiceItemSqlDao.create(item2Repair, internalCallContext);
-        invoiceItemSqlDao.create(item2Replace, internalCallContext);
+        createInvoiceItem(item2Repair, internalCallContext);
+        createInvoiceItem(item2Replace, internalCallContext);
         balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(new BigDecimal("-10.00")), 0);
 
         // CBA
         final CreditBalanceAdjInvoiceItem cbaItem = new CreditBalanceAdjInvoiceItem(invoice1.getId(), accountId, new LocalDate(), balance.negate(), Currency.USD);
-        invoiceItemSqlDao.create(cbaItem, internalCallContext);
+        createInvoiceItem(cbaItem, internalCallContext);
         balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(new BigDecimal("-10.00")), 0);
         BigDecimal cba = invoiceDao.getAccountCBA(accountId, internalCallContext);
@@ -807,9 +805,9 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         // partial REFUND on the payment (along with CBA generated by the system)
         final InvoicePayment refund = new DefaultInvoicePayment(UUID.randomUUID(), InvoicePaymentType.ATTEMPT, UUID.randomUUID(), invoice1.getId(), new DateTime(), rate2.negate(), Currency.USD, null, payment.getId());
-        invoicePaymentDao.create(refund, internalCallContext);
+        createPayment(refund, internalCallContext);
         final CreditBalanceAdjInvoiceItem cbaItem2 = new CreditBalanceAdjInvoiceItem(invoice1.getId(), accountId, new LocalDate(), rate2.negate(), Currency.USD);
-        invoiceItemSqlDao.create(cbaItem2, internalCallContext);
+        createInvoiceItem(cbaItem2, internalCallContext);
 
         balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(BigDecimal.ZERO), 0);
@@ -819,11 +817,11 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         // NEXT RECURRING on invoice 2
 
         final Invoice invoice2 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1.plusMonths(1), Currency.USD);
-        invoiceDao.create(invoice2, invoice2.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice2, true, internalCallContext);
 
         final RecurringInvoiceItem nextItem = new RecurringInvoiceItem(invoice2.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test bla", startDate.plusMonths(1),
                                                                        endDate.plusMonths(1), rate2, rate2, Currency.USD);
-        invoiceItemSqlDao.create(nextItem, internalCallContext);
+        createInvoiceItem(nextItem, internalCallContext);
         balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(new BigDecimal("10.00")), 0);
         cba = invoiceDao.getAccountCBA(accountId, internalCallContext);
@@ -831,7 +829,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         // FINALLY ISSUE A CREDIT ADJ
         final CreditAdjInvoiceItem creditItem = new CreditAdjInvoiceItem(invoice2.getId(), accountId, new LocalDate(), rate2.negate(), Currency.USD);
-        invoiceItemSqlDao.create(creditItem, internalCallContext);
+        createInvoiceItem(creditItem, internalCallContext);
         balance = invoiceDao.getAccountBalance(accountId, internalCallContext);
         assertEquals(balance.compareTo(new BigDecimal("0.00")), 0);
         cba = invoiceDao.getAccountCBA(accountId, internalCallContext);
@@ -850,20 +848,20 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         invoiceDao.insertCredit(accountId, null, creditAmount, effectiveDate, Currency.USD, internalCallContext);
 
-        final List<Invoice> invoices = invoiceDao.getAllInvoicesByAccount(accountId, internalCallContext);
+        final List<InvoiceModelDao> invoices = invoiceDao.getAllInvoicesByAccount(accountId, internalCallContext);
         assertEquals(invoices.size(), 1);
 
-        final Invoice invoice = invoices.get(0);
-        assertTrue(invoice.getBalance().compareTo(BigDecimal.ZERO) == 0);
-        final List<InvoiceItem> invoiceItems = invoice.getInvoiceItems();
+        final InvoiceModelDao invoice = invoices.get(0);
+        assertTrue(InvoiceModelDaoHelper.getBalance(invoice).compareTo(BigDecimal.ZERO) == 0);
+        final List<InvoiceItemModelDao> invoiceItems = invoice.getInvoiceItems();
         assertEquals(invoiceItems.size(), 2);
         boolean foundCredit = false;
         boolean foundCBA = false;
-        for (final InvoiceItem cur : invoiceItems) {
-            if (cur.getInvoiceItemType() == InvoiceItemType.CREDIT_ADJ) {
+        for (final InvoiceItemModelDao cur : invoiceItems) {
+            if (cur.getType() == InvoiceItemType.CREDIT_ADJ) {
                 foundCredit = true;
                 assertTrue(cur.getAmount().compareTo(creditAmount.negate()) == 0);
-            } else if (cur.getInvoiceItemType() == InvoiceItemType.CBA_ADJ) {
+            } else if (cur.getType() == InvoiceItemType.CBA_ADJ) {
                 foundCBA = true;
                 assertTrue(cur.getAmount().compareTo(creditAmount) == 0);
             }
@@ -904,7 +902,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         // Create one invoice with a fixed invoice item
         final LocalDate targetDate = new LocalDate(2011, 2, 15);
         final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate, Currency.USD);
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
 
         final LocalDate startDate = new LocalDate(2011, 3, 1);
 
@@ -913,27 +911,27 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         // Fixed Item
         final FixedPriceInvoiceItem item1 = new FixedPriceInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase A", startDate,
                                                                       amount1, Currency.USD);
-        invoiceItemSqlDao.create(item1, internalCallContext);
+        createInvoiceItem(item1, internalCallContext);
 
         // Create the credit item
         final LocalDate effectiveDate = new LocalDate(2011, 3, 1);
 
         invoiceDao.insertCredit(accountId, invoice1.getId(), creditAmount, effectiveDate, Currency.USD, internalCallContext);
 
-        final List<Invoice> invoices = invoiceDao.getAllInvoicesByAccount(accountId, internalCallContext);
+        final List<InvoiceModelDao> invoices = invoiceDao.getAllInvoicesByAccount(accountId, internalCallContext);
         assertEquals(invoices.size(), 1);
 
-        final Invoice invoice = invoices.get(0);
-        assertTrue(invoice.getBalance().compareTo(expectedBalance) == 0);
-        final List<InvoiceItem> invoiceItems = invoice.getInvoiceItems();
+        final InvoiceModelDao invoice = invoices.get(0);
+        assertTrue(InvoiceModelDaoHelper.getBalance(invoice).compareTo(expectedBalance) == 0);
+        final List<InvoiceItemModelDao> invoiceItems = invoice.getInvoiceItems();
         assertEquals(invoiceItems.size(), expectCBA ? 3 : 2);
         boolean foundCredit = false;
         boolean foundCBA = false;
-        for (final InvoiceItem cur : invoiceItems) {
-            if (cur.getInvoiceItemType() == InvoiceItemType.CREDIT_ADJ) {
+        for (final InvoiceItemModelDao cur : invoiceItems) {
+            if (cur.getType() == InvoiceItemType.CREDIT_ADJ) {
                 foundCredit = true;
                 assertTrue(cur.getAmount().compareTo(creditAmount.negate()) == 0);
-            } else if (cur.getInvoiceItemType() == InvoiceItemType.CBA_ADJ) {
+            } else if (cur.getType() == InvoiceItemType.CBA_ADJ) {
                 foundCBA = true;
             }
         }
@@ -947,7 +945,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final UUID bundleId = UUID.randomUUID();
         final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
         final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
 
         final LocalDate startDate = new LocalDate(2011, 3, 1);
         final LocalDate endDate = startDate.plusMonths(1);
@@ -957,14 +955,14 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         final RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase A", startDate, endDate,
                                                                     rate1, rate1, Currency.USD);
-        invoiceItemSqlDao.create(item1, internalCallContext);
+        createInvoiceItem(item1, internalCallContext);
 
         final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate, endDate,
                                                                     rate2, rate2, Currency.USD);
-        invoiceItemSqlDao.create(item2, internalCallContext);
+        createInvoiceItem(item2, internalCallContext);
 
         LocalDate upToDate;
-        Collection<Invoice> invoices;
+        Collection<InvoiceModelDao> invoices;
 
         upToDate = new LocalDate(2011, 1, 1);
         invoices = invoiceDao.getUnpaidInvoicesByAccountId(accountId, upToDate, internalCallContext);
@@ -976,7 +974,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         final LocalDate targetDate2 = new LocalDate(2011, 7, 1);
         final Invoice invoice2 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate2, Currency.USD);
-        invoiceDao.create(invoice2, invoice2.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice2, true, internalCallContext);
 
         final LocalDate startDate2 = new LocalDate(2011, 6, 1);
         final LocalDate endDate2 = startDate2.plusMonths(3);
@@ -985,7 +983,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         final RecurringInvoiceItem item3 = new RecurringInvoiceItem(invoice2.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase C", startDate2, endDate2,
                                                                     rate3, rate3, Currency.USD);
-        invoiceItemSqlDao.create(item3, internalCallContext);
+        createInvoiceItem(item3, internalCallContext);
 
         upToDate = new LocalDate(2011, 1, 1);
         invoices = invoiceDao.getUnpaidInvoicesByAccountId(accountId, upToDate, internalCallContext);
@@ -1046,14 +1044,14 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         assertEquals(invoice2.getBalance(), FIVE);
         invoiceList.add(invoice2);
 
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
-        invoiceDao.create(invoice2, invoice2.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
+        createInvoice(invoice2, true, internalCallContext);
 
-        final Invoice savedInvoice1 = invoiceDao.getById(invoice1.getId(), internalCallContext);
-        assertEquals(savedInvoice1.getBalance(), ZERO);
+        final InvoiceModelDao savedInvoice1 = invoiceDao.getById(invoice1.getId(), internalCallContext);
+        assertEquals(InvoiceModelDaoHelper.getBalance(savedInvoice1), ZERO);
 
-        final Invoice savedInvoice2 = invoiceDao.getById(invoice2.getId(), internalCallContext);
-        assertEquals(savedInvoice2.getBalance(), FIFTEEN);
+        final InvoiceModelDao savedInvoice2 = invoiceDao.getById(invoice2.getId(), internalCallContext);
+        assertEquals(InvoiceModelDaoHelper.getBalance(savedInvoice2), FIFTEEN);
     }
 
     @Test(groups = "slow")
@@ -1121,7 +1119,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final List<Invoice> invoiceList = new ArrayList<Invoice>();
         invoiceList.add(invoice1);
 
-        //invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), internalCallContext);
+        //createInvoice(invoice1, invoice1.getTargetDate().getDayOfMonth(), internalCallContext);
 
         final DateTime effectiveDate2 = effectiveDate1.plusDays(30);
         final BillingEvent event2 = createMockBillingEvent(null, subscription, effectiveDate2, plan, phase2, null,
@@ -1136,7 +1134,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         invoiceList.add(invoice2);
 
-        //invoiceDao.create(invoice2, invoice2.getTargetDate().getDayOfMonth(), internalCallContext);
+        //createInvoice(invoice2, invoice2.getTargetDate().getDayOfMonth(), internalCallContext);
 
         final DateTime effectiveDate3 = effectiveDate2.plusMonths(1);
         final Invoice invoice3 = generator.generateInvoice(accountId, events, invoiceList, new LocalDate(effectiveDate3), DateTimeZone.UTC, Currency.USD);
@@ -1144,7 +1142,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         assertEquals(invoice3.getNumberOfItems(), 1);
         assertEquals(invoice3.getBalance().compareTo(cheapAmount), 0);
 
-        //invoiceDao.create(invoice3, invoice3.getTargetDate().getDayOfMonth(), internalCallContext);
+        //createInvoice(invoice3, invoice3.getTargetDate().getDayOfMonth(), internalCallContext);
     }
 
     @Test(groups = "slow")
@@ -1189,12 +1187,12 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         assertEquals(invoice.getNumberOfItems(), 2);
         assertEquals(invoice.getBalance().compareTo(cheapAmount), 0);
 
-        invoiceDao.create(invoice, invoice.getTargetDate().getDayOfMonth(), true, internalCallContext);
-        final Invoice savedInvoice = invoiceDao.getById(invoice.getId(), internalCallContext);
+        createInvoice(invoice, true, internalCallContext);
+        final InvoiceModelDao savedInvoice = invoiceDao.getById(invoice.getId(), internalCallContext);
 
         assertNotNull(savedInvoice);
-        assertEquals(savedInvoice.getNumberOfItems(), 2);
-        assertEquals(savedInvoice.getBalance().compareTo(cheapAmount), 0);
+        assertEquals(savedInvoice.getInvoiceItems().size(), 2);
+        assertEquals(InvoiceModelDaoHelper.getBalance(savedInvoice).compareTo(cheapAmount), 0);
     }
 
     @Test(groups = "slow")
@@ -1219,26 +1217,27 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
                                                                  recuringStartDate, recuringEndDate, new BigDecimal("239.00"), new BigDecimal("239.00"), Currency.USD);
 
         invoice.addInvoiceItem(invoiceItem);
-        invoiceDao.create(invoice, invoice.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice, true, internalCallContext);
 
         ((ClockMock) clock).addDays(1);
 
         // SECOND CREATE THE PAYMENT
         final BigDecimal paymentAmount = new BigDecimal("239.00");
         final UUID paymentId = UUID.randomUUID();
-        invoiceDao.notifyOfPayment(new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoiceId, clock.getUTCNow(), paymentAmount, Currency.USD), internalCallContext);
+        final DefaultInvoicePayment defaultInvoicePayment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoiceId, clock.getUTCNow(), paymentAmount, Currency.USD);
+        invoiceDao.notifyOfPayment(new InvoicePaymentModelDao(defaultInvoicePayment), internalCallContext);
 
         // AND THEN THIRD THE REFUND
         final Map<UUID, BigDecimal> invoiceItemMap = new HashMap<UUID, BigDecimal>();
         invoiceItemMap.put(invoiceItem.getId(), new BigDecimal("239.00"));
         invoiceDao.createRefund(paymentId, paymentAmount, true, invoiceItemMap, UUID.randomUUID(), internalCallContext);
 
-        final Invoice savedInvoice = invoiceDao.getById(invoiceId, internalCallContext);
+        final InvoiceModelDao savedInvoice = invoiceDao.getById(invoiceId, internalCallContext);
         assertNotNull(savedInvoice);
         assertEquals(savedInvoice.getInvoiceItems().size(), 2);
 
         final List<Invoice> invoices = new ArrayList<Invoice>();
-        invoices.add(savedInvoice);
+        invoices.add(new DefaultInvoice(savedInvoice));
 
         // NOW COMPUTE A DIFFERENT ITEM TO TRIGGER REPAIR
         final BillingEventSet events = new MockBillingEventSet();
@@ -1259,10 +1258,10 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
                                                            "new-event", 1L, SubscriptionTransitionType.CREATE);
         events.add(event1);
         final Invoice newInvoice = generator.generateInvoice(UUID.randomUUID(), events, invoices, targetDate, DateTimeZone.UTC, Currency.USD);
-        invoiceDao.create(newInvoice, newInvoice.getTargetDate().getDayOfMonth(), true, internalCallContext);
+        createInvoice(newInvoice, true, internalCallContext);
 
         // VERIFY THAT WE STILL HAVE ONLY 2 ITEMS, MENAING THERE WERE NO REPAIR AND NO CBA GENERATED
-        final Invoice firstInvoice = invoiceDao.getById(invoiceId, internalCallContext);
+        final Invoice firstInvoice = new DefaultInvoice(invoiceDao.getById(invoiceId, internalCallContext));
         assertNotNull(firstInvoice);
         assertEquals(firstInvoice.getInvoiceItems().size(), 2);
     }
@@ -1295,8 +1294,8 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         Invoice invoice1 = generator.generateInvoice(UUID.randomUUID(), events, invoices, new LocalDate(targetDate1), DateTimeZone.UTC, Currency.USD);
         invoices.add(invoice1);
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
-        invoice1 = invoiceDao.getById(invoice1.getId(), internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
+        invoice1 = new DefaultInvoice(invoiceDao.getById(invoice1.getId(), internalCallContext));
         assertNotNull(invoice1.getInvoiceNumber());
 
         final BillingEvent event2 = createMockBillingEvent(null, subscription, targetDate1, plan, phase2, null,
@@ -1305,8 +1304,8 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
                                                            "testEvent2", 2L, SubscriptionTransitionType.CHANGE);
         events.add(event2);
         Invoice invoice2 = generator.generateInvoice(UUID.randomUUID(), events, invoices, new LocalDate(targetDate2), DateTimeZone.UTC, Currency.USD);
-        invoiceDao.create(invoice2, invoice2.getTargetDate().getDayOfMonth(), true, internalCallContext);
-        invoice2 = invoiceDao.getById(invoice2.getId(), internalCallContext);
+        createInvoice(invoice2, true, internalCallContext);
+        invoice2 = new DefaultInvoice(invoiceDao.getById(invoice2.getId(), internalCallContext));
         assertNotNull(invoice2.getInvoiceNumber());
     }
 
@@ -1330,10 +1329,10 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final CreditBalanceAdjInvoiceItem creditBalanceAdjInvoiceItem1 = new CreditBalanceAdjInvoiceItem(fixedItem1.getInvoiceId(), fixedItem1.getAccountId(),
                                                                                                          fixedItem1.getStartDate(), fixedItem1.getAmount(),
                                                                                                          fixedItem1.getCurrency());
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
-        invoiceItemSqlDao.create(fixedItem1, internalCallContext);
-        invoiceItemSqlDao.create(repairAdjInvoiceItem, internalCallContext);
-        invoiceItemSqlDao.create(creditBalanceAdjInvoiceItem1, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
+        createInvoiceItem(fixedItem1, internalCallContext);
+        createInvoiceItem(repairAdjInvoiceItem, internalCallContext);
+        createInvoiceItem(creditBalanceAdjInvoiceItem1, internalCallContext);
 
         // Verify scenario - no CBA should have been used
         Assert.assertEquals(invoiceDao.getAccountCBA(accountId, internalCallContext).doubleValue(), 10.00);
@@ -1367,10 +1366,10 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final CreditBalanceAdjInvoiceItem creditBalanceAdjInvoiceItem1 = new CreditBalanceAdjInvoiceItem(fixedItem1.getInvoiceId(), fixedItem1.getAccountId(),
                                                                                                          fixedItem1.getStartDate(), fixedItem1.getAmount(),
                                                                                                          fixedItem1.getCurrency());
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
-        invoiceItemSqlDao.create(fixedItem1, internalCallContext);
-        invoiceItemSqlDao.create(repairAdjInvoiceItem, internalCallContext);
-        invoiceItemSqlDao.create(creditBalanceAdjInvoiceItem1, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
+        createInvoiceItem(fixedItem1, internalCallContext);
+        createInvoiceItem(repairAdjInvoiceItem, internalCallContext);
+        createInvoiceItem(creditBalanceAdjInvoiceItem1, internalCallContext);
 
         // Create invoice 2
         // Scenario: single item
@@ -1382,9 +1381,9 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final CreditBalanceAdjInvoiceItem creditBalanceAdjInvoiceItem2 = new CreditBalanceAdjInvoiceItem(fixedItem2.getInvoiceId(), fixedItem2.getAccountId(),
                                                                                                          fixedItem2.getStartDate(), fixedItem2.getAmount().negate(),
                                                                                                          fixedItem2.getCurrency());
-        invoiceDao.create(invoice2, invoice2.getTargetDate().getDayOfMonth(), true, internalCallContext);
-        invoiceItemSqlDao.create(fixedItem2, internalCallContext);
-        invoiceItemSqlDao.create(creditBalanceAdjInvoiceItem2, internalCallContext);
+        createInvoice(invoice2, true, internalCallContext);
+        createInvoiceItem(fixedItem2, internalCallContext);
+        createInvoiceItem(creditBalanceAdjInvoiceItem2, internalCallContext);
 
         // Verify scenario - half of the CBA should have been used
         Assert.assertEquals(invoiceDao.getAccountCBA(accountId, internalCallContext).doubleValue(), 5.00);
@@ -1420,10 +1419,10 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final CreditBalanceAdjInvoiceItem creditBalanceAdjInvoiceItem1 = new CreditBalanceAdjInvoiceItem(fixedItem1.getInvoiceId(), fixedItem1.getAccountId(),
                                                                                                          fixedItem1.getStartDate(), fixedItem1.getAmount(),
                                                                                                          fixedItem1.getCurrency());
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
-        invoiceItemSqlDao.create(fixedItem1, internalCallContext);
-        invoiceItemSqlDao.create(repairAdjInvoiceItem, internalCallContext);
-        invoiceItemSqlDao.create(creditBalanceAdjInvoiceItem1, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
+        createInvoiceItem(fixedItem1, internalCallContext);
+        createInvoiceItem(repairAdjInvoiceItem, internalCallContext);
+        createInvoiceItem(creditBalanceAdjInvoiceItem1, internalCallContext);
 
         // Create invoice 2
         // Scenario: single item
@@ -1435,9 +1434,9 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final CreditBalanceAdjInvoiceItem creditBalanceAdjInvoiceItem2 = new CreditBalanceAdjInvoiceItem(fixedItem2.getInvoiceId(), fixedItem2.getAccountId(),
                                                                                                          fixedItem2.getStartDate(), fixedItem2.getAmount().negate(),
                                                                                                          fixedItem2.getCurrency());
-        invoiceDao.create(invoice2, invoice2.getTargetDate().getDayOfMonth(), true, internalCallContext);
-        invoiceItemSqlDao.create(fixedItem2, internalCallContext);
-        invoiceItemSqlDao.create(creditBalanceAdjInvoiceItem2, internalCallContext);
+        createInvoice(invoice2, true, internalCallContext);
+        createInvoiceItem(fixedItem2, internalCallContext);
+        createInvoiceItem(creditBalanceAdjInvoiceItem2, internalCallContext);
 
         // Create invoice 3
         // Scenario: single item
@@ -1449,9 +1448,9 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final CreditBalanceAdjInvoiceItem creditBalanceAdjInvoiceItem3 = new CreditBalanceAdjInvoiceItem(fixedItem3.getInvoiceId(), fixedItem3.getAccountId(),
                                                                                                          fixedItem3.getStartDate(), fixedItem3.getAmount().negate(),
                                                                                                          fixedItem3.getCurrency());
-        invoiceDao.create(invoice3, invoice3.getTargetDate().getDayOfMonth(), true, internalCallContext);
-        invoiceItemSqlDao.create(fixedItem3, internalCallContext);
-        invoiceItemSqlDao.create(creditBalanceAdjInvoiceItem3, internalCallContext);
+        createInvoice(invoice3, true, internalCallContext);
+        createInvoiceItem(fixedItem3, internalCallContext);
+        createInvoiceItem(creditBalanceAdjInvoiceItem3, internalCallContext);
 
         // Verify scenario - all CBA should have been used
         Assert.assertEquals(invoiceDao.getAccountCBA(accountId, internalCallContext).doubleValue(), 0.00);
@@ -1485,9 +1484,9 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final CreditBalanceAdjInvoiceItem creditBalanceAdjInvoiceItem1 = new CreditBalanceAdjInvoiceItem(invoice1.getId(), invoice1.getAccountId(),
                                                                                                          invoice1.getInvoiceDate(), repairAdjInvoiceItem.getAmount().negate(),
                                                                                                          invoice1.getCurrency());
-        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, internalCallContext);
-        invoiceItemSqlDao.create(repairAdjInvoiceItem, internalCallContext);
-        invoiceItemSqlDao.create(creditBalanceAdjInvoiceItem1, internalCallContext);
+        createInvoice(invoice1, true, internalCallContext);
+        createInvoiceItem(repairAdjInvoiceItem, internalCallContext);
+        createInvoiceItem(creditBalanceAdjInvoiceItem1, internalCallContext);
 
         // Verify scenario
         Assert.assertEquals(invoiceDao.getAccountCBA(accountId, internalCallContext).doubleValue(), 10.00);
@@ -1506,10 +1505,4 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         Assert.assertEquals(invoiceDao.getAccountCBA(accountId, internalCallContext).doubleValue(), 10.00);
         verifyInvoice(invoice1.getId(), 0.00, 10.00);
     }
-
-    private void verifyInvoice(final UUID invoiceId, final double balance, final double cbaAmount) throws InvoiceApiException {
-        final Invoice invoice = invoiceDao.getById(invoiceId, internalCallContext);
-        Assert.assertEquals(invoice.getBalance().doubleValue(), balance);
-        Assert.assertEquals(invoice.getCBAAmount().doubleValue(), cbaAmount);
-    }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDaoForItemAdjustment.java b/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDaoForItemAdjustment.java
index 8182355..9cc7557 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDaoForItemAdjustment.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDaoForItemAdjustment.java
@@ -21,7 +21,6 @@ import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.LocalDate;
-import org.mockito.Mockito;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -33,7 +32,6 @@ import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
-import com.ning.billing.util.callcontext.InternalCallContext;
 
 public class TestInvoiceDaoForItemAdjustment extends InvoiceDaoTestBase {
 
@@ -45,7 +43,6 @@ public class TestInvoiceDaoForItemAdjustment extends InvoiceDaoTestBase {
         final UUID invoiceId = UUID.randomUUID();
         final UUID invoiceItemId = UUID.randomUUID();
         final LocalDate effectiveDate = new LocalDate();
-        final InternalCallContext context = Mockito.mock(InternalCallContext.class);
 
         try {
             invoiceDao.insertInvoiceItemAdjustment(accountId, invoiceId, invoiceItemId, effectiveDate, null, null, internalCallContext);
@@ -63,7 +60,7 @@ public class TestInvoiceDaoForItemAdjustment extends InvoiceDaoTestBase {
                                                                  new LocalDate(2010, 1, 1), new LocalDate(2010, 4, 1),
                                                                  INVOICE_ITEM_AMOUNT, new BigDecimal("7.00"), Currency.USD);
         invoice.addInvoiceItem(invoiceItem);
-        invoiceDao.create(invoice, 1, true, internalCallContext);
+        createInvoice(invoice, true, internalCallContext);
 
         try {
             invoiceDao.insertInvoiceItemAdjustment(invoice.getAccountId(), UUID.randomUUID(), invoiceItem.getId(), new LocalDate(2010, 1, 1), null, null, internalCallContext);
@@ -81,9 +78,9 @@ public class TestInvoiceDaoForItemAdjustment extends InvoiceDaoTestBase {
                                                                  new LocalDate(2010, 1, 1), new LocalDate(2010, 4, 1),
                                                                  INVOICE_ITEM_AMOUNT, new BigDecimal("7.00"), Currency.USD);
         invoice.addInvoiceItem(invoiceItem);
-        invoiceDao.create(invoice, 1, true, internalCallContext);
+        createInvoice(invoice, true, internalCallContext);
 
-        final InvoiceItem adjustedInvoiceItem = createAndCheckAdjustment(invoice, invoiceItem, null);
+        final InvoiceItemModelDao adjustedInvoiceItem = createAndCheckAdjustment(invoice, invoiceItem, null);
         Assert.assertEquals(adjustedInvoiceItem.getAmount().compareTo(invoiceItem.getAmount().negate()), 0);
     }
 
@@ -95,23 +92,22 @@ public class TestInvoiceDaoForItemAdjustment extends InvoiceDaoTestBase {
                                                                  new LocalDate(2010, 1, 1), new LocalDate(2010, 4, 1),
                                                                  INVOICE_ITEM_AMOUNT, new BigDecimal("7.00"), Currency.USD);
         invoice.addInvoiceItem(invoiceItem);
-        invoiceDao.create(invoice, 1, true, internalCallContext);
+        createInvoice(invoice, true, internalCallContext);
 
-        final InvoiceItem adjustedInvoiceItem = createAndCheckAdjustment(invoice, invoiceItem, BigDecimal.TEN);
+        final InvoiceItemModelDao adjustedInvoiceItem = createAndCheckAdjustment(invoice, invoiceItem, BigDecimal.TEN);
         Assert.assertEquals(adjustedInvoiceItem.getAmount().compareTo(BigDecimal.TEN.negate()), 0);
     }
 
-    private InvoiceItem createAndCheckAdjustment(final Invoice invoice, final InvoiceItem invoiceItem, final BigDecimal amount) throws InvoiceApiException {
+    private InvoiceItemModelDao createAndCheckAdjustment(final Invoice invoice, final InvoiceItem invoiceItem, final BigDecimal amount) throws InvoiceApiException {
         final LocalDate effectiveDate = new LocalDate(2010, 1, 1);
-        final InvoiceItem adjustedInvoiceItem = invoiceDao.insertInvoiceItemAdjustment(invoice.getAccountId(), invoice.getId(), invoiceItem.getId(),
-                                                                                       effectiveDate, amount, null, internalCallContext);
+        final InvoiceItemModelDao adjustedInvoiceItem = invoiceDao.insertInvoiceItemAdjustment(invoice.getAccountId(), invoice.getId(), invoiceItem.getId(),
+                                                                                               effectiveDate, amount, null, internalCallContext);
         Assert.assertEquals(adjustedInvoiceItem.getAccountId(), invoiceItem.getAccountId());
         Assert.assertNull(adjustedInvoiceItem.getBundleId());
         Assert.assertEquals(adjustedInvoiceItem.getCurrency(), invoiceItem.getCurrency());
-        Assert.assertEquals(adjustedInvoiceItem.getDescription(), "Invoice item adjustment");
         Assert.assertEquals(adjustedInvoiceItem.getEndDate(), effectiveDate);
         Assert.assertEquals(adjustedInvoiceItem.getInvoiceId(), invoiceItem.getInvoiceId());
-        Assert.assertEquals(adjustedInvoiceItem.getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        Assert.assertEquals(adjustedInvoiceItem.getType(), InvoiceItemType.ITEM_ADJ);
         Assert.assertEquals(adjustedInvoiceItem.getLinkedItemId(), invoiceItem.getId());
         Assert.assertNull(adjustedInvoiceItem.getPhaseName());
         Assert.assertNull(adjustedInvoiceItem.getPlanName());
@@ -120,26 +116,50 @@ public class TestInvoiceDaoForItemAdjustment extends InvoiceDaoTestBase {
         Assert.assertNull(adjustedInvoiceItem.getSubscriptionId());
 
         // Retrieve the item by id
-        final InvoiceItem retrievedInvoiceItem = invoiceItemSqlDao.getById(adjustedInvoiceItem.getId().toString(), internalCallContext);
-        Assert.assertEquals(retrievedInvoiceItem, adjustedInvoiceItem);
+        final InvoiceItemModelDao retrievedInvoiceItem = invoiceItemSqlDao.getById(adjustedInvoiceItem.getId().toString(), internalCallContext);
+        // TODO We can't use equals() due to the createdDate field
+        Assert.assertEquals(retrievedInvoiceItem.getAccountId(), adjustedInvoiceItem.getAccountId());
+        Assert.assertNull(retrievedInvoiceItem.getBundleId());
+        Assert.assertEquals(retrievedInvoiceItem.getCurrency(), adjustedInvoiceItem.getCurrency());
+        Assert.assertEquals(retrievedInvoiceItem.getEndDate(), adjustedInvoiceItem.getEndDate());
+        Assert.assertEquals(retrievedInvoiceItem.getInvoiceId(), adjustedInvoiceItem.getInvoiceId());
+        Assert.assertEquals(retrievedInvoiceItem.getType(), adjustedInvoiceItem.getType());
+        Assert.assertEquals(retrievedInvoiceItem.getLinkedItemId(), adjustedInvoiceItem.getLinkedItemId());
+        Assert.assertNull(retrievedInvoiceItem.getPhaseName());
+        Assert.assertNull(retrievedInvoiceItem.getPlanName());
+        Assert.assertNull(retrievedInvoiceItem.getRate());
+        Assert.assertEquals(retrievedInvoiceItem.getStartDate(), adjustedInvoiceItem.getStartDate());
+        Assert.assertNull(retrievedInvoiceItem.getSubscriptionId());
 
         // Retrieve the item by invoice id
-        final Invoice retrievedInvoice = invoiceDao.getById(adjustedInvoiceItem.getInvoiceId(), internalCallContext);
-        final List<InvoiceItem> invoiceItems = retrievedInvoice.getInvoiceItems();
+        final InvoiceModelDao retrievedInvoice = invoiceDao.getById(adjustedInvoiceItem.getInvoiceId(), internalCallContext);
+        final List<InvoiceItemModelDao> invoiceItems = retrievedInvoice.getInvoiceItems();
         Assert.assertEquals(invoiceItems.size(), 2);
-        final InvoiceItem retrievedByInvoiceInvoiceItem;
+        final InvoiceItemModelDao retrievedByInvoiceInvoiceItem;
         if (invoiceItems.get(0).getId().equals(adjustedInvoiceItem.getId())) {
             retrievedByInvoiceInvoiceItem = invoiceItems.get(0);
         } else {
             retrievedByInvoiceInvoiceItem = invoiceItems.get(1);
         }
-        Assert.assertEquals(retrievedByInvoiceInvoiceItem, adjustedInvoiceItem);
+        // TODO We can't use equals() due to the createdDate field
+        Assert.assertEquals(retrievedByInvoiceInvoiceItem.getAccountId(), adjustedInvoiceItem.getAccountId());
+        Assert.assertEquals(retrievedByInvoiceInvoiceItem.getBundleId(), adjustedInvoiceItem.getBundleId());
+        Assert.assertEquals(retrievedByInvoiceInvoiceItem.getCurrency(), adjustedInvoiceItem.getCurrency());
+        Assert.assertEquals(retrievedByInvoiceInvoiceItem.getEndDate(), adjustedInvoiceItem.getEndDate());
+        Assert.assertEquals(retrievedByInvoiceInvoiceItem.getInvoiceId(), adjustedInvoiceItem.getInvoiceId());
+        Assert.assertEquals(retrievedByInvoiceInvoiceItem.getType(), adjustedInvoiceItem.getType());
+        Assert.assertEquals(retrievedByInvoiceInvoiceItem.getLinkedItemId(), adjustedInvoiceItem.getLinkedItemId());
+        Assert.assertEquals(retrievedByInvoiceInvoiceItem.getPhaseName(), adjustedInvoiceItem.getPhaseName());
+        Assert.assertEquals(retrievedByInvoiceInvoiceItem.getPlanName(), adjustedInvoiceItem.getPlanName());
+        Assert.assertEquals(retrievedByInvoiceInvoiceItem.getRate(), adjustedInvoiceItem.getRate());
+        Assert.assertEquals(retrievedInvoiceItem.getStartDate(), adjustedInvoiceItem.getStartDate());
+        Assert.assertEquals(retrievedInvoiceItem.getSubscriptionId(), adjustedInvoiceItem.getSubscriptionId());
 
         // Verify the invoice balance
         if (amount == null) {
-            Assert.assertEquals(retrievedInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
+            Assert.assertEquals(InvoiceModelDaoHelper.getBalance(retrievedInvoice).compareTo(BigDecimal.ZERO), 0);
         } else {
-            Assert.assertEquals(retrievedInvoice.getBalance().compareTo(INVOICE_ITEM_AMOUNT.add(amount.negate())), 0);
+            Assert.assertEquals(InvoiceModelDaoHelper.getBalance(retrievedInvoice).compareTo(INVOICE_ITEM_AMOUNT.add(amount.negate())), 0);
         }
 
         return adjustedInvoiceItem;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceItemDao.java b/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceItemDao.java
index 59ae006..513a95d 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceItemDao.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceItemDao.java
@@ -29,6 +29,7 @@ import com.ning.billing.invoice.model.CreditBalanceAdjInvoiceItem;
 import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.invoice.model.ExternalChargeInvoiceItem;
 import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
+import com.ning.billing.invoice.model.InvoiceItemFactory;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
 import com.ning.billing.util.entity.EntityPersistenceException;
 
@@ -50,9 +51,9 @@ public class TestInvoiceItemDao extends InvoiceDaoTestBase {
 
         final RecurringInvoiceItem item = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, "test plan", "test phase", startDate, endDate,
                                                                    rate, rate, Currency.USD);
-        invoiceItemSqlDao.create(item, internalCallContext);
+        createInvoiceItem(item, internalCallContext);
 
-        final RecurringInvoiceItem thisItem = (RecurringInvoiceItem) invoiceItemSqlDao.getById(item.getId().toString(), internalCallContext);
+        final InvoiceItemModelDao thisItem = invoiceItemSqlDao.getById(item.getId().toString(), internalCallContext);
         assertNotNull(thisItem);
         assertEquals(thisItem.getId(), item.getId());
         assertEquals(thisItem.getInvoiceId(), item.getInvoiceId());
@@ -80,10 +81,10 @@ public class TestInvoiceItemDao extends InvoiceDaoTestBase {
             final RecurringInvoiceItem item = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId,
                                                                        "test plan", "test phase", startDate.plusMonths(i), startDate.plusMonths(i + 1),
                                                                        rate, rate, Currency.USD);
-            invoiceItemSqlDao.create(item, internalCallContext);
+            createInvoiceItem(item, internalCallContext);
         }
 
-        final List<InvoiceItem> items = invoiceItemSqlDao.getInvoiceItemsBySubscription(subscriptionId.toString(), internalCallContext);
+        final List<InvoiceItemModelDao> items = invoiceItemSqlDao.getInvoiceItemsBySubscription(subscriptionId.toString(), internalCallContext);
         assertEquals(items.size(), 3);
     }
 
@@ -102,10 +103,10 @@ public class TestInvoiceItemDao extends InvoiceDaoTestBase {
             final RecurringInvoiceItem item = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId,
                                                                        "test plan", "test phase", startDate, startDate.plusMonths(1),
                                                                        amount, amount, Currency.USD);
-            invoiceItemSqlDao.create(item, internalCallContext);
+            createInvoiceItem(item, internalCallContext);
         }
 
-        final List<InvoiceItem> items = invoiceItemSqlDao.getInvoiceItemsByInvoice(invoiceId.toString(), internalCallContext);
+        final List<InvoiceItemModelDao> items = invoiceItemSqlDao.getInvoiceItemsByInvoice(invoiceId.toString(), internalCallContext);
         assertEquals(items.size(), 5);
     }
 
@@ -116,7 +117,7 @@ public class TestInvoiceItemDao extends InvoiceDaoTestBase {
         final LocalDate targetDate = new LocalDate(2011, 5, 23);
         final DefaultInvoice invoice = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate, Currency.USD);
 
-        invoiceDao.create(invoice, targetDate.getDayOfMonth(), true, internalCallContext);
+        createInvoice(invoice, true, internalCallContext);
 
         final UUID invoiceId = invoice.getId();
         final LocalDate startDate = new LocalDate(2011, 3, 1);
@@ -127,9 +128,9 @@ public class TestInvoiceItemDao extends InvoiceDaoTestBase {
         final RecurringInvoiceItem item = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId,
                                                                    "test plan", "test phase", startDate, startDate.plusMonths(1),
                                                                    rate, rate, Currency.USD);
-        invoiceItemSqlDao.create(item, internalCallContext);
+        createInvoiceItem(item, internalCallContext);
 
-        final List<InvoiceItem> items = invoiceItemSqlDao.getInvoiceItemsByAccount(accountId.toString(), internalCallContext);
+        final List<InvoiceItemModelDao> items = invoiceItemSqlDao.getInvoiceItemsByAccount(accountId.toString(), internalCallContext);
         assertEquals(items.size(), 1);
     }
 
@@ -140,10 +141,10 @@ public class TestInvoiceItemDao extends InvoiceDaoTestBase {
         final LocalDate creditDate = new LocalDate(2012, 4, 1);
 
         final InvoiceItem creditInvoiceItem = new CreditBalanceAdjInvoiceItem(invoiceId, accountId, creditDate, TEN, Currency.USD);
-        invoiceItemSqlDao.create(creditInvoiceItem, internalCallContext);
+        createInvoiceItem(creditInvoiceItem, internalCallContext);
 
-        final InvoiceItem savedItem = invoiceItemSqlDao.getById(creditInvoiceItem.getId().toString(), internalCallContext);
-        assertEquals(savedItem, creditInvoiceItem);
+        final InvoiceItemModelDao savedItem = invoiceItemSqlDao.getById(creditInvoiceItem.getId().toString(), internalCallContext);
+        assertEquals(InvoiceItemFactory.fromModelDao(savedItem), creditInvoiceItem);
     }
 
     @Test(groups = "slow")
@@ -154,10 +155,10 @@ public class TestInvoiceItemDao extends InvoiceDaoTestBase {
 
         final InvoiceItem fixedPriceInvoiceItem = new FixedPriceInvoiceItem(invoiceId, accountId, UUID.randomUUID(),
                                                                             UUID.randomUUID(), "test plan", "test phase", startDate, TEN, Currency.USD);
-        invoiceItemSqlDao.create(fixedPriceInvoiceItem, internalCallContext);
+        createInvoiceItem(fixedPriceInvoiceItem, internalCallContext);
 
-        final InvoiceItem savedItem = invoiceItemSqlDao.getById(fixedPriceInvoiceItem.getId().toString(), internalCallContext);
-        assertEquals(savedItem, fixedPriceInvoiceItem);
+        final InvoiceItemModelDao savedItem = invoiceItemSqlDao.getById(fixedPriceInvoiceItem.getId().toString(), internalCallContext);
+        assertEquals(InvoiceItemFactory.fromModelDao(savedItem), fixedPriceInvoiceItem);
     }
 
     @Test(groups = "slow")
@@ -169,9 +170,9 @@ public class TestInvoiceItemDao extends InvoiceDaoTestBase {
         final LocalDate startDate = new LocalDate(2012, 4, 1);
         final InvoiceItem externalChargeInvoiceItem = new ExternalChargeInvoiceItem(invoiceId, accountId, bundleId, description,
                                                                                     startDate, TEN, Currency.USD);
-        invoiceItemSqlDao.create(externalChargeInvoiceItem, internalCallContext);
+        createInvoiceItem(externalChargeInvoiceItem, internalCallContext);
 
-        final InvoiceItem savedItem = invoiceItemSqlDao.getById(externalChargeInvoiceItem.getId().toString(), internalCallContext);
-        assertEquals(savedItem, externalChargeInvoiceItem);
+        final InvoiceItemModelDao savedItem = invoiceItemSqlDao.getById(externalChargeInvoiceItem.getId().toString(), internalCallContext);
+        assertEquals(InvoiceItemFactory.fromModelDao(savedItem), externalChargeInvoiceItem);
     }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGenerator.java b/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGenerator.java
index 86488d9..5864d95 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGenerator.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGenerator.java
@@ -44,7 +44,6 @@ 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.util.config.InvoiceConfig;
 import com.ning.billing.entitlement.api.SubscriptionTransitionType;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.invoice.MockBillingEventSet;
@@ -61,6 +60,7 @@ import com.ning.billing.invoice.tests.InvoicingTestBase;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.clock.DefaultClock;
+import com.ning.billing.util.config.InvoiceConfig;
 import com.ning.billing.util.svcapi.junction.BillingEvent;
 import com.ning.billing.util.svcapi.junction.BillingEventSet;
 import com.ning.billing.util.svcapi.junction.BillingModeType;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGeneratorUnit.java b/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGeneratorUnit.java
index c78c286..1fd99d3 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGeneratorUnit.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGeneratorUnit.java
@@ -26,7 +26,6 @@ import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.util.config.InvoiceConfig;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoiceItemType;
@@ -37,12 +36,14 @@ import com.ning.billing.invoice.model.RepairAdjInvoiceItem;
 import com.ning.billing.invoice.tests.InvoicingTestBase;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.config.InvoiceConfig;
 
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertEquals;
 
 public class TestDefaultInvoiceGeneratorUnit extends InvoicingTestBase {
+
     private DefaultInvoiceGenerator gen;
     private Clock clock;
 
@@ -55,6 +56,7 @@ public class TestDefaultInvoiceGeneratorUnit extends InvoicingTestBase {
     private final Currency currency = Currency.USD;
 
     public static final class TestDefaultInvoiceGeneratorMock extends DefaultInvoiceGenerator {
+
         public TestDefaultInvoiceGeneratorMock(final Clock clock, final InvoiceConfig config) {
             super(clock, config);
         }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java
index 0942b5a..b2590f9 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java
@@ -16,8 +16,6 @@
 
 package com.ning.billing.invoice.glue;
 
-import static org.testng.Assert.assertNotNull;
-
 import java.io.IOException;
 import java.net.URL;
 
@@ -48,7 +46,10 @@ import com.ning.billing.util.notificationq.NotificationQueueService;
 import com.ning.billing.util.svcapi.account.AccountInternalApi;
 import com.ning.billing.util.svcapi.junction.BillingInternalApi;
 
+import static org.testng.Assert.assertNotNull;
+
 public class InvoiceModuleWithEmbeddedDb extends DefaultInvoiceModule {
+
     private final MysqlTestingHelper helper = KillbillTestSuiteWithEmbeddedDB.getMysqlTestingHelper();
 
     private void installNotificationQueue() {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java
index 3e2a772..f13d10c 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java
@@ -29,6 +29,7 @@ import com.ning.billing.util.globallocker.MockGlobalLocker;
 import com.ning.billing.util.template.translation.TranslatorConfig;
 
 public class InvoiceModuleWithMocks extends DefaultInvoiceModule {
+
     @Override
     protected void installInvoiceDao() {
         bind(MockInvoiceDao.class).asEagerSingleton();
diff --git a/invoice/src/test/java/com/ning/billing/invoice/MockModule.java b/invoice/src/test/java/com/ning/billing/invoice/MockModule.java
index b8b1a94..e12e3d5 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/MockModule.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/MockModule.java
@@ -16,8 +16,6 @@
 
 package com.ning.billing.invoice;
 
-import static org.testng.Assert.assertNotNull;
-
 import java.io.IOException;
 import java.net.URL;
 
@@ -52,7 +50,10 @@ import com.ning.billing.util.svcapi.entitlement.EntitlementInternalApi;
 
 import com.google.inject.AbstractModule;
 
+import static org.testng.Assert.assertNotNull;
+
 public class MockModule extends AbstractModule {
+
     @Override
     protected void configure() {
         loadSystemPropertiesFromClasspath("/resource.properties");
diff --git a/invoice/src/test/java/com/ning/billing/invoice/model/TestInAdvanceBillingMode.java b/invoice/src/test/java/com/ning/billing/invoice/model/TestInAdvanceBillingMode.java
index fccd7b1..c5bc813 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/model/TestInAdvanceBillingMode.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/model/TestInAdvanceBillingMode.java
@@ -31,7 +31,7 @@ import com.ning.billing.catalog.api.BillingPeriod;
 public class TestInAdvanceBillingMode {
 
     private static final DateTimeZone TIMEZONE = DateTimeZone.forID("Pacific/Pitcairn");
-    public static final BillingPeriod BILLING_PERIOD = BillingPeriod.MONTHLY;
+    private static final BillingPeriod BILLING_PERIOD = BillingPeriod.MONTHLY;
 
     @Test(groups = "fast")
     public void testItemShouldNotStartInThePast() throws Exception {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDateNotifier.java b/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDateNotifier.java
index 727d9d7..52155a8 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDateNotifier.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDateNotifier.java
@@ -17,6 +17,7 @@
 package com.ning.billing.invoice.notification;
 
 public class MockNextBillingDateNotifier implements NextBillingDateNotifier {
+
     @Override
     public void initialize() {
         // do nothing
diff --git a/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDatePoster.java b/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDatePoster.java
index 5742247..6b77d80 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDatePoster.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDatePoster.java
@@ -19,11 +19,13 @@ package com.ning.billing.invoice.notification;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 
 public class MockNextBillingDatePoster implements NextBillingDatePoster {
+
     @Override
-    public void insertNextBillingNotification(final Transmogrifier transactionalDao, final UUID accountId, final UUID subscriptionId, final DateTime futureNotificationTime) {
-        // do nothing
+    public void insertNextBillingNotification(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID accountId, final UUID subscriptionId, final DateTime futureNotificationTime) {
     }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java b/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
index fe1b45d..3b9a47e 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
@@ -16,9 +16,6 @@
 
 package com.ning.billing.invoice.notification;
 
-import static com.jayway.awaitility.Awaitility.await;
-import static java.util.concurrent.TimeUnit.MINUTES;
-
 import java.io.IOException;
 import java.sql.SQLException;
 import java.util.UUID;
@@ -28,8 +25,6 @@ import org.joda.time.DateTime;
 import org.mockito.Mockito;
 import org.skife.config.ConfigurationObjectFactory;
 import org.skife.jdbi.v2.IDBI;
-import org.skife.jdbi.v2.Transaction;
-import org.skife.jdbi.v2.TransactionStatus;
 import org.testng.Assert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
@@ -37,7 +32,6 @@ import org.testng.annotations.Test;
 
 import com.ning.billing.KillbillTestSuiteWithEmbeddedDB;
 import com.ning.billing.catalog.MockCatalogModule;
-import com.ning.billing.util.config.InvoiceConfig;
 import com.ning.billing.dbi.DBIProvider;
 import com.ning.billing.dbi.DbiConfig;
 import com.ning.billing.dbi.MysqlTestingHelper;
@@ -55,12 +49,16 @@ import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.config.InvoiceConfig;
 import com.ning.billing.util.email.templates.TemplateModule;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.BusModule.BusType;
 import com.ning.billing.util.glue.NotificationQueueModule;
 import com.ning.billing.util.glue.TagStoreModule;
-import com.ning.billing.util.notificationq.DummySqlTest;
 import com.ning.billing.util.notificationq.NotificationQueueService;
 import com.ning.billing.util.svcapi.account.AccountInternalApi;
 import com.ning.billing.util.svcapi.entitlement.EntitlementInternalApi;
@@ -71,18 +69,21 @@ import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Stage;
 
-
+import static com.jayway.awaitility.Awaitility.await;
+import static java.util.concurrent.TimeUnit.MINUTES;
 
 public class TestNextBillingDateNotifier extends InvoiceTestSuiteWithEmbeddedDB {
+
     private Clock clock;
     private DefaultNextBillingDateNotifier notifier;
-    private DummySqlTest dao;
     private InternalBus eventBus;
     private InvoiceListenerMock listener;
     private NotificationQueueService notificationQueueService;
     private InternalCallContextFactory internalCallContextFactory;
+    private EntitySqlDaoTransactionalJdbiWrapper entitySqlDaoTransactionalJdbiWrapper;
 
     private static final class InvoiceListenerMock extends InvoiceListener {
+
         int eventCount = 0;
         UUID latestSubscriptionId = null;
 
@@ -149,7 +150,9 @@ public class TestNextBillingDateNotifier extends InvoiceTestSuiteWithEmbeddedDB 
 
         clock = g.getInstance(Clock.class);
         final IDBI dbi = g.getInstance(IDBI.class);
-        dao = dbi.onDemand(DummySqlTest.class);
+
+        entitySqlDaoTransactionalJdbiWrapper = new EntitySqlDaoTransactionalJdbiWrapper(dbi);
+
         eventBus = g.getInstance(InternalBus.class);
         notificationQueueService = g.getInstance(NotificationQueueService.class);
         final InvoiceDispatcher dispatcher = g.getInstance(InvoiceDispatcher.class);
@@ -177,12 +180,10 @@ public class TestNextBillingDateNotifier extends InvoiceTestSuiteWithEmbeddedDB 
         notifier.initialize();
         notifier.start();
 
-        dao.inTransaction(new Transaction<Void, DummySqlTest>() {
+        entitySqlDaoTransactionalJdbiWrapper.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final DummySqlTest transactional,
-                                      final TransactionStatus status) throws Exception {
-
-                poster.insertNextBillingNotification(transactional, accountId, subscriptionId, readyTime);
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                poster.insertNextBillingNotification(entitySqlDaoWrapperFactory, accountId, subscriptionId, readyTime);
                 return null;
             }
         });
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestHtmlInvoiceGenerator.java b/invoice/src/test/java/com/ning/billing/invoice/TestHtmlInvoiceGenerator.java
index 5d7cb5b..bcd4ea7 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestHtmlInvoiceGenerator.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestHtmlInvoiceGenerator.java
@@ -42,6 +42,7 @@ import com.ning.billing.util.email.templates.TemplateEngine;
 import com.ning.billing.util.template.translation.TranslatorConfig;
 
 public class TestHtmlInvoiceGenerator extends InvoiceTestSuite {
+
     private HtmlInvoiceGenerator g;
 
     @BeforeSuite(groups = "fast")
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
index 87de546..95341d8 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
@@ -49,6 +49,7 @@ import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.invoice.api.InvoiceNotifier;
 import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.invoice.dao.InvoiceModelDao;
 import com.ning.billing.invoice.generator.InvoiceGenerator;
 import com.ning.billing.invoice.notification.NextBillingDateNotifier;
 import com.ning.billing.invoice.notification.NullInvoiceNotifier;
@@ -107,7 +108,6 @@ public class TestInvoiceDispatcher extends InvoicingTestBase {
     @Inject
     private InternalCallContextFactory internalCallContextFactory;
 
-
     private Account account;
     private Subscription subscription;
 
@@ -123,7 +123,6 @@ public class TestInvoiceDispatcher extends InvoicingTestBase {
 
         final UUID accountId = UUID.randomUUID();
 
-
         Mockito.when(accountInternalApi.getAccountById(Mockito.<UUID>any(), Mockito.<InternalTenantContext>any())).thenReturn(account);
 
         Mockito.when(account.getCurrency()).thenReturn(Currency.USD);
@@ -168,13 +167,13 @@ public class TestInvoiceDispatcher extends InvoicingTestBase {
         final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
         final InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountInternalApi, billingApi, entitlementInternalApi, invoiceDao,
                                                                    invoiceNotifier, locker, busService.getBus(),
-                                                                   clock, new InternalCallContextFactory(getMysqlTestingHelper().getDBI(), clock));
+                                                                   clock);
 
         Invoice invoice = dispatcher.processAccount(accountId, target, true, internalCallContext);
         Assert.assertNotNull(invoice);
 
         final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(callContext);
-        List<Invoice> invoices = invoiceDao.getInvoicesByAccount(accountId, internalTenantContext);
+        List<InvoiceModelDao> invoices = invoiceDao.getInvoicesByAccount(accountId, internalTenantContext);
         Assert.assertEquals(invoices.size(), 0);
 
         // Try it again to double check
@@ -222,7 +221,7 @@ public class TestInvoiceDispatcher extends InvoicingTestBase {
         final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
         final InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountInternalApi, billingApi, entitlementInternalApi, invoiceDao,
                                                                    invoiceNotifier, locker, busService.getBus(),
-                                                                   clock, internalCallContextFactory);
+                                                                   clock);
 
         final Invoice invoice = dispatcher.processAccount(account.getId(), new DateTime("2012-07-30T00:00:00.000Z"), false, internalCallContext);
         Assert.assertNotNull(invoice);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/GenericProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/GenericProRationTests.java
index 4b37ad8..b1c5ab6 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/GenericProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/GenericProRationTests.java
@@ -18,12 +18,11 @@ package com.ning.billing.invoice.tests.inAdvance.annual;
 
 import java.math.BigDecimal;
 
-import org.testng.annotations.Test;
-
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.invoice.tests.inAdvance.GenericProRationTestBase;
 
 public class GenericProRationTests extends GenericProRationTestBase {
+
     @Override
     protected BillingPeriod getBillingPeriod() {
         return BillingPeriod.ANNUAL;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/TestDoubleProRation.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/TestDoubleProRation.java
index 4cb1f53..e2e8e55 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/TestDoubleProRation.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/TestDoubleProRation.java
@@ -26,6 +26,7 @@ import com.ning.billing.invoice.model.InvalidDateSequenceException;
 import com.ning.billing.invoice.tests.inAdvance.ProRationInAdvanceTestBase;
 
 public class TestDoubleProRation extends ProRationInAdvanceTestBase {
+
     @Override
     protected BillingPeriod getBillingPeriod() {
         return BillingPeriod.ANNUAL;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/TestProRation.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/TestProRation.java
index 366f987..16e1407 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/TestProRation.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/TestProRation.java
@@ -26,6 +26,7 @@ import com.ning.billing.invoice.model.InvalidDateSequenceException;
 import com.ning.billing.invoice.tests.inAdvance.ProRationInAdvanceTestBase;
 
 public class TestProRation extends ProRationInAdvanceTestBase {
+
     @Override
     protected BillingPeriod getBillingPeriod() {
         return BillingPeriod.ANNUAL;
@@ -50,7 +51,7 @@ public class TestProRation extends ProRationInAdvanceTestBase {
     }
 
     // TODO Test fails, needs to be investigated
-    @Test(groups = "fast", enabled=false)
+    @Test(groups = "fast", enabled = false)
     public void testSinglePlanDoubleProRation() throws InvalidDateSequenceException {
         final LocalDate startDate = buildDate(2011, 1, 10);
         final LocalDate endDate = buildDate(2012, 3, 4);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/TestTrailingProRation.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/TestTrailingProRation.java
index fb7d4cd..d6f38ac 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/TestTrailingProRation.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/TestTrailingProRation.java
@@ -26,6 +26,7 @@ import com.ning.billing.invoice.model.InvalidDateSequenceException;
 import com.ning.billing.invoice.tests.inAdvance.ProRationInAdvanceTestBase;
 
 public class TestTrailingProRation extends ProRationInAdvanceTestBase {
+
     @Override
     protected BillingPeriod getBillingPeriod() {
         return BillingPeriod.ANNUAL;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/GenericProRationTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/GenericProRationTestBase.java
index 42ed57d..a23a534 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/GenericProRationTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/GenericProRationTestBase.java
@@ -19,12 +19,12 @@ package com.ning.billing.invoice.tests.inAdvance;
 import java.math.BigDecimal;
 
 import org.joda.time.LocalDate;
-import org.joda.time.LocalDate;
 import org.testng.annotations.Test;
 
 import com.ning.billing.invoice.model.InvalidDateSequenceException;
 
 public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBase {
+
     /**
      * used for testing cancellation in less than a single billing period
      *
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/GenericProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/GenericProRationTests.java
index 87aeac6..42f4c62 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/GenericProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/GenericProRationTests.java
@@ -18,12 +18,11 @@ package com.ning.billing.invoice.tests.inAdvance.monthly;
 
 import java.math.BigDecimal;
 
-import org.testng.annotations.Test;
-
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.invoice.tests.inAdvance.GenericProRationTestBase;
 
 public class GenericProRationTests extends GenericProRationTestBase {
+
     @Override
     protected BillingPeriod getBillingPeriod() {
         return BillingPeriod.MONTHLY;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TestDoubleProRation.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TestDoubleProRation.java
index 4586e51..cbf4ce1 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TestDoubleProRation.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TestDoubleProRation.java
@@ -26,6 +26,7 @@ import com.ning.billing.invoice.model.InvalidDateSequenceException;
 import com.ning.billing.invoice.tests.inAdvance.ProRationInAdvanceTestBase;
 
 public class TestDoubleProRation extends ProRationInAdvanceTestBase {
+
     @Override
     protected BillingPeriod getBillingPeriod() {
         return BillingPeriod.MONTHLY;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TestLeadingProRation.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TestLeadingProRation.java
index 2bf67d4..34de837 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TestLeadingProRation.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TestLeadingProRation.java
@@ -26,6 +26,7 @@ import com.ning.billing.invoice.model.InvalidDateSequenceException;
 import com.ning.billing.invoice.tests.inAdvance.ProRationInAdvanceTestBase;
 
 public class TestLeadingProRation extends ProRationInAdvanceTestBase {
+
     @Override
     protected BillingPeriod getBillingPeriod() {
         return BillingPeriod.MONTHLY;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TestProRation.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TestProRation.java
index e5ad773..0206b88 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TestProRation.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TestProRation.java
@@ -26,6 +26,7 @@ import com.ning.billing.invoice.model.InvalidDateSequenceException;
 import com.ning.billing.invoice.tests.inAdvance.ProRationInAdvanceTestBase;
 
 public class TestProRation extends ProRationInAdvanceTestBase {
+
     @Override
     protected BillingPeriod getBillingPeriod() {
         return BillingPeriod.MONTHLY;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TestTrailingProRation.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TestTrailingProRation.java
index d643d36..1eaa8a6 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TestTrailingProRation.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TestTrailingProRation.java
@@ -26,6 +26,7 @@ import com.ning.billing.invoice.model.InvalidDateSequenceException;
 import com.ning.billing.invoice.tests.inAdvance.ProRationInAdvanceTestBase;
 
 public class TestTrailingProRation extends ProRationInAdvanceTestBase {
+
     @Override
     protected BillingPeriod getBillingPeriod() {
         return BillingPeriod.MONTHLY;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/ProRationInAdvanceTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/ProRationInAdvanceTestBase.java
index dca4d61..9652fea 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/ProRationInAdvanceTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/ProRationInAdvanceTestBase.java
@@ -16,13 +16,12 @@
 
 package com.ning.billing.invoice.tests.inAdvance;
 
-import org.testng.annotations.Test;
-
 import com.ning.billing.invoice.model.BillingMode;
 import com.ning.billing.invoice.model.InAdvanceBillingMode;
 import com.ning.billing.invoice.tests.ProRationTestBase;
 
 public abstract class ProRationInAdvanceTestBase extends ProRationTestBase {
+
     @Override
     protected BillingMode getBillingMode() {
         return new InAdvanceBillingMode();
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/GenericProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/GenericProRationTests.java
index 72e83d1..e2342cd 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/GenericProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/GenericProRationTests.java
@@ -18,12 +18,11 @@ package com.ning.billing.invoice.tests.inAdvance.quarterly;
 
 import java.math.BigDecimal;
 
-import org.testng.annotations.Test;
-
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.invoice.tests.inAdvance.GenericProRationTestBase;
 
 public class GenericProRationTests extends GenericProRationTestBase {
+
     @Override
     protected BillingPeriod getBillingPeriod() {
         return BillingPeriod.QUARTERLY;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TestDoubleProRation.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TestDoubleProRation.java
index 05e145d..df71741 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TestDoubleProRation.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TestDoubleProRation.java
@@ -26,6 +26,7 @@ import com.ning.billing.invoice.model.InvalidDateSequenceException;
 import com.ning.billing.invoice.tests.inAdvance.ProRationInAdvanceTestBase;
 
 public class TestDoubleProRation extends ProRationInAdvanceTestBase {
+
     @Override
     protected BillingPeriod getBillingPeriod() {
         return BillingPeriod.QUARTERLY;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TestLeadingProRation.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TestLeadingProRation.java
index b831003..cd13b01 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TestLeadingProRation.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TestLeadingProRation.java
@@ -26,6 +26,7 @@ import com.ning.billing.invoice.model.InvalidDateSequenceException;
 import com.ning.billing.invoice.tests.inAdvance.ProRationInAdvanceTestBase;
 
 public class TestLeadingProRation extends ProRationInAdvanceTestBase {
+
     @Override
     protected BillingPeriod getBillingPeriod() {
         return BillingPeriod.QUARTERLY;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TestProRation.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TestProRation.java
index 36a98e2..a46c7a8 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TestProRation.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TestProRation.java
@@ -26,6 +26,7 @@ import com.ning.billing.invoice.model.InvalidDateSequenceException;
 import com.ning.billing.invoice.tests.inAdvance.ProRationInAdvanceTestBase;
 
 public class TestProRation extends ProRationInAdvanceTestBase {
+
     @Override
     protected BillingPeriod getBillingPeriod() {
         return BillingPeriod.QUARTERLY;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TestTrailingProRation.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TestTrailingProRation.java
index ff53385..15a9b4c 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TestTrailingProRation.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TestTrailingProRation.java
@@ -26,6 +26,7 @@ import com.ning.billing.invoice.model.InvalidDateSequenceException;
 import com.ning.billing.invoice.tests.inAdvance.ProRationInAdvanceTestBase;
 
 public class TestTrailingProRation extends ProRationInAdvanceTestBase {
+
     @Override
     protected BillingPeriod getBillingPeriod() {
         return BillingPeriod.QUARTERLY;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/InternationalPriceMock.java b/invoice/src/test/java/com/ning/billing/invoice/tests/InternationalPriceMock.java
index 4e7467f..69dfd2f 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/InternationalPriceMock.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/InternationalPriceMock.java
@@ -25,6 +25,7 @@ import com.ning.billing.catalog.api.Price;
 import static org.testng.Assert.fail;
 
 public class InternationalPriceMock implements InternationalPrice {
+
     private final BigDecimal rate;
 
     public InternationalPriceMock(final BigDecimal rate) {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/InvoiceTestUtils.java b/invoice/src/test/java/com/ning/billing/invoice/tests/InvoiceTestUtils.java
index 574457f..b7bc6cb 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/InvoiceTestUtils.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/InvoiceTestUtils.java
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.UUID;
 
 import org.mockito.Mockito;
+import org.testng.Assert;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
@@ -29,13 +30,16 @@ 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.InvoicePayment.InvoicePaymentType;
+import com.ning.billing.invoice.dao.InvoiceItemModelDao;
 import com.ning.billing.invoice.dao.InvoiceItemSqlDao;
+import com.ning.billing.invoice.dao.InvoiceModelDao;
 import com.ning.billing.invoice.dao.InvoiceSqlDao;
 import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.entity.EntityPersistenceException;
 import com.ning.billing.util.svcapi.invoice.InvoiceInternalApi;
 
 import com.google.common.collect.ImmutableList;
@@ -51,8 +55,13 @@ public class InvoiceTestUtils {
                                                   final Currency currency,
                                                   final CallContext callContext,
                                                   final InternalCallContextFactory internalCallContextFactory) {
-        return createAndPersistInvoice(invoiceSqlDao, invoiceItemSqlDao, clock, ImmutableList.<BigDecimal>of(amount),
-                                       currency, callContext, internalCallContextFactory);
+        try {
+            return createAndPersistInvoice(invoiceSqlDao, invoiceItemSqlDao, clock, ImmutableList.<BigDecimal>of(amount),
+                                           currency, callContext, internalCallContextFactory);
+        } catch (EntityPersistenceException e) {
+            Assert.fail(e.getMessage());
+            return null;
+        }
     }
 
     public static Invoice createAndPersistInvoice(final InvoiceSqlDao invoiceSqlDao,
@@ -61,7 +70,7 @@ public class InvoiceTestUtils {
                                                   final List<BigDecimal> amounts,
                                                   final Currency currency,
                                                   final CallContext callContext,
-                                                  final InternalCallContextFactory internalCallContextFactory) {
+                                                  final InternalCallContextFactory internalCallContextFactory) throws EntityPersistenceException {
         final Invoice invoice = Mockito.mock(Invoice.class);
         final UUID invoiceId = UUID.randomUUID();
         final UUID accountId = UUID.randomUUID();
@@ -76,12 +85,16 @@ public class InvoiceTestUtils {
         final List<InvoiceItem> invoiceItems = new ArrayList<InvoiceItem>();
         for (final BigDecimal amount : amounts) {
             final InvoiceItem invoiceItem = createInvoiceItem(clock, invoiceId, accountId, amount, currency);
-            invoiceItemSqlDao.create(invoiceItem, internalCallContextFactory.createInternalCallContext(accountId, callContext));
+            invoiceItemSqlDao.create(new InvoiceItemModelDao(invoiceItem), internalCallContextFactory.createInternalCallContext(accountId, callContext));
             invoiceItems.add(invoiceItem);
         }
         Mockito.when(invoice.getInvoiceItems()).thenReturn(invoiceItems);
 
-        invoiceSqlDao.create(invoice, internalCallContextFactory.createInternalCallContext(accountId, callContext));
+        try {
+            invoiceSqlDao.create(new InvoiceModelDao(invoice), internalCallContextFactory.createInternalCallContext(accountId, callContext));
+        } catch (EntityPersistenceException e) {
+            Assert.fail(e.getMessage());
+        }
 
         return invoice;
     }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java
index 5562605..68f6cd4 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java
@@ -16,9 +16,10 @@
 
 package com.ning.billing.invoice.tests;
 
-import javax.annotation.Nullable;
 import java.math.BigDecimal;
 
+import javax.annotation.Nullable;
+
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
@@ -33,11 +34,11 @@ import com.ning.billing.entitlement.api.SubscriptionTransitionType;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.invoice.InvoiceTestSuiteWithEmbeddedDB;
 import com.ning.billing.invoice.model.InvoicingConfiguration;
-import com.ning.billing.mock.api.MockBillCycleDay;
 import com.ning.billing.util.svcapi.junction.BillingEvent;
 import com.ning.billing.util.svcapi.junction.BillingModeType;
 
 public abstract class InvoicingTestBase extends InvoiceTestSuiteWithEmbeddedDB {
+
     protected static final int NUMBER_OF_DECIMALS = InvoicingConfiguration.getNumberOfDecimals();
     protected static final int ROUNDING_METHOD = InvoicingConfiguration.getRoundingMode();
 
@@ -101,7 +102,7 @@ public abstract class InvoicingTestBase extends InvoiceTestSuiteWithEmbeddedDB {
                                       totalOrdering, type);
     }
 
-        protected BillingEvent createMockBillingEvent(@Nullable final Account account, final Subscription subscription,
+    protected BillingEvent createMockBillingEvent(@Nullable final Account account, final Subscription subscription,
                                                   final DateTime effectiveDate,
                                                   final Plan plan, final PlanPhase planPhase,
                                                   @Nullable final BigDecimal fixedPrice, @Nullable final BigDecimal recurringPrice,
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/ProRationTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/tests/ProRationTestBase.java
index fd5cf16..cacc246 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/ProRationTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/ProRationTestBase.java
@@ -19,7 +19,6 @@ package com.ning.billing.invoice.tests;
 import java.math.BigDecimal;
 import java.util.List;
 
-import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/TestChargeBacks.java b/invoice/src/test/java/com/ning/billing/invoice/tests/TestChargeBacks.java
index 78904ec..6cda9cb 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/TestChargeBacks.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/TestChargeBacks.java
@@ -28,6 +28,7 @@ import org.skife.jdbi.v2.exceptions.TransactionFailedException;
 import org.testng.annotations.BeforeSuite;
 import org.testng.annotations.Test;
 
+import com.ning.billing.ErrorCode;
 import com.ning.billing.KillbillTestSuiteWithEmbeddedDB;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.dbi.MysqlTestingHelper;
@@ -38,7 +39,7 @@ import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.invoice.api.InvoicePaymentApi;
 import com.ning.billing.invoice.api.invoice.DefaultInvoicePaymentApi;
 import com.ning.billing.invoice.api.svcs.DefaultInvoiceInternalApi;
-import com.ning.billing.invoice.dao.AuditedInvoiceDao;
+import com.ning.billing.invoice.dao.DefaultInvoiceDao;
 import com.ning.billing.invoice.dao.InvoiceDao;
 import com.ning.billing.invoice.dao.InvoiceItemSqlDao;
 import com.ning.billing.invoice.dao.InvoiceSqlDao;
@@ -86,7 +87,7 @@ public class TestChargeBacks extends InvoiceTestSuiteWithEmbeddedDB {
         invoiceItemSqlDao.test(internalCallContext);
         final NextBillingDatePoster nextBillingDatePoster = new MockNextBillingDatePoster();
         internalCallContextFactory = new InternalCallContextFactory(dbi, clock);
-        final InvoiceDao invoiceDao = new AuditedInvoiceDao(dbi, nextBillingDatePoster, clock, Mockito.mock(InternalBus.class));
+        final InvoiceDao invoiceDao = new DefaultInvoiceDao(dbi, nextBillingDatePoster, Mockito.mock(InternalBus.class));
         invoicePaymentApi = new DefaultInvoicePaymentApi(invoiceDao, internalCallContextFactory);
         invoiceApi = new DefaultInvoiceInternalApi(invoiceDao);
     }
@@ -162,9 +163,15 @@ public class TestChargeBacks extends InvoiceTestSuiteWithEmbeddedDB {
         assertEquals(accountId, invoice.getAccountId());
     }
 
-    @Test(groups = "slow", expectedExceptions = InvoiceApiException.class)
+    @Test(groups = "slow")
     public void testGetAccountIdFromPaymentIdBadPaymentId() throws InvoiceApiException {
-        invoicePaymentApi.getAccountIdFromInvoicePaymentId(UUID.randomUUID(), callContext);
+        try {
+            invoicePaymentApi.getAccountIdFromInvoicePaymentId(UUID.randomUUID(), callContext);
+            fail();
+        } catch (TransactionFailedException e) {
+            assertTrue(e.getCause() instanceof InvoiceApiException);
+            assertEquals(((InvoiceApiException) e.getCause()).getCode(), ErrorCode.CHARGE_BACK_COULD_NOT_FIND_ACCOUNT_ID.getCode());
+        }
     }
 
     @Test(groups = "slow")
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountEmailJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountEmailJson.java
index 3efaf7f..26311b8 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountEmailJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountEmailJson.java
@@ -44,8 +44,7 @@ public class AccountEmailJson {
         return email;
     }
 
-    public AccountEmail toAccountEmail() {
-        final UUID accountEmailId = UUID.randomUUID();
+    public AccountEmail toAccountEmail(final UUID accountEmailId) {
 
         return new AccountEmail() {
             @Override
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJsonNoSubscriptions.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJsonNoSubscriptions.java
index ded56fe..b9a75a9 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJsonNoSubscriptions.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJsonNoSubscriptions.java
@@ -44,7 +44,7 @@ public class BundleJsonNoSubscriptions extends BundleJsonSimple {
     }
 
     public BundleJsonNoSubscriptions(final SubscriptionBundle bundle) {
-        super(bundle.getId().toString(), bundle.getKey(), null);
+        super(bundle.getId().toString(), bundle.getExternalKey(), null);
         this.accountId = bundle.getAccountId().toString();
     }
 
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/CustomFieldJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/CustomFieldJson.java
index d6daf76..a72db5c 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/CustomFieldJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/CustomFieldJson.java
@@ -37,8 +37,8 @@ public class CustomFieldJson {
     }
 
     public CustomFieldJson(final CustomField input) {
-        this.name = input.getName();
-        this.value = input.getValue();
+        this.name = input.getFieldName();
+        this.value = input.getFieldValue();
     }
 
     public String getName() {
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
index 4777f64..0162c8f 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
@@ -76,6 +76,7 @@ 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.AuditUserApi;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
@@ -453,7 +454,7 @@ public class AccountResource extends JaxRsResourceBase {
                                        @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                        @HeaderParam(HDR_REASON) final String reason,
                                        @HeaderParam(HDR_COMMENT) final String comment,
-                                       @javax.ws.rs.core.Context final HttpServletRequest request) {
+                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws CustomFieldApiException {
         return super.createCustomFields(UUID.fromString(id), customFields,
                                         context.createContext(createdBy, reason, comment, request));
     }
@@ -568,7 +569,7 @@ public class AccountResource extends JaxRsResourceBase {
         // Make sure the account exist or we will confuse the history and auditing code
         accountApi.getAccountById(accountId, callContext);
 
-        accountApi.addEmail(accountId, json.toAccountEmail(), callContext);
+        accountApi.addEmail(accountId, json.toAccountEmail(UUID.randomUUID()), callContext);
 
         return uriBuilder.buildResponse(AccountResource.class, "getEmails", json.getAccountId());
     }
@@ -583,10 +584,15 @@ public class AccountResource extends JaxRsResourceBase {
                                 @HeaderParam(HDR_COMMENT) final String comment,
                                 @javax.ws.rs.core.Context final HttpServletRequest request) {
         final UUID accountId = UUID.fromString(id);
-        final AccountEmailJson accountEmailJson = new AccountEmailJson(id, email);
-        final AccountEmail accountEmail = accountEmailJson.toAccountEmail();
-        accountApi.removeEmail(accountId, accountEmail, context.createContext(createdBy, reason, comment, request));
 
+        final List<AccountEmail> emails = accountApi.getEmails(accountId, context.createContext(request));
+        for (AccountEmail cur : emails) {
+            if (cur.getEmail().equals(email)) {
+                final AccountEmailJson accountEmailJson = new AccountEmailJson(accountId.toString(), email);
+                final AccountEmail accountEmail = accountEmailJson.toAccountEmail(cur.getId());
+                accountApi.removeEmail(accountId, accountEmail, context.createContext(createdBy, reason, comment, request));
+            }
+        }
         return Response.status(Status.OK).build();
     }
 
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
index 3bca566..9549904 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
@@ -51,6 +51,7 @@ import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
 import com.ning.billing.jaxrs.util.Context;
 import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
 import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
@@ -149,7 +150,7 @@ public class BundleResource extends JaxRsResourceBase {
                                        @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                        @HeaderParam(HDR_REASON) final String reason,
                                        @HeaderParam(HDR_COMMENT) final String comment,
-                                       @javax.ws.rs.core.Context final HttpServletRequest request) {
+                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws CustomFieldApiException{
         return super.createCustomFields(UUID.fromString(id), customFields,
                                         context.createContext(createdBy, reason, comment, request));
     }
@@ -194,7 +195,7 @@ public class BundleResource extends JaxRsResourceBase {
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
         final SubscriptionBundle bundle = entitlementApi.getBundleFromId(UUID.fromString(id), callContext);
         final DateTime inputDate = (requestedDate != null) ? DATE_TIME_FORMATTER.parseDateTime(requestedDate) : null;
-        final SubscriptionBundle newBundle = transferApi.transferBundle(bundle.getAccountId(), UUID.fromString(json.getAccountId()), bundle.getKey(), inputDate, transferAddOn,
+        final SubscriptionBundle newBundle = transferApi.transferBundle(bundle.getAccountId(), UUID.fromString(json.getAccountId()), bundle.getExternalKey(), inputDate, transferAddOn,
                                                                         cancelImmediatley, callContext);
 
         return uriBuilder.buildResponse(BundleResource.class, "getBundle", newBundle.getId(), uriInfo.getBaseUri().toString());
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
index 5e84036..952bf60 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
@@ -65,6 +65,7 @@ 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.util.api.AuditUserApi;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
@@ -439,7 +440,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                        @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                        @HeaderParam(HDR_REASON) final String reason,
                                        @HeaderParam(HDR_COMMENT) final String comment,
-                                       @javax.ws.rs.core.Context final HttpServletRequest request) {
+                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws CustomFieldApiException {
         return super.createCustomFields(UUID.fromString(id), customFields,
                                         context.createContext(createdBy, reason, comment, request));
     }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
index 4114e9e..88a579a 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
@@ -20,7 +20,6 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -32,6 +31,7 @@ import org.joda.time.format.ISODateTimeFormat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.ning.billing.BillingExceptionBase;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.ObjectType;
 import com.ning.billing.jaxrs.json.CustomFieldJson;
@@ -39,6 +39,7 @@ import com.ning.billing.jaxrs.json.TagJson;
 import com.ning.billing.jaxrs.util.Context;
 import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
 import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
@@ -83,10 +84,10 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
     }
 
     protected Response getTags(final UUID id, final boolean withAudit, final TenantContext context) throws TagDefinitionApiException {
-        final Map<String, Tag> tags = tagUserApi.getTags(id, getObjectType(), context);
+        final List<Tag> tags = tagUserApi.getTags(id, getObjectType(), context);
         final Collection<UUID> tagIdList = (tags.size() == 0) ?
                                            Collections.<UUID>emptyList() :
-                                           Collections2.transform(tags.values(), new Function<Tag, UUID>() {
+                                           Collections2.transform(tags, new Function<Tag, UUID>() {
                                                @Override
                                                public UUID apply(final Tag input) {
                                                    return input.getTagDefinitionId();
@@ -153,10 +154,10 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
     }
 
     protected Response getCustomFields(final UUID id, final TenantContext context) {
-        final Map<String, CustomField> fields = customFieldUserApi.getCustomFields(id, getObjectType(), context);
+        final List<CustomField> fields = customFieldUserApi.getCustomFields(id, getObjectType(), context);
 
         final List<CustomFieldJson> result = new LinkedList<CustomFieldJson>();
-        for (final CustomField cur : fields.values()) {
+        for (final CustomField cur : fields) {
             result.add(new CustomFieldJson(cur));
         }
 
@@ -165,13 +166,13 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
 
     protected Response createCustomFields(final UUID id,
                                           final List<CustomFieldJson> customFields,
-                                          final CallContext context) {
+                                          final CallContext context) throws CustomFieldApiException {
         final LinkedList<CustomField> input = new LinkedList<CustomField>();
         for (final CustomFieldJson cur : customFields) {
-            input.add(new StringCustomField(cur.getName(), cur.getValue()));
+            input.add(new StringCustomField(cur.getName(), cur.getValue(), getObjectType(), id, context.getCreatedDate()));
         }
 
-        customFieldUserApi.saveCustomFields(id, getObjectType(), input, context);
+        customFieldUserApi.addCustomFields(input, context);
         return uriBuilder.buildResponse(this.getClass(), "createCustomFields", id);
     }
 
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
index 2b22ea1..b8f1c4e 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
@@ -57,6 +57,7 @@ import com.ning.billing.payment.api.PaymentApi;
 import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.api.Refund;
 import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
@@ -206,7 +207,7 @@ public class PaymentResource extends JaxRsResourceBase {
                                        @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                        @HeaderParam(HDR_REASON) final String reason,
                                        @HeaderParam(HDR_COMMENT) final String comment,
-                                       @javax.ws.rs.core.Context final HttpServletRequest request) {
+                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws CustomFieldApiException {
         return super.createCustomFields(UUID.fromString(id), customFields,
                                         context.createContext(createdBy, reason, comment, request));
     }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
index 590ec31..97b3c28 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
@@ -55,6 +55,7 @@ import com.ning.billing.jaxrs.util.Context;
 import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
 import com.ning.billing.jaxrs.util.KillbillEventHandler;
 import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
@@ -366,7 +367,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
                                        @HeaderParam(HDR_REASON) final String reason,
                                        @HeaderParam(HDR_COMMENT) final String comment,
                                        @javax.ws.rs.core.Context final UriInfo uriInfo,
-                                       @javax.ws.rs.core.Context final HttpServletRequest request) {
+                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws CustomFieldApiException {
         return super.createCustomFields(UUID.fromString(id), customFields,
                                         context.createContext(createdBy, reason, comment, request));
     }
diff --git a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAccountEmailJson.java b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAccountEmailJson.java
index 08a56fd..9c0e859 100644
--- a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAccountEmailJson.java
+++ b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAccountEmailJson.java
@@ -54,7 +54,7 @@ public class TestAccountEmailJson extends JaxrsTestSuite {
         Assert.assertEquals(accountEmailJson.getAccountId(), accountId);
         Assert.assertEquals(accountEmailJson.getEmail(), email);
 
-        final AccountEmail accountEmail = accountEmailJson.toAccountEmail();
+        final AccountEmail accountEmail = accountEmailJson.toAccountEmail(UUID.randomUUID());
         Assert.assertEquals(accountEmail.getAccountId().toString(), accountId);
         Assert.assertEquals(accountEmail.getEmail(), email);
     }
diff --git a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAuditLogJson.java b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAuditLogJson.java
index e7784f2..908daaf 100644
--- a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAuditLogJson.java
+++ b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAuditLogJson.java
@@ -80,7 +80,7 @@ public class TestAuditLogJson extends JaxrsTestSuite {
         final TableName tableName = TableName.ACCOUNT_EMAIL_HISTORY;
         final long recordId = Long.MAX_VALUE;
         final ChangeType changeType = ChangeType.DELETE;
-        final EntityAudit entityAudit = new EntityAudit(tableName, recordId, changeType);
+        final EntityAudit entityAudit = new EntityAudit(tableName, recordId, changeType, null);
 
         final AuditLog auditLog = new DefaultAuditLog(entityAudit, callContext);
 
@@ -89,7 +89,7 @@ public class TestAuditLogJson extends JaxrsTestSuite {
         Assert.assertNotNull(auditLogJson.getChangeDate());
         Assert.assertEquals(auditLogJson.getChangedBy(), callContext.getUserName());
         Assert.assertEquals(auditLogJson.getReasonCode(), callContext.getReasonCode());
-        Assert.assertEquals(auditLogJson.getComments(), callContext.getComment());
+        Assert.assertEquals(auditLogJson.getComments(), callContext.getComments());
         Assert.assertEquals(auditLogJson.getUserToken(), callContext.getUserToken().toString());
     }
 }
diff --git a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestBundleJsonNoSubscriptions.java b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestBundleJsonNoSubscriptions.java
index d87274a..3702e5b 100644
--- a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestBundleJsonNoSubscriptions.java
+++ b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestBundleJsonNoSubscriptions.java
@@ -52,7 +52,7 @@ public class TestBundleJsonNoSubscriptions extends JaxrsTestSuite {
         final String externalKey = UUID.randomUUID().toString();
         final UUID accountId = UUID.randomUUID();
         Mockito.when(bundle.getId()).thenReturn(bundleId);
-        Mockito.when(bundle.getKey()).thenReturn(externalKey);
+        Mockito.when(bundle.getExternalKey()).thenReturn(externalKey);
         Mockito.when(bundle.getAccountId()).thenReturn(accountId);
 
         final BundleJsonNoSubscriptions bundleJsonNoSubscriptions = new BundleJsonNoSubscriptions(bundle);
diff --git a/junction/src/main/java/com/ning/billing/junction/api/svcs/DefaultInternalBlockingApi.java b/junction/src/main/java/com/ning/billing/junction/api/svcs/DefaultInternalBlockingApi.java
index 554a5eb..48a4988 100644
--- a/junction/src/main/java/com/ning/billing/junction/api/svcs/DefaultInternalBlockingApi.java
+++ b/junction/src/main/java/com/ning/billing/junction/api/svcs/DefaultInternalBlockingApi.java
@@ -43,7 +43,7 @@ public class DefaultInternalBlockingApi implements BlockingInternalApi {
 
     @Override
     public BlockingState getBlockingStateFor(final Blockable overdueable, final InternalTenantContext context) {
-        BlockingState state = dao.getBlockingStateFor(overdueable, context);
+        BlockingState state = dao.getBlockingStateFor(overdueable.getId(), context);
         if (state == null) {
             state = DefaultBlockingState.getClearState();
         }
@@ -57,7 +57,7 @@ public class DefaultInternalBlockingApi implements BlockingInternalApi {
 
     @Override
     public List<BlockingState> getBlockingHistory(final Blockable overdueable, final InternalTenantContext context) {
-        return dao.getBlockingHistoryFor(overdueable, context);
+        return dao.getBlockingHistoryFor(overdueable.getId(), context);
     }
 
     @Override
diff --git a/junction/src/main/java/com/ning/billing/junction/dao/BlockingStateDao.java b/junction/src/main/java/com/ning/billing/junction/dao/BlockingStateDao.java
index f33d3d4..8266091 100644
--- a/junction/src/main/java/com/ning/billing/junction/dao/BlockingStateDao.java
+++ b/junction/src/main/java/com/ning/billing/junction/dao/BlockingStateDao.java
@@ -27,16 +27,9 @@ import com.ning.billing.util.clock.Clock;
 
 public interface BlockingStateDao {
 
-    // Read
-    public BlockingState getBlockingStateFor(Blockable blockable, InternalTenantContext context);
-
     public BlockingState getBlockingStateFor(UUID blockableId, InternalTenantContext context);
 
-    public List<BlockingState> getBlockingHistoryFor(Blockable blockable, InternalTenantContext context);
-
     public List<BlockingState> getBlockingHistoryFor(UUID blockableId, InternalTenantContext context);
 
-    // Write
     <T extends Blockable> void setBlockingState(BlockingState state, Clock clock, InternalCallContext context);
-
-} 
+}
diff --git a/junction/src/main/java/com/ning/billing/junction/dao/BlockingStateModelDao.java b/junction/src/main/java/com/ning/billing/junction/dao/BlockingStateModelDao.java
new file mode 100644
index 0000000..5dbdd25
--- /dev/null
+++ b/junction/src/main/java/com/ning/billing/junction/dao/BlockingStateModelDao.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.junction.dao;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.junction.api.Blockable;
+import com.ning.billing.junction.api.Blockable.Type;
+import com.ning.billing.junction.api.BlockingState;
+import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.dao.EntityModelDao;
+import com.ning.billing.util.svcapi.junction.DefaultBlockingState;
+
+public class BlockingStateModelDao extends EntityBase implements EntityModelDao<BlockingState>{
+
+    private final UUID blockableId;
+    private final Type type;
+    private final String state;
+    private final String service;
+    private final Boolean blockChange;
+    private final Boolean blockEntitlement;
+    private final Boolean blockBilling;
+
+    public BlockingStateModelDao(final UUID id, final UUID blockableId, final Type type, final String state, final String service, final Boolean blockChange, final Boolean blockEntitlement,
+                                 final Boolean blockBilling, final DateTime createDate, final DateTime updateDate) {
+        super(id, createDate, updateDate);
+        this.blockableId = blockableId;
+        this.type = type;
+        this.state = state;
+        this.service = service;
+        this.blockChange = blockChange;
+        this.blockEntitlement = blockEntitlement;
+        this.blockBilling = blockBilling;
+    }
+
+    public BlockingStateModelDao(final BlockingState src, InternalCallContext context) {
+        this(src.getId(), src.getBlockedId(), src.getType(), src.getStateName(), src.getService(), src.isBlockChange(),
+             src.isBlockEntitlement(), src.isBlockBilling(), context.getCreatedDate(), context.getUpdatedDate());
+    }
+
+    public UUID getBlockableId() {
+        return blockableId;
+    }
+
+    public Type getType() {
+        return type;
+    }
+
+    public String getState() {
+        return state;
+    }
+
+    public String getService() {
+        return service;
+    }
+
+    public Boolean getBlockChange() {
+        return blockChange;
+    }
+
+    public Boolean getBlockEntitlement() {
+        return blockEntitlement;
+    }
+
+    public Boolean getBlockBilling() {
+        return blockBilling;
+    }
+
+    public static BlockingState toBlockingState(BlockingStateModelDao src) {
+        if (src == null) {
+            return null;
+        }
+        return new DefaultBlockingState(src.getId(), src.getBlockableId(),src.getState(), src.getType(), src.getService(), src.getBlockChange(), src.getBlockEntitlement(), src.getBlockBilling(),
+                                 src.getCreatedDate(), src.getUpdatedDate());
+    }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.BLOCKING_STATES;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("BlockingStateModelDao");
+        sb.append("{blockableId=").append(blockableId);
+        sb.append(", type=").append(type);
+        sb.append(", state='").append(state).append('\'');
+        sb.append(", service='").append(service).append('\'');
+        sb.append(", blockChange=").append(blockChange);
+        sb.append(", blockEntitlement=").append(blockEntitlement);
+        sb.append(", blockBilling=").append(blockBilling);
+        sb.append('}');
+        return sb.toString();
+    }
+}
diff --git a/junction/src/main/java/com/ning/billing/junction/dao/BlockingStateSqlDao.java b/junction/src/main/java/com/ning/billing/junction/dao/BlockingStateSqlDao.java
index c9260dc..cb91383 100644
--- a/junction/src/main/java/com/ning/billing/junction/dao/BlockingStateSqlDao.java
+++ b/junction/src/main/java/com/ning/billing/junction/dao/BlockingStateSqlDao.java
@@ -22,72 +22,40 @@ import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
-import org.skife.jdbi.v2.sqlobject.SqlUpdate;
-import org.skife.jdbi.v2.sqlobject.customizers.Mapper;
-import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
-import com.ning.billing.junction.api.Blockable;
+import com.ning.billing.junction.api.Blockable.Type;
 import com.ning.billing.junction.api.BlockingApiException;
 import com.ning.billing.junction.api.BlockingState;
-import com.ning.billing.junction.api.Blockable.Type;
-import com.ning.billing.overdue.OverdueState;
-import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.clock.Clock;
-import com.ning.billing.util.dao.BinderBase;
 import com.ning.billing.util.dao.MapperBase;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 import com.ning.billing.util.svcapi.junction.DefaultBlockingState;
 
-@ExternalizedSqlViaStringTemplate3()
-public interface BlockingStateSqlDao extends BlockingStateDao, CloseMe, Transmogrifier {
-
-    @Override
-    @SqlUpdate
-    public abstract <T extends Blockable> void setBlockingState(@Bind(binder = BlockingStateBinder.class) BlockingState state,
-                                                                @Bind(binder = CurrentTimeBinder.class) Clock clock,
-                                                                @InternalTenantContextBinder final InternalCallContext context);
-
-    @Override
-    @SqlQuery
-    @Mapper(BlockingHistorySqlMapper.class)
-    public abstract BlockingState getBlockingStateFor(@Bind(binder = BlockableBinder.class) Blockable overdueable,
-                                                      @InternalTenantContextBinder final InternalTenantContext context);
-
-    @Override
-    @SqlQuery
-    @Mapper(BlockingHistorySqlMapper.class)
-    public abstract BlockingState getBlockingStateFor(@Bind(binder = UUIDBinder.class) UUID overdueableId,
-                                                      @InternalTenantContextBinder final InternalTenantContext context);
+@EntitySqlDaoStringTemplate
+@RegisterMapper(BlockingStateSqlDao.BlockingHistorySqlMapper.class)
+public interface BlockingStateSqlDao extends EntitySqlDao<BlockingStateModelDao, BlockingState> {
 
-    @Override
     @SqlQuery
-    @Mapper(BlockingHistorySqlMapper.class)
-    public abstract List<BlockingState> getBlockingHistoryFor(@Bind(binder = BlockableBinder.class) Blockable blockable,
-                                                              @InternalTenantContextBinder final InternalTenantContext context);
+    public BlockingStateModelDao getBlockingStateFor(@Bind("blockableId") UUID blockableId, @BindBean final InternalTenantContext context);
 
-    @Override
     @SqlQuery
-    @Mapper(BlockingHistorySqlMapper.class)
-    public abstract List<BlockingState> getBlockingHistoryFor(@Bind(binder = UUIDBinder.class) UUID blockableId,
-                                                              @InternalTenantContextBinder final InternalTenantContext context);
+    public abstract List<BlockingStateModelDao> getBlockingHistoryFor(@Bind("blockableId") UUID blockableId,
+                                                                      @BindBean final InternalTenantContext context);
 
-    public class BlockingHistorySqlMapper extends MapperBase implements ResultSetMapper<BlockingState> {
+    public class BlockingHistorySqlMapper extends MapperBase implements ResultSetMapper<BlockingStateModelDao> {
 
         @Override
-        public BlockingState map(final int index, final ResultSet r, final StatementContext ctx)
+        public BlockingStateModelDao map(final int index, final ResultSet r, final StatementContext ctx)
                 throws SQLException {
 
             final UUID id;
-            final DateTime timestamp;
             final UUID blockableId;
             final String stateName;
             final String service;
@@ -95,12 +63,10 @@ public interface BlockingStateSqlDao extends BlockingStateDao, CloseMe, Transmog
             final boolean blockEntitlement;
             final boolean blockBilling;
             final Type type;
-            DateTime createdDate;
-            DateTime updatedDate;
+            final DateTime createdDate;
 
             try {
                 id = UUID.fromString(r.getString("id"));
-                timestamp = getDateTime(r, "created_date");
                 blockableId = UUID.fromString(r.getString("blockable_id"));
                 stateName = r.getString("state") == null ? DefaultBlockingState.CLEAR_STATE_NAME : r.getString("state");
                 type = Type.get(r.getString("type"));
@@ -112,74 +78,7 @@ public interface BlockingStateSqlDao extends BlockingStateDao, CloseMe, Transmog
             } catch (BlockingApiException e) {
                 throw new SQLException(e);
             }
-            return new DefaultBlockingState(id, blockableId, stateName, type, service, blockChange, blockEntitlement, blockBilling, timestamp, createdDate);
-        }
-    }
-
-    public static class BlockingStateSqlMapper extends MapperBase implements ResultSetMapper<String> {
-
-        @Override
-        public String map(final int index, final ResultSet r, final StatementContext ctx)
-                throws SQLException {
-            return r.getString("state") == null ? DefaultBlockingState.CLEAR_STATE_NAME : r.getString("state");
-        }
-    }
-
-    public static class BlockingStateBinder extends BinderBase implements Binder<Bind, DefaultBlockingState> {
-
-        @Override
-        public void bind(@SuppressWarnings("rawtypes") final SQLStatement stmt, final Bind bind, final DefaultBlockingState state) {
-            stmt.bind("id", state.getId().toString());
-            stmt.bind("blockable_id", state.getBlockedId().toString());
-            stmt.bind("state", state.getStateName().toString());
-            stmt.bind("type", state.getType().toString());
-            stmt.bind("service", state.getService().toString());
-            stmt.bind("block_change", state.isBlockChange());
-            stmt.bind("block_entitlement", state.isBlockEntitlement());
-            stmt.bind("block_billing", state.isBlockBilling());
-        }
-    }
-
-    public static class UUIDBinder extends BinderBase implements Binder<Bind, UUID> {
-
-        @Override
-        public void bind(@SuppressWarnings("rawtypes") final SQLStatement stmt, final Bind bind, final UUID id) {
-            stmt.bind("blockable_id", id.toString());
+            return new BlockingStateModelDao(id, blockableId, type, stateName, service, blockChange, blockEntitlement, blockBilling, createdDate, createdDate);
         }
     }
-
-    public static class BlockableBinder extends BinderBase implements Binder<Bind, Blockable> {
-
-        @Override
-        public void bind(@SuppressWarnings("rawtypes") final SQLStatement stmt, final Bind bind, final Blockable overdueable) {
-            stmt.bind("blockable_id", overdueable.getId().toString());
-        }
-    }
-
-    public static class OverdueStateBinder<T extends Blockable> extends BinderBase implements Binder<Bind, OverdueState<T>> {
-
-        @Override
-        public void bind(final SQLStatement<?> stmt, final Bind bind, final OverdueState<T> overdueState) {
-            stmt.bind("state", overdueState.getName());
-        }
-    }
-
-    public class BlockableTypeBinder extends BinderBase implements Binder<Bind, Blockable.Type> {
-
-        @Override
-        public void bind(@SuppressWarnings("rawtypes") final SQLStatement stmt, final Bind bind, final Type type) {
-            stmt.bind("type", type.name());
-        }
-
-    }
-
-    public static class CurrentTimeBinder extends BinderBase implements Binder<Bind, Clock> {
-
-        @Override
-        public void bind(@SuppressWarnings("rawtypes") final SQLStatement stmt, final Bind bind, final Clock clock) {
-            stmt.bind("created_date", clock.getUTCNow().toDate());
-        }
-
-    }
-
 }
diff --git a/junction/src/main/java/com/ning/billing/junction/dao/DefaultBlockingStateDao.java b/junction/src/main/java/com/ning/billing/junction/dao/DefaultBlockingStateDao.java
new file mode 100644
index 0000000..1ea8e5a
--- /dev/null
+++ b/junction/src/main/java/com/ning/billing/junction/dao/DefaultBlockingStateDao.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.junction.dao;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+
+import org.skife.jdbi.v2.IDBI;
+
+import com.ning.billing.junction.api.Blockable;
+import com.ning.billing.junction.api.BlockingState;
+import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+
+public class DefaultBlockingStateDao implements BlockingStateDao {
+
+    private final EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao;
+
+    @Inject
+    public DefaultBlockingStateDao(final IDBI dbi) {
+        this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi);
+    }
+
+    @Override
+    public BlockingState getBlockingStateFor(final UUID blockableId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<BlockingState>() {
+            @Override
+            public BlockingState inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final BlockingStateModelDao model = entitySqlDaoWrapperFactory.become(BlockingStateSqlDao.class).getBlockingStateFor(blockableId, context);
+                return BlockingStateModelDao.toBlockingState(model);
+
+            }
+        });
+    }
+
+    @Override
+    public List<BlockingState> getBlockingHistoryFor(final UUID blockableId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<BlockingState>>() {
+            @Override
+            public List<BlockingState> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final List<BlockingStateModelDao> models = entitySqlDaoWrapperFactory.become(BlockingStateSqlDao.class).getBlockingHistoryFor(blockableId, context);
+                return new ArrayList<BlockingState>(Collections2.transform(models, new Function<BlockingStateModelDao, BlockingState>() {
+                    @Override
+                    public BlockingState apply(@Nullable final BlockingStateModelDao src) {
+                        return BlockingStateModelDao.toBlockingState(src);
+                    }
+                }));
+            }
+        });
+    }
+
+    @Override
+    public <T extends Blockable> void setBlockingState(final BlockingState state, final Clock clock, final InternalCallContext context) {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final BlockingStateSqlDao sqlDao = entitySqlDaoWrapperFactory.become(BlockingStateSqlDao.class);
+                sqlDao.create(new BlockingStateModelDao(state, context), context);
+                return null;
+            }
+        });
+    }
+}
diff --git a/junction/src/main/java/com/ning/billing/junction/glue/DefaultJunctionModule.java b/junction/src/main/java/com/ning/billing/junction/glue/DefaultJunctionModule.java
index e7b37bd..756b50c 100644
--- a/junction/src/main/java/com/ning/billing/junction/glue/DefaultJunctionModule.java
+++ b/junction/src/main/java/com/ning/billing/junction/glue/DefaultJunctionModule.java
@@ -26,12 +26,14 @@ import com.ning.billing.junction.block.BlockingChecker;
 import com.ning.billing.junction.block.DefaultBlockingChecker;
 import com.ning.billing.junction.dao.BlockingStateDao;
 import com.ning.billing.junction.dao.BlockingStateSqlDao;
+import com.ning.billing.junction.dao.DefaultBlockingStateDao;
 import com.ning.billing.junction.plumbing.api.BlockingAccountUserApi;
 import com.ning.billing.junction.plumbing.api.BlockingEntitlementUserApi;
 import com.ning.billing.junction.plumbing.billing.BlockingCalculator;
 import com.ning.billing.junction.plumbing.billing.DefaultInternalBillingApi;
 import com.ning.billing.util.svcapi.junction.BillingInternalApi;
 import com.ning.billing.util.svcapi.junction.BlockingInternalApi;
+import com.ning.billing.util.svcapi.junction.DefaultBlockingState;
 
 import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
@@ -63,7 +65,7 @@ public class DefaultJunctionModule extends AbstractModule implements JunctionMod
     }
 
     public void installBlockingStateDao() {
-        bind(BlockingStateDao.class).toProvider(BlockingDaoProvider.class);
+        bind(BlockingStateDao.class).to(DefaultBlockingStateDao.class).asEagerSingleton();
     }
 
     public void installAccountUserApi() {
@@ -81,19 +83,4 @@ public class DefaultJunctionModule extends AbstractModule implements JunctionMod
     public void installBlockingCalculator() {
         bind(BlockingCalculator.class).asEagerSingleton();
     }
-
-    public static class BlockingDaoProvider implements Provider<BlockingStateDao> {
-        private final IDBI dbi;
-
-
-        @Inject
-        public BlockingDaoProvider(final IDBI dbi) {
-            this.dbi = dbi;
-        }
-
-        @Override
-        public BlockingStateDao get() {
-            return dbi.onDemand(BlockingStateSqlDao.class);
-        }
-    }
 }
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccountUserApi.java b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccountUserApi.java
index 3d8a7ab..85d01cc 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccountUserApi.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccountUserApi.java
@@ -101,7 +101,7 @@ public class BlockingAccountUserApi implements AccountUserApi {
     }
 
     @Override
-    public void addEmail(final UUID accountId, final AccountEmail email, final CallContext context) {
+    public void addEmail(final UUID accountId, final AccountEmail email, final CallContext context) throws AccountApiException {
         userApi.addEmail(accountId, email, context);
     }
 
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscriptionBundle.java b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscriptionBundle.java
index b852ac8..c69407e 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscriptionBundle.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscriptionBundle.java
@@ -60,8 +60,8 @@ public class BlockingSubscriptionBundle implements SubscriptionBundle {
     }
 
     @Override
-    public String getKey() {
-        return subscriptionBundle.getKey();
+    public String getExternalKey() {
+        return subscriptionBundle.getExternalKey();
     }
 
     @Override
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
index 591ff9e..2804a04 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
@@ -82,8 +82,8 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
             final Account account = accountApi.getAccountById(accountId, context);
 
             // Check to see if billing is off for the account
-            final Map<String, Tag> accountTags = tagApi.getTags(accountId, ObjectType.ACCOUNT, context);
-            for (final Tag cur : accountTags.values()) {
+            final List<Tag> accountTags = tagApi.getTags(accountId, ObjectType.ACCOUNT, context);
+            for (final Tag cur : accountTags) {
                 if (ControlTagType.AUTO_INVOICING_OFF.getId().equals(cur.getTagDefinitionId())) {
                     result.setAccountAutoInvoiceIsOff(true);
                     return result; // billing is off, we are done
@@ -116,10 +116,10 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
             final List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(bundle.getId(), context);
 
             //Check if billing is off for the bundle
-            final Map<String, Tag> bundleTags = tagApi.getTags(bundle.getId(), ObjectType.BUNDLE, context);
+            final List<Tag> bundleTags = tagApi.getTags(bundle.getId(), ObjectType.BUNDLE, context);
 
             boolean found_AUTO_INVOICING_OFF = false;
-            for (final Tag cur : bundleTags.values()) {
+            for (final Tag cur : bundleTags) {
                 if (ControlTagType.AUTO_INVOICING_OFF.getId().equals(cur.getTagDefinitionId())) {
                     found_AUTO_INVOICING_OFF = true;
                     break;
diff --git a/junction/src/main/resources/com/ning/billing/junction/dao/BlockingStateSqlDao.sql.stg b/junction/src/main/resources/com/ning/billing/junction/dao/BlockingStateSqlDao.sql.stg
index 9a9077d..66a246f 100644
--- a/junction/src/main/resources/com/ning/billing/junction/dao/BlockingStateSqlDao.sql.stg
+++ b/junction/src/main/resources/com/ning/billing/junction/dao/BlockingStateSqlDao.sql.stg
@@ -1,73 +1,52 @@
-group BlockingStateSqlDao;
+group BlockingStateSqlDao: EntitySqlDao;
 
-CHECK_TENANT() ::= "tenant_record_id = :tenantRecordId"
-AND_CHECK_TENANT() ::= "AND <CHECK_TENANT()>"
 
-getBlockingStateFor() ::= <<
-    select
-        id
-      , blockable_id
-      , state
-      , type
-      , service
-      , block_change
-      , block_entitlement
-      , block_billing
-      , created_date
-      , tenant_record_id
-    from blocking_states
-    where blockable_id = :blockable_id
-    <AND_CHECK_TENANT()>
-    -- We want the current state, hence the order desc and limit 1
-    order by created_date desc, record_id desc
-    limit 1
-    ;
+tableName() ::= "blocking_states"
+
+tableFields(prefix) ::= <<
+  <prefix>blockable_id
+, <prefix>type
+, <prefix>state
+, <prefix>service
+, <prefix>block_change
+, <prefix>block_entitlement
+, <prefix>block_billing
+, <prefix>created_date
 >>
 
-getBlockingHistoryFor() ::= <<
-    select
-       id
-      , blockable_id
-      , state
-      , type
-      , service
-      , block_change
-      , block_entitlement
-      , block_billing
-      , created_date
-      , tenant_record_id
-    from blocking_states
-    where blockable_id = :blockable_id
-    <AND_CHECK_TENANT()>
-    -- We want the history in order
-    order by created_date asc, record_id asc
-    ;
+
+tableValues() ::= <<
+  :blockableId
+, :type
+, :state
+, :service
+, :blockChange
+, :blockEntitlement
+, :blockBilling
+, :createdDate
 >>
 
-setBlockingState() ::= <<
-    insert into blocking_states (
-       id
-      , blockable_id
-      , state
-      , type
-      , service
-      , block_change
-      , block_entitlement
-      , block_billing
-      , created_date
-      , account_record_id
-      , tenant_record_id
-    ) values (
-        :id
-      , :blockable_id
-      , :state
-      , :type
-      , :service
-      , :block_change
-      , :block_entitlement
-      , :block_billing
-      , :created_date
-      , :accountRecordId
-      , :tenantRecordId
-    );
+getBlockingStateFor() ::= <<
+select
+<allTableFields()>
+from
+<tableName()>
+where blockable_id = :blockableId
+<AND_CHECK_TENANT()>
+-- We want the current state, hence the order desc and limit 1
+order by record_id desc
+limit 1
+;
+>>
+
+getBlockingHistoryFor() ::= <<
+select
+<allTableFields()>
+from
+<tableName()>
+where blockable_id = :blockableId
+<AND_CHECK_TENANT()>
+-- We want the current state, hence the order desc and limit 1
+order by record_id asc
+;
 >>
diff --git a/junction/src/test/java/com/ning/billing/junction/api/blocking/TestDefaultBlockingApi.java b/junction/src/test/java/com/ning/billing/junction/api/blocking/TestDefaultBlockingApi.java
index 5d1c7a2..e763464 100644
--- a/junction/src/test/java/com/ning/billing/junction/api/blocking/TestDefaultBlockingApi.java
+++ b/junction/src/test/java/com/ning/billing/junction/api/blocking/TestDefaultBlockingApi.java
@@ -31,7 +31,7 @@ import com.ning.billing.junction.api.Blockable.Type;
 import com.ning.billing.junction.api.svcs.DefaultInternalBlockingApi;
 import com.ning.billing.junction.api.BlockingState;
 import com.ning.billing.junction.dao.BlockingStateDao;
-import com.ning.billing.junction.dao.BlockingStateSqlDao;
+import com.ning.billing.junction.dao.DefaultBlockingStateDao;
 import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.svcapi.junction.DefaultBlockingState;
 
@@ -43,11 +43,10 @@ public class TestDefaultBlockingApi extends JunctionTestSuiteWithEmbeddedDB {
 
     @BeforeMethod(groups = "slow")
     public void setUp() throws Exception {
-        final BlockingStateDao blockingStateDao = getMysqlTestingHelper().getDBI().onDemand(BlockingStateSqlDao.class);
+        final BlockingStateDao blockingStateDao = new DefaultBlockingStateDao(getMysqlTestingHelper().getDBI());
         blockingApi = new DefaultInternalBlockingApi(blockingStateDao, clock);
     }
 
-    // API_FIX
     @Test(groups = "slow", enabled=false)
     public void testSetBlockingStateOnBundle() throws Exception {
         final UUID bundleId = UUID.randomUUID();
diff --git a/junction/src/test/java/com/ning/billing/junction/blocking/TestBlockingChecker.java b/junction/src/test/java/com/ning/billing/junction/blocking/TestBlockingChecker.java
index f3b2856..1b3d1ba 100644
--- a/junction/src/test/java/com/ning/billing/junction/blocking/TestBlockingChecker.java
+++ b/junction/src/test/java/com/ning/billing/junction/blocking/TestBlockingChecker.java
@@ -53,16 +53,7 @@ public class TestBlockingChecker extends JunctionTestSuite {
     private BlockingState accountState;
 
     private final BlockingStateDao dao = new BlockingStateDao() {
-        @Override
-        public BlockingState getBlockingStateFor(final Blockable blockable, final InternalTenantContext context) {
-            if (blockable.getId() == account.getId()) {
-                return accountState;
-            } else if (blockable.getId() == subscription.getId()) {
-                return subscriptionState;
-            } else {
-                return bundleState;
-            }
-        }
+
 
         @Override
         public BlockingState getBlockingStateFor(final UUID blockableId, final InternalTenantContext context) {
@@ -75,10 +66,6 @@ public class TestBlockingChecker extends JunctionTestSuite {
             }
         }
 
-        @Override
-        public List<BlockingState> getBlockingHistoryFor(final Blockable overdueable, final InternalTenantContext context) {
-            throw new UnsupportedOperationException();
-        }
 
         @Override
         public List<BlockingState> getBlockingHistoryFor(final UUID overdueableId, final InternalTenantContext context) {
@@ -106,7 +93,7 @@ public class TestBlockingChecker extends JunctionTestSuite {
         Mockito.when(bundle.getAccountId()).thenReturn(accountId);
         final UUID bundleId = UUID.randomUUID();
         Mockito.when(bundle.getId()).thenReturn(bundleId);
-        Mockito.when(bundle.getKey()).thenReturn("key");
+        Mockito.when(bundle.getExternalKey()).thenReturn("key");
 
         subscription = Mockito.mock(Subscription.class);
         Mockito.when(subscription.getId()).thenReturn(UUID.randomUUID());
diff --git a/junction/src/test/java/com/ning/billing/junction/dao/TestBlockingDao.java b/junction/src/test/java/com/ning/billing/junction/dao/TestBlockingDao.java
index 2c1c4a2..5730a39 100644
--- a/junction/src/test/java/com/ning/billing/junction/dao/TestBlockingDao.java
+++ b/junction/src/test/java/com/ning/billing/junction/dao/TestBlockingDao.java
@@ -43,8 +43,6 @@ public class TestBlockingDao extends JunctionTestSuiteWithEmbeddedDB {
     @Inject
     private BlockingStateDao dao;
 
-    private final InternalTenantContext tenantContext = Mockito.mock(InternalTenantContext.class);
-    private final InternalCallContext context = Mockito.mock(InternalCallContext.class);
 
     @Test(groups = "slow")
     public void testDao() {
@@ -58,18 +56,17 @@ public class TestBlockingDao extends JunctionTestSuiteWithEmbeddedDB {
         final boolean blockBilling = false;
 
         final BlockingState state1 = new DefaultBlockingState(uuid, overdueStateName, Blockable.Type.SUBSCRIPTION_BUNDLE, service, blockChange, blockEntitlement, blockBilling);
-        dao.setBlockingState(state1, clock, context);
+        dao.setBlockingState(state1, clock, internalCallContext);
         clock.setDeltaFromReality(1000 * 3600 * 24);
 
         final String overdueStateName2 = "NoReallyThisCantGoOn";
         final BlockingState state2 = new DefaultBlockingState(uuid, overdueStateName2, Blockable.Type.SUBSCRIPTION_BUNDLE, service, blockChange, blockEntitlement, blockBilling);
-        dao.setBlockingState(state2, clock, context);
+        dao.setBlockingState(state2, clock, internalCallContext);
 
         final SubscriptionBundle bundle = Mockito.mock(SubscriptionBundle.class);
         Mockito.when(bundle.getId()).thenReturn(uuid);
 
-        Assert.assertEquals(dao.getBlockingStateFor(bundle, tenantContext).getStateName(), state2.getStateName());
-        Assert.assertEquals(dao.getBlockingStateFor(bundle.getId(), tenantContext).getStateName(), overdueStateName2);
+        Assert.assertEquals(dao.getBlockingStateFor(uuid, internalCallContext).getStateName(), state2.getStateName());
     }
 
     @Test(groups = "slow")
@@ -84,23 +81,18 @@ public class TestBlockingDao extends JunctionTestSuiteWithEmbeddedDB {
         final boolean blockBilling = false;
 
         final BlockingState state1 = new DefaultBlockingState(uuid, overdueStateName, Blockable.Type.SUBSCRIPTION_BUNDLE, service, blockChange, blockEntitlement, blockBilling);
-        dao.setBlockingState(state1, clock, context);
+        dao.setBlockingState(state1, clock, internalCallContext);
         clock.setDeltaFromReality(1000 * 3600 * 24);
 
         final String overdueStateName2 = "NoReallyThisCantGoOn";
         final BlockingState state2 = new DefaultBlockingState(uuid, overdueStateName2, Blockable.Type.SUBSCRIPTION_BUNDLE, service, blockChange, blockEntitlement, blockBilling);
-        dao.setBlockingState(state2, clock, context);
+        dao.setBlockingState(state2, clock, internalCallContext);
 
         final SubscriptionBundle bundle = Mockito.mock(SubscriptionBundle.class);
         Mockito.when(bundle.getId()).thenReturn(uuid);
 
-        final List<BlockingState> history1 = dao.getBlockingHistoryFor(bundle, tenantContext);
-        final List<BlockingState> history2 = dao.getBlockingHistoryFor(bundle.getId(), tenantContext);
-
-        Assert.assertEquals(history1.size(), 2);
-        Assert.assertEquals(history1.get(0).getStateName(), overdueStateName);
-        Assert.assertEquals(history1.get(1).getStateName(), overdueStateName2);
 
+        final List<BlockingState> history2 = dao.getBlockingHistoryFor(bundle.getId(), internalCallContext);
         Assert.assertEquals(history2.size(), 2);
         Assert.assertEquals(history2.get(0).getStateName(), overdueStateName);
         Assert.assertEquals(history2.get(1).getStateName(), overdueStateName2);
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java
index 3b628c6..2ab8a90 100644
--- a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java
@@ -276,10 +276,11 @@ public class TestBillingApi extends JunctionTestSuite {
 
         final Account account = createAccount(32);
 
-        final Map<String, Tag> tags = new HashMap<String, Tag>();
         final Tag aioTag = mock(Tag.class);
         when(aioTag.getTagDefinitionId()).thenReturn(ControlTagType.AUTO_INVOICING_OFF.getId());
-        tags.put(ControlTagType.AUTO_INVOICING_OFF.name(), aioTag);
+        final List<Tag> tags = new ArrayList<Tag>();
+        tags.add(aioTag);
+
         when(tagApi.getTags(account.getId(), ObjectType.ACCOUNT, internalCallContext)).thenReturn(tags);
         assertEquals(tagApi.getTags(account.getId(), ObjectType.ACCOUNT, internalCallContext), tags);
 
@@ -297,10 +298,10 @@ public class TestBillingApi extends JunctionTestSuite {
 
         final Account account = createAccount(32);
 
-        final Map<String, Tag> tags = new HashMap<String, Tag>();
+        final List<Tag> tags = new ArrayList<Tag>();
         final Tag aioTag = mock(Tag.class);
         when(aioTag.getTagDefinitionId()).thenReturn(ControlTagType.AUTO_INVOICING_OFF.getId());
-        tags.put(ControlTagType.AUTO_INVOICING_OFF.name(), aioTag);
+        tags.add(aioTag);
         when(tagApi.getTags(bunId, ObjectType.BUNDLE, internalCallContext)).thenReturn(tags);
 
         final BillingEventSet events = api.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext);
diff --git a/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java b/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java
index 6873c2e..a6621cd 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java
@@ -89,22 +89,27 @@ public class OverdueStateApplicator<T extends Blockable> {
         this.bus = bus;
     }
 
+
     public void apply(final OverdueState<T> firstOverdueState, final BillingState<T> billingState,
                       final T overdueable, final String previousOverdueStateName,
                       final OverdueState<T> nextOverdueState, final InternalCallContext context) throws OverdueException {
         try {
-            // We did not reach first state, we we need to check if there is any pending condition for which we will not receive
-            // any notifications.. (last two conditions are there for test purpose)
-            if (nextOverdueState.isClearState() && firstOverdueState != null && billingState != null) {
-                final LocalDate firstUnpaidInvoice = billingState.getDateOfEarliestUnpaidInvoice();
-                if (firstUnpaidInvoice != null) {
-                    final Period reevaluationInterval = firstOverdueState.getReevaluationInterval();
-                    createFutureNotification(overdueable, firstUnpaidInvoice.toDateTimeAtCurrentTime().plus(reevaluationInterval), context);
-                }
+
+            log.debug("OverdueStateApplicator <enter> : time = " + clock.getUTCNow() + ", previousState = " + previousOverdueStateName + ", nextState = " + nextOverdueState);
+
+            final boolean conditionForNextNotfication = !nextOverdueState.isClearState() ||
+                                                        // We did not reach the first state yet but we have an unpaid invoice
+                                                        (firstOverdueState != null && billingState != null && billingState.getDateOfEarliestUnpaidInvoice() != null);
+
+            if (conditionForNextNotfication) {
+                final Period reevaluationInterval = nextOverdueState.isClearState() ? firstOverdueState.getReevaluationInterval() : nextOverdueState.getReevaluationInterval();
+                createFutureNotification(overdueable, clock.getUTCNow().plus(reevaluationInterval), context);
+
+                log.debug("OverdueStateApplicator <notificationQ> : inserting notification for time = " + clock.getUTCNow().plus(reevaluationInterval));
             }
 
             if (previousOverdueStateName.equals(nextOverdueState.getName())) {
-                return; // That's it, we are done...
+                return;
             }
 
             storeNewState(overdueable, nextOverdueState, context);
@@ -113,11 +118,6 @@ public class OverdueStateApplicator<T extends Blockable> {
 
             sendEmailIfRequired(billingState, overdueable, nextOverdueState, context);
 
-            // Add entry in notification queue
-            final Period reevaluationInterval = nextOverdueState.getReevaluationInterval();
-            if (!nextOverdueState.isClearState()) {
-                createFutureNotification(overdueable, clock.getUTCNow().plus(reevaluationInterval), context);
-            }
         } catch (OverdueApiException e) {
             if (e.getCode() != ErrorCode.OVERDUE_NO_REEVALUATION_INTERVAL.getCode()) {
                 throw new OverdueException(e);
diff --git a/overdue/src/test/java/com/ning/billing/overdue/applicator/ApplicatorMockJunctionModule.java b/overdue/src/test/java/com/ning/billing/overdue/applicator/ApplicatorMockJunctionModule.java
index 35edf31..0c1ac40 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/applicator/ApplicatorMockJunctionModule.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/applicator/ApplicatorMockJunctionModule.java
@@ -45,6 +45,11 @@ public class ApplicatorMockJunctionModule extends MockJunctionModule {
             return new BlockingState() {
 
                 @Override
+                public UUID getBlockedId() {
+                    return overdueable.getId();
+                }
+
+                @Override
                 public String getStateName() {
                     return DefaultBlockingState.CLEAR_STATE_NAME;
                 }
@@ -86,6 +91,11 @@ public class ApplicatorMockJunctionModule extends MockJunctionModule {
                 }
 
                 @Override
+                public String getService() {
+                    return "whatever";
+                }
+
+                @Override
                 public UUID getId() {
                     return UUID.randomUUID();
                 }
diff --git a/overdue/src/test/java/com/ning/billing/overdue/config/TestCondition.java b/overdue/src/test/java/com/ning/billing/overdue/config/TestCondition.java
index f9d9583..5f3542b 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/config/TestCondition.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/config/TestCondition.java
@@ -28,10 +28,13 @@ import org.joda.time.LocalDate;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.ning.billing.ObjectType;
 import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.overdue.OverdueTestSuite;
 import com.ning.billing.overdue.config.api.BillingState;
 import com.ning.billing.overdue.config.api.PaymentResponse;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.config.catalog.XMLLoader;
 import com.ning.billing.util.tag.ControlTagType;
 import com.ning.billing.util.tag.DefaultControlTag;
@@ -43,6 +46,8 @@ public class TestCondition extends OverdueTestSuite {
     @XmlRootElement(name = "condition")
     private static class MockCondition extends DefaultCondition<Blockable> {}
 
+    private Clock clock = new ClockMock();
+
     @Test(groups = "fast")
     public void testNumberOfUnpaidInvoicesEqualsOrExceeds() throws Exception {
         final String xml =
@@ -147,16 +152,24 @@ public class TestCondition extends OverdueTestSuite {
 
         final LocalDate now = new LocalDate();
 
-        final BillingState<Blockable> state0 = new BillingState<Blockable>(new UUID(0L, 1L), 0, BigDecimal.ZERO, null,
-                                                                           DateTimeZone.UTC, unpaidInvoiceId, PaymentResponse.LOST_OR_STOLEN_CARD, new Tag[]{new DefaultControlTag(ControlTagType.AUTO_INVOICING_OFF), new DescriptiveTag(UUID.randomUUID())});
-        final BillingState<Blockable> state1 = new BillingState<Blockable>(new UUID(0L, 1L), 1, new BigDecimal("100.00"), now.minusDays(10),
-                                                                           DateTimeZone.UTC, unpaidInvoiceId, PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{new DefaultControlTag(ControlTagType.OVERDUE_ENFORCEMENT_OFF)});
-        final BillingState<Blockable> state2 = new BillingState<Blockable>(new UUID(0L, 1L), 1, new BigDecimal("200.00"), now.minusDays(20),
+        final ObjectType objectType = ObjectType.BUNDLE;
+
+        final UUID objectId = new UUID(0L, 1L);
+        final BillingState<Blockable> state0 = new BillingState<Blockable>(objectId, 0, BigDecimal.ZERO, null,
+                                                                           DateTimeZone.UTC, unpaidInvoiceId, PaymentResponse.LOST_OR_STOLEN_CARD,
+                                                                           new Tag[]{new DefaultControlTag( ControlTagType.AUTO_INVOICING_OFF, objectType, objectId, clock.getUTCNow()),
+                                                                                   new DescriptiveTag(UUID.randomUUID(), objectType, objectId, clock.getUTCNow())});
+
+        final BillingState<Blockable> state1 = new BillingState<Blockable>(objectId, 1, new BigDecimal("100.00"), now.minusDays(10),
+                                                                           DateTimeZone.UTC, unpaidInvoiceId, PaymentResponse.INSUFFICIENT_FUNDS,
+                                                                           new Tag[]{new DefaultControlTag(ControlTagType.OVERDUE_ENFORCEMENT_OFF, objectType, objectId, clock.getUTCNow())});
+
+        final BillingState<Blockable> state2 = new BillingState<Blockable>(objectId, 1, new BigDecimal("200.00"), now.minusDays(20),
                                                                            DateTimeZone.UTC, unpaidInvoiceId,
                                                                            PaymentResponse.DO_NOT_HONOR,
-                                                                           new Tag[]{new DefaultControlTag(ControlTagType.OVERDUE_ENFORCEMENT_OFF),
-                                                                                     new DefaultControlTag(ControlTagType.AUTO_INVOICING_OFF),
-                                                                                     new DescriptiveTag(UUID.randomUUID())});
+                                                                           new Tag[]{new DefaultControlTag(ControlTagType.OVERDUE_ENFORCEMENT_OFF, objectType, objectId, clock.getUTCNow()),
+                                                                                     new DefaultControlTag(ControlTagType.AUTO_INVOICING_OFF, objectType, objectId, clock.getUTCNow()),
+                                                                                     new DescriptiveTag(UUID.randomUUID(), objectType, objectId, clock.getUTCNow())});
 
         Assert.assertTrue(!c.evaluate(state0, now));
         Assert.assertTrue(c.evaluate(state1, now));
diff --git a/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueCheckNotifier.java b/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueCheckNotifier.java
index 6f9541b..d91d213 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueCheckNotifier.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueCheckNotifier.java
@@ -62,7 +62,7 @@ import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
-import com.ning.billing.util.customfield.dao.AuditedCustomFieldDao;
+import com.ning.billing.util.customfield.dao.DefaultCustomFieldDao;
 import com.ning.billing.util.customfield.dao.CustomFieldDao;
 import com.ning.billing.util.email.EmailModule;
 import com.ning.billing.util.email.templates.TemplateModule;
@@ -74,7 +74,7 @@ import com.ning.billing.util.notificationq.NotificationQueueService;
 import com.ning.billing.util.svcapi.account.AccountInternalApi;
 import com.ning.billing.util.svcapi.entitlement.EntitlementInternalApi;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
-import com.ning.billing.util.tag.dao.AuditedTagDao;
+import com.ning.billing.util.tag.dao.DefaultTagDao;
 import com.ning.billing.util.tag.dao.TagDao;
 
 import com.google.inject.Guice;
@@ -130,8 +130,8 @@ public class TestOverdueCheckNotifier extends OverdueTestSuiteWithEmbeddedDB {
                 bind(MysqlTestingHelper.class).toInstance(helper);
                 final IDBI dbi = helper.getDBI();
                 bind(IDBI.class).toInstance(dbi);
-                bind(TagDao.class).to(AuditedTagDao.class).asEagerSingleton();
-                bind(CustomFieldDao.class).to(AuditedCustomFieldDao.class).asEagerSingleton();
+                bind(TagDao.class).to(DefaultTagDao.class).asEagerSingleton();
+                bind(CustomFieldDao.class).to(DefaultCustomFieldDao.class).asEagerSingleton();
                 bind(GlobalLocker.class).to(MySqlGlobalLocker.class).asEagerSingleton();
                 install(new MockJunctionModule());
                 install(new EmailModule());
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPayment.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPayment.java
index cde2b82..690b321 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPayment.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPayment.java
@@ -175,7 +175,7 @@ public class DefaultPayment extends EntityBase implements Payment {
                 return new PaymentAttempt() {
                     @Override
                     public PaymentStatus getPaymentStatus() {
-                        return input.getPaymentStatus();
+                        return input.getProcessingStatus();
                     }
 
                     @Override
@@ -189,6 +189,16 @@ public class DefaultPayment extends EntityBase implements Payment {
                     }
 
                     @Override
+                    public DateTime getCreatedDate() {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    @Override
+                    public DateTime getUpdatedDate() {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    @Override
                     public String getGatewayErrorCode() {
                         return input.getGatewayErrorCode();
                     }
diff --git a/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
index 40bdb84..d3a06d3 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
@@ -310,6 +310,7 @@ public class PaymentProcessor extends ProcessorBase {
     }
 
     public void retryFailedPayment(final UUID paymentId, final InternalCallContext context) {
+        log.info("STEPH retrying failed payment " + paymentId + " time = " + clock.getUTCNow());
         retryFailedPaymentInternal(paymentId, context, PaymentStatus.PAYMENT_FAILURE);
     }
 
@@ -449,6 +450,7 @@ public class PaymentProcessor extends ProcessorBase {
                 break;
 
             case ERROR:
+                log.info("STEPH payment failure...");
                 allAttempts = paymentDao.getAttemptsForPayment(paymentInput.getId(), context);
                 // Schedule if non instant payment and max attempt for retry not reached yet
                 if (!isInstantPayment) {
@@ -504,7 +506,12 @@ public class PaymentProcessor extends ProcessorBase {
         final List<PaymentAttemptModelDao> allAttempts = paymentDao.getAttemptsForPayment(paymentId, context);
         final int retryAttempt = getNumberAttemptsInState(paymentId, allAttempts,
                 PaymentStatus.UNKNOWN, PaymentStatus.PAYMENT_FAILURE);
+
+
         final boolean isScheduledForRetry = failedPaymentRetryService.scheduleRetry(paymentId, retryAttempt);
+
+        log.info("STEPH scheduleRetryOnPaymentFailure id = " + paymentId + ", retryAttempt = " + retryAttempt + ", retry :" + isScheduledForRetry);
+
         return isScheduledForRetry ? PaymentStatus.PAYMENT_FAILURE : PaymentStatus.PAYMENT_FAILURE_ABORTED;
     }
 
@@ -516,7 +523,7 @@ public class PaymentProcessor extends ProcessorBase {
             @Override
             public boolean apply(final PaymentAttemptModelDao input) {
                 for (final PaymentStatus cur : statuses) {
-                    if (input.getPaymentStatus() == cur) {
+                    if (input.getProcessingStatus() == cur) {
                         return true;
                     }
                 }
diff --git a/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java b/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
index 8c73ea2..1282a03 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.payment.core;
 
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.Callable;
@@ -79,8 +80,8 @@ public abstract class ProcessorBase {
     }
 
     protected boolean isAccountAutoPayOff(final UUID accountId, final InternalTenantContext context) {
-        final Map<String, Tag> accountTags = tagInternalApi.getTags(accountId, ObjectType.ACCOUNT, context);
-        for (final Tag cur : accountTags.values()) {
+        final List<Tag> accountTags = tagInternalApi.getTags(accountId, ObjectType.ACCOUNT, context);
+        for (final Tag cur : accountTags) {
             if (ControlTagType.AUTO_PAY_OFF.getId().equals(cur.getTagDefinitionId())) {
                 return true;
             }
diff --git a/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
index 8509d69..679e695 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
@@ -16,8 +16,6 @@
 
 package com.ning.billing.payment.core;
 
-import static com.ning.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
-
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -66,6 +64,8 @@ import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.name.Named;
 
+import static com.ning.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
+
 public class RefundProcessor extends ProcessorBase {
 
     private static final Logger log = LoggerFactory.getLogger(RefundProcessor.class);
@@ -174,7 +174,7 @@ public class RefundProcessor extends ProcessorBase {
                                              paymentId, refundInfo.getAmount(), account.getCurrency(),
                                              isAdjusted, refundInfo.getCreatedDate());
                 } catch (PaymentPluginApiException e) {
-                    throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_REFUND, account.getId(), e.getMessage());
+                    throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_REFUND, account.getId(), e.getErrorMessage());
                 } catch (InvoiceApiException e) {
                     throw new PaymentApiException(e);
                 }
@@ -239,7 +239,7 @@ public class RefundProcessor extends ProcessorBase {
         }
         return new DefaultRefund(result.getId(), result.getCreatedDate(), result.getUpdatedDate(),
                                  result.getPaymentId(), result.getAmount(), result.getCurrency(),
-                                 result.isAdjsuted(), result.getCreatedDate());
+                                 result.isAdjusted(), result.getCreatedDate());
     }
 
     public List<Refund> getAccountRefunds(final Account account, final InternalTenantContext context)
@@ -268,7 +268,7 @@ public class RefundProcessor extends ProcessorBase {
             public Refund apply(final RefundModelDao cur) {
                 return new DefaultRefund(cur.getId(), cur.getCreatedDate(), cur.getUpdatedDate(),
                                          cur.getPaymentId(), cur.getAmount(), cur.getCurrency(),
-                                         cur.isAdjsuted(), cur.getCreatedDate());
+                                         cur.isAdjusted(), cur.getCreatedDate());
             }
         }));
     }
@@ -304,7 +304,7 @@ public class RefundProcessor extends ProcessorBase {
                     try {
                         for (final RefundModelDao cur : refundsToBeFixed) {
                             // TODO - we currently don't save the items to be adjusted. If we crash, they won't be adjusted...
-                            invoiceApi.createRefund(cur.getPaymentId(), cur.getAmount(), cur.isAdjsuted(), ImmutableMap.<UUID, BigDecimal>of(), cur.getId(), context);
+                            invoiceApi.createRefund(cur.getPaymentId(), cur.getAmount(), cur.isAdjusted(), ImmutableMap.<UUID, BigDecimal>of(), cur.getId(), context);
                             paymentDao.updateRefundStatus(cur.getId(), RefundStatus.COMPLETED, context);
                         }
                     } catch (InvoiceApiException e) {
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
new file mode 100644
index 0000000..381fd3b
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
@@ -0,0 +1,339 @@
+/*
+ * 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.payment.dao;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.skife.jdbi.v2.IDBI;
+
+import com.ning.billing.payment.api.PaymentStatus;
+import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
+import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.entity.EntityPersistenceException;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+
+
+public class DefaultPaymentDao implements PaymentDao {
+
+    private final EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao;
+
+    @Inject
+    public DefaultPaymentDao(final IDBI dbi) {
+        this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi);
+    }
+
+    @Override
+    public PaymentAttemptModelDao insertNewAttemptForPayment(final UUID paymentId, final PaymentAttemptModelDao attempt, final InternalCallContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<PaymentAttemptModelDao>() {
+            @Override
+            public PaymentAttemptModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final PaymentAttemptSqlDao transactional = entitySqlDaoWrapperFactory.become(PaymentAttemptSqlDao.class);
+                transactional.create(attempt, context);
+                final PaymentAttemptModelDao savedAttempt = transactional.getById(attempt.getId().toString(), context);
+
+                entitySqlDaoWrapperFactory.become(PaymentSqlDao.class).updatePaymentAmount(paymentId.toString(), savedAttempt.getRequestedAmount(), context);
+
+                return savedAttempt;
+            }
+        });
+    }
+
+    @Override
+    public PaymentModelDao insertPaymentWithAttempt(final PaymentModelDao payment, final PaymentAttemptModelDao attempt, final InternalCallContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<PaymentModelDao>() {
+
+            @Override
+            public PaymentModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final PaymentSqlDao transactional = entitySqlDaoWrapperFactory.become(PaymentSqlDao.class);
+                transactional.create(payment, context);
+
+
+                entitySqlDaoWrapperFactory.become(PaymentAttemptSqlDao.class).create(attempt, context);
+
+                return transactional.getById(payment.getId().toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public PaymentAttemptModelDao getPaymentAttempt(final UUID attemptId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<PaymentAttemptModelDao>() {
+            @Override
+            public PaymentAttemptModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(PaymentAttemptSqlDao.class).getById(attemptId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public void updateStatusForPaymentWithAttempt(final UUID paymentId,
+                                                  final PaymentStatus paymentStatus,
+                                                  final String gatewayErrorCode,
+                                                  final String gatewayErrorMsg,
+                                                  final String extFirstPaymentRefId,
+                                                  final String extSecondPaymentRefId,
+                                                  final UUID attemptId,
+                                                  final InternalCallContext context) {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                entitySqlDaoWrapperFactory.become(PaymentSqlDao.class).updatePaymentStatusAndExtRef(paymentId.toString(), paymentStatus.toString(), extFirstPaymentRefId, extSecondPaymentRefId, context);
+                entitySqlDaoWrapperFactory.become(PaymentAttemptSqlDao.class).updatePaymentAttemptStatus(attemptId.toString(), paymentStatus.toString(), gatewayErrorCode, gatewayErrorMsg, context);
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public PaymentMethodModelDao insertPaymentMethod(final PaymentMethodModelDao paymentMethod, final InternalCallContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<PaymentMethodModelDao>() {
+            @Override
+            public PaymentMethodModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return insertPaymentMethodInTransaction(entitySqlDaoWrapperFactory, paymentMethod, context);
+            }
+        });
+    }
+
+    private PaymentMethodModelDao insertPaymentMethodInTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final PaymentMethodModelDao paymentMethod, final InternalCallContext context)
+            throws EntityPersistenceException {
+        final PaymentMethodSqlDao transactional = entitySqlDaoWrapperFactory.become(PaymentMethodSqlDao.class);
+        transactional.create(paymentMethod, context);
+
+        return transactional.getById(paymentMethod.getId().toString(), context);
+    }
+
+    @Override
+    public List<PaymentMethodModelDao> refreshPaymentMethods(final UUID accountId, final List<PaymentMethodModelDao> paymentMethods, final InternalCallContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<PaymentMethodModelDao>>() {
+
+            @Override
+            public List<PaymentMethodModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final PaymentMethodSqlDao transactional = entitySqlDaoWrapperFactory.become(PaymentMethodSqlDao.class);
+                final List<PaymentMethodModelDao> existingPaymentMethods = transactional.getByAccountId(accountId.toString(), context);
+
+                final Set<String> externalPaymentIdProcessed = new HashSet<String>();
+                for (final PaymentMethodModelDao finalPaymentMethod : paymentMethods) {
+                    boolean isExistingPaymentMethod = false;
+
+                    for (final PaymentMethodModelDao existingPaymentMethod : existingPaymentMethods) {
+                        if (existingPaymentMethod.equals(finalPaymentMethod)) {
+                            // We already have it - nothing to do
+                            isExistingPaymentMethod = true;
+                            break;
+                        } else if (existingPaymentMethod.equalsButActive(finalPaymentMethod)) {
+                            // We already have it but its status has changed - update it accordingly
+                            // Note - in the remote system, the payment method will always be active
+                            undeletedPaymentMethodInTransaction(entitySqlDaoWrapperFactory, existingPaymentMethod.getId(), context);
+                            isExistingPaymentMethod = true;
+                            break;
+                        }
+                        // Otherwise, we don't have it
+                    }
+
+                    if (!isExistingPaymentMethod) {
+                        insertPaymentMethodInTransaction(entitySqlDaoWrapperFactory, finalPaymentMethod, context);
+                    }
+
+                    externalPaymentIdProcessed.add(finalPaymentMethod.getExternalId());
+                }
+
+                // Finally, mark as deleted the ones that don't exist in the specified list (remote system)
+                for (final PaymentMethodModelDao existingPaymentMethod : existingPaymentMethods) {
+                    if (!externalPaymentIdProcessed.contains(existingPaymentMethod.getExternalId())) {
+                        deletedPaymentMethodInTransaction(entitySqlDaoWrapperFactory, existingPaymentMethod.getId(), context);
+                    }
+                }
+                return transactional.getByAccountId(accountId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public RefundModelDao insertRefund(final RefundModelDao refundInfo, final InternalCallContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<RefundModelDao>() {
+
+            @Override
+            public RefundModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final RefundSqlDao transactional = entitySqlDaoWrapperFactory.become(RefundSqlDao.class);
+                transactional.create(refundInfo, context);
+                return transactional.getById(refundInfo.getId().toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public void updateRefundStatus(final UUID refundId, final RefundStatus refundStatus, final InternalCallContext context) {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                entitySqlDaoWrapperFactory.become(RefundSqlDao.class).updateStatus(refundId.toString(), refundStatus.toString(), context);
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public RefundModelDao getRefund(final UUID refundId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<RefundModelDao>() {
+            @Override
+            public RefundModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(RefundSqlDao.class).getById(refundId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public List<RefundModelDao> getRefundsForPayment(final UUID paymentId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<RefundModelDao>>() {
+            @Override
+            public List<RefundModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(RefundSqlDao.class).getRefundsForPayment(paymentId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public List<RefundModelDao> getRefundsForAccount(final UUID accountId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<RefundModelDao>>() {
+            @Override
+            public List<RefundModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(RefundSqlDao.class).getRefundsForAccount(accountId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public PaymentMethodModelDao getPaymentMethod(final UUID paymentMethodId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<PaymentMethodModelDao>() {
+            @Override
+            public PaymentMethodModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(PaymentMethodSqlDao.class).getById(paymentMethodId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public PaymentMethodModelDao getPaymentMethodIncludedDeleted(final UUID paymentMethodId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<PaymentMethodModelDao>() {
+            @Override
+            public PaymentMethodModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(PaymentMethodSqlDao.class).getPaymentMethodIncludedDelete(paymentMethodId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public List<PaymentMethodModelDao> getPaymentMethods(final UUID accountId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<PaymentMethodModelDao>>() {
+            @Override
+            public List<PaymentMethodModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(PaymentMethodSqlDao.class).getByAccountId(accountId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public void deletedPaymentMethod(final UUID paymentMethodId, final InternalCallContext context) {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                deletedPaymentMethodInTransaction(entitySqlDaoWrapperFactory, paymentMethodId, context);
+                return null;
+            }
+        });
+    }
+
+    private void deletedPaymentMethodInTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID paymentMethodId, final InternalCallContext context) {
+        entitySqlDaoWrapperFactory.become(PaymentMethodSqlDao.class).markPaymentMethodAsDeleted(paymentMethodId.toString(), context);
+    }
+
+    @Override
+    public void undeletedPaymentMethod(final UUID paymentMethodId, final InternalCallContext context) {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                undeletedPaymentMethodInTransaction(entitySqlDaoWrapperFactory, paymentMethodId, context);
+                return null;
+            }
+        });
+    }
+
+    private void undeletedPaymentMethodInTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID paymentMethodId, final InternalCallContext context) {
+        final PaymentMethodSqlDao paymentMethodSqlDao = entitySqlDaoWrapperFactory.become(PaymentMethodSqlDao.class);
+        paymentMethodSqlDao.unmarkPaymentMethodAsDeleted(paymentMethodId.toString(), context);
+    }
+
+    @Override
+    public List<PaymentModelDao> getPaymentsForInvoice(final UUID invoiceId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<PaymentModelDao>>() {
+            @Override
+            public List<PaymentModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(PaymentSqlDao.class).getPaymentsForInvoice(invoiceId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public PaymentModelDao getLastPaymentForPaymentMethod(final UUID accountId, final UUID paymentMethodId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<PaymentModelDao>() {
+            @Override
+            public PaymentModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(PaymentSqlDao.class).getLastPaymentForAccountAndPaymentMethod(accountId.toString(), paymentMethodId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public PaymentModelDao getPayment(final UUID paymentId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<PaymentModelDao>() {
+            @Override
+            public PaymentModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(PaymentSqlDao.class).getById(paymentId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public List<PaymentModelDao> getPaymentsForAccount(final UUID accountId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<PaymentModelDao>>() {
+            @Override
+            public List<PaymentModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(PaymentSqlDao.class).getPaymentsForAccount(accountId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public List<PaymentAttemptModelDao> getAttemptsForPayment(final UUID paymentId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<PaymentAttemptModelDao>>() {
+            @Override
+            public List<PaymentAttemptModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(PaymentAttemptSqlDao.class).getByPaymentId(paymentId.toString(), context);
+            }
+        });
+    }
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptModelDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptModelDao.java
index fb83540..5defd47 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptModelDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptModelDao.java
@@ -23,19 +23,24 @@ import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
 
+import com.ning.billing.payment.api.Payment.PaymentAttempt;
 import com.ning.billing.payment.api.PaymentStatus;
+import com.ning.billing.util.dao.TableName;
 import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.dao.EntityModelDao;
 
-public class PaymentAttemptModelDao extends EntityBase {
+public class PaymentAttemptModelDao extends EntityBase implements EntityModelDao<PaymentAttempt> {
 
-    private final UUID accountId;
-    private final UUID invoiceId;
-    private final UUID paymentId;
-    private final PaymentStatus processingStatus;
-    private final DateTime effectiveDate;
-    private final String gatewayErrorCode;
-    private final String gatewayErrorMsg;
-    private final BigDecimal requestedAmount;
+    private UUID accountId;
+    private UUID invoiceId;
+    private UUID paymentId;
+    private PaymentStatus processingStatus;
+    private DateTime effectiveDate;
+    private String gatewayErrorCode;
+    private String gatewayErrorMsg;
+    private BigDecimal requestedAmount;
+
+    public PaymentAttemptModelDao() { /* For the DAO mapper */ }
 
     public PaymentAttemptModelDao(final UUID id, @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate,
                                   final UUID accountId, final UUID invoiceId,
@@ -77,7 +82,7 @@ public class PaymentAttemptModelDao extends EntityBase {
         return paymentId;
     }
 
-    public PaymentStatus getPaymentStatus() {
+    public PaymentStatus getProcessingStatus() {
         return processingStatus;
     }
 
@@ -96,4 +101,81 @@ public class PaymentAttemptModelDao extends EntityBase {
     public BigDecimal getRequestedAmount() {
         return requestedAmount;
     }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("PaymentAttemptModelDao");
+        sb.append("{accountId=").append(accountId);
+        sb.append(", invoiceId=").append(invoiceId);
+        sb.append(", paymentId=").append(paymentId);
+        sb.append(", processingStatus=").append(processingStatus);
+        sb.append(", effectiveDate=").append(effectiveDate);
+        sb.append(", gatewayErrorCode='").append(gatewayErrorCode).append('\'');
+        sb.append(", gatewayErrorMsg='").append(gatewayErrorMsg).append('\'');
+        sb.append(", requestedAmount=").append(requestedAmount);
+        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 PaymentAttemptModelDao that = (PaymentAttemptModelDao) o;
+
+        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
+            return false;
+        }
+        if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
+            return false;
+        }
+        if (gatewayErrorCode != null ? !gatewayErrorCode.equals(that.gatewayErrorCode) : that.gatewayErrorCode != null) {
+            return false;
+        }
+        if (gatewayErrorMsg != null ? !gatewayErrorMsg.equals(that.gatewayErrorMsg) : that.gatewayErrorMsg != null) {
+            return false;
+        }
+        if (invoiceId != null ? !invoiceId.equals(that.invoiceId) : that.invoiceId != null) {
+            return false;
+        }
+        if (paymentId != null ? !paymentId.equals(that.paymentId) : that.paymentId != null) {
+            return false;
+        }
+        if (processingStatus != that.processingStatus) {
+            return false;
+        }
+        if (requestedAmount != null ? !requestedAmount.equals(that.requestedAmount) : that.requestedAmount != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (accountId != null ? accountId.hashCode() : 0);
+        result = 31 * result + (invoiceId != null ? invoiceId.hashCode() : 0);
+        result = 31 * result + (paymentId != null ? paymentId.hashCode() : 0);
+        result = 31 * result + (processingStatus != null ? processingStatus.hashCode() : 0);
+        result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
+        result = 31 * result + (gatewayErrorCode != null ? gatewayErrorCode.hashCode() : 0);
+        result = 31 * result + (gatewayErrorMsg != null ? gatewayErrorMsg.hashCode() : 0);
+        result = 31 * result + (requestedAmount != null ? requestedAmount.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.PAYMENT_ATTEMPTS;
+    }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptSqlDao.java
index 84d5ed5..f9791c8 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptSqlDao.java
@@ -16,95 +16,33 @@
 
 package com.ning.billing.payment.dao;
 
-import java.math.BigDecimal;
-import java.sql.ResultSet;
-import java.sql.SQLException;
 import java.util.List;
-import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.skife.jdbi.v2.SQLStatement;
-import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 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.mixins.CloseMe;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
-import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
-import com.ning.billing.payment.api.PaymentStatus;
+import com.ning.billing.payment.api.Payment.PaymentAttempt;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.BinderBase;
-import com.ning.billing.util.dao.EntityHistory;
-import com.ning.billing.util.dao.MapperBase;
-import com.ning.billing.util.entity.dao.UpdatableEntitySqlDao;
+import com.ning.billing.util.entity.dao.Audited;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 
-@ExternalizedSqlViaStringTemplate3()
-@RegisterMapper(PaymentAttemptSqlDao.PaymentAttemptModelDaoMapper.class)
-public interface PaymentAttemptSqlDao extends Transactional<PaymentAttemptSqlDao>, UpdatableEntitySqlDao<PaymentAttemptModelDao>, Transmogrifier, CloseMe {
-
-    @SqlUpdate
-    void insertPaymentAttempt(@Bind(binder = PaymentAttemptModelDaoBinder.class) final PaymentAttemptModelDao attempt,
-                              @InternalTenantContextBinder final InternalCallContext context);
+@EntitySqlDaoStringTemplate
+public interface PaymentAttemptSqlDao extends EntitySqlDao<PaymentAttemptModelDao, PaymentAttempt> {
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     void updatePaymentAttemptStatus(@Bind("id") final String attemptId,
                                     @Bind("processingStatus") final String processingStatus,
                                     @Bind("gatewayErrorCode") final String gatewayErrorCode,
                                     @Bind("gatewayErrorMsg") final String gatewayErrorMsg,
-                                    @InternalTenantContextBinder final InternalCallContext context);
-
-    @SqlQuery
-    PaymentAttemptModelDao getPaymentAttempt(@Bind("id") final String attemptId,
-                                             @InternalTenantContextBinder final InternalTenantContext context);
+                                    @BindBean final InternalCallContext context);
 
     @SqlQuery
-    List<PaymentAttemptModelDao> getPaymentAttempts(@Bind("paymentId") final String paymentId,
-                                                    @InternalTenantContextBinder final InternalTenantContext context);
-
-    @Override
-    @SqlUpdate
-    void insertHistoryFromTransaction(@PaymentAttemptHistoryBinder final EntityHistory<PaymentAttemptModelDao> payment,
-                                      @InternalTenantContextBinder final InternalCallContext context);
-
-    public static final class PaymentAttemptModelDaoBinder extends BinderBase implements Binder<Bind, PaymentAttemptModelDao> {
-
-        @Override
-        public void bind(@SuppressWarnings("rawtypes") final SQLStatement stmt, final Bind bind, final PaymentAttemptModelDao attempt) {
-            stmt.bind("id", attempt.getId().toString());
-            stmt.bind("paymentId", attempt.getPaymentId().toString());
-            stmt.bind("processingStatus", attempt.getPaymentStatus().toString());
-            stmt.bind("gatewayErrorCode", attempt.getGatewayErrorCode());
-            stmt.bind("gatewayErrorMsg", attempt.getGatewayErrorMsg());
-            stmt.bind("requestedAmount", attempt.getRequestedAmount());
-        }
-    }
-
-    public static class PaymentAttemptModelDaoMapper extends MapperBase implements ResultSetMapper<PaymentAttemptModelDao> {
-
-        @Override
-        public PaymentAttemptModelDao map(final int index, final ResultSet rs, final StatementContext ctx)
-                throws SQLException {
-            final UUID id = getUUID(rs, "id");
-            final UUID accountId = getUUID(rs, "account_id");
-            final UUID invoiceId = getUUID(rs, "invoice_id");
-            final UUID paymentId = getUUID(rs, "payment_id");
-            final DateTime effectiveDate = getDateTime(rs, "effective_date");
-            final PaymentStatus processingStatus = PaymentStatus.valueOf(rs.getString("processing_status"));
-            final String gatewayErrorCode = rs.getString("gateway_error_code");
-            final String gatewayErrorMsg = rs.getString("gateway_error_msg");
-            final BigDecimal requestedAmount = rs.getBigDecimal("requested_amount");
-            final DateTime createdDate = getDateTime(rs, "created_date");
-            final DateTime updatedDate = getDateTime(rs, "updated_date");
-
-            return new PaymentAttemptModelDao(id, createdDate, updatedDate, accountId, invoiceId, paymentId, processingStatus,
-                                              effectiveDate, requestedAmount, gatewayErrorCode, gatewayErrorMsg);
-        }
-    }
+    List<PaymentAttemptModelDao> getByPaymentId(@Bind("paymentId") final String paymentId,
+                                                @BindBean final InternalTenantContext context);
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodModelDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodModelDao.java
index 38d144b..9f3fc4f 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodModelDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodModelDao.java
@@ -22,14 +22,19 @@ import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
 
+import com.ning.billing.payment.api.PaymentMethod;
+import com.ning.billing.util.dao.TableName;
 import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.dao.EntityModelDao;
 
-public class PaymentMethodModelDao extends EntityBase {
+public class PaymentMethodModelDao extends EntityBase implements EntityModelDao<PaymentMethod> {
 
-    private final UUID accountId;
-    private final String pluginName;
-    private final Boolean isActive;
-    private final String externalId;
+    private UUID accountId;
+    private String pluginName;
+    private Boolean isActive;
+    private String externalId;
+
+    public PaymentMethodModelDao() { /* For the DAO mapper */ }
 
     public PaymentMethodModelDao(final UUID id, @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate,
                                  final UUID accountId, final String pluginName,
@@ -49,6 +54,11 @@ public class PaymentMethodModelDao extends EntityBase {
         return pluginName;
     }
 
+    // TODO  Required for making the BindBeanFactory with Introspector work
+    public Boolean getIsActive() {
+        return isActive;
+    }
+
     public Boolean isActive() {
         return isActive;
     }
@@ -113,4 +123,9 @@ public class PaymentMethodModelDao extends EntityBase {
         result = 31 * result + (externalId != null ? externalId.hashCode() : 0);
         return result;
     }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.PAYMENT_METHODS;
+    }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodSqlDao.java
index 8ccadca..2c04db0 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodSqlDao.java
@@ -16,91 +16,38 @@
 
 package com.ning.billing.payment.dao;
 
-import java.sql.ResultSet;
-import java.sql.SQLException;
 import java.util.List;
-import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.skife.jdbi.v2.SQLStatement;
-import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 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.mixins.CloseMe;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
-import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
+import com.ning.billing.payment.api.PaymentMethod;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.BinderBase;
-import com.ning.billing.util.dao.EntityHistory;
-import com.ning.billing.util.dao.MapperBase;
-import com.ning.billing.util.entity.dao.UpdatableEntitySqlDao;
+import com.ning.billing.util.entity.dao.Audited;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 
-@ExternalizedSqlViaStringTemplate3()
-@RegisterMapper(PaymentMethodSqlDao.PaymentMethodDaoMapper.class)
-public interface PaymentMethodSqlDao extends Transactional<PaymentMethodSqlDao>, UpdatableEntitySqlDao<PaymentMethodModelDao>, Transmogrifier, CloseMe {
-
-    @SqlUpdate
-    void insertPaymentMethod(@Bind(binder = PaymentMethodModelDaoBinder.class) final PaymentMethodModelDao paymentMethod,
-                             @InternalTenantContextBinder final InternalCallContext context);
+@EntitySqlDaoStringTemplate
+public interface PaymentMethodSqlDao extends EntitySqlDao<PaymentMethodModelDao, PaymentMethod> {
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     void markPaymentMethodAsDeleted(@Bind("id") final String paymentMethodId,
-                                    @InternalTenantContextBinder final InternalCallContext context);
+                                    @BindBean final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     void unmarkPaymentMethodAsDeleted(@Bind("id") final String paymentMethodId,
-                                      @InternalTenantContextBinder final InternalCallContext context);
-
-    @SqlQuery
-    PaymentMethodModelDao getPaymentMethod(@Bind("id") final String paymentMethodId,
-                                           @InternalTenantContextBinder final InternalTenantContext context);
+                                      @BindBean final InternalCallContext context);
 
     @SqlQuery
     PaymentMethodModelDao getPaymentMethodIncludedDelete(@Bind("id") final String paymentMethodId,
-                                                         @InternalTenantContextBinder final InternalTenantContext context);
+                                                         @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    List<PaymentMethodModelDao> getPaymentMethods(@Bind("accountId") final String accountId,
-                                                  @InternalTenantContextBinder final InternalTenantContext context);
-
-    @Override
-    @SqlUpdate
-    public void insertHistoryFromTransaction(@PaymentMethodHistoryBinder final EntityHistory<PaymentMethodModelDao> payment,
-                                             @InternalTenantContextBinder final InternalCallContext context);
-
-    public static final class PaymentMethodModelDaoBinder extends BinderBase implements Binder<Bind, PaymentMethodModelDao> {
-
-        @Override
-        public void bind(@SuppressWarnings("rawtypes") final SQLStatement stmt, final Bind bind, final PaymentMethodModelDao method) {
-            stmt.bind("id", method.getId().toString());
-            stmt.bind("accountId", method.getAccountId().toString());
-            stmt.bind("pluginName", method.getPluginName());
-            stmt.bind("isActive", method.isActive());
-            stmt.bind("externalId", method.getExternalId());
-        }
-    }
-
-    public static class PaymentMethodDaoMapper extends MapperBase implements ResultSetMapper<PaymentMethodModelDao> {
-
-        @Override
-        public PaymentMethodModelDao map(final int index, final ResultSet rs, final StatementContext ctx)
-                throws SQLException {
-            final UUID id = getUUID(rs, "id");
-            final UUID accountId = getUUID(rs, "account_id");
-            final String pluginName = rs.getString("plugin_name");
-            final Boolean isActive = rs.getBoolean("is_active");
-            final String externalId = rs.getString("external_id");
-            final DateTime createdDate = getDateTime(rs, "created_date");
-            final DateTime updatedDate = getDateTime(rs, "updated_date");
-            return new PaymentMethodModelDao(id, createdDate, updatedDate, accountId, pluginName, isActive, externalId);
-        }
-    }
+    List<PaymentMethodModelDao> getByAccountId(@Bind("accountId") final String accountId, @BindBean final InternalTenantContext context);
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentModelDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentModelDao.java
index a2af3dd..ab6d409 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentModelDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentModelDao.java
@@ -24,23 +24,28 @@ import javax.annotation.Nullable;
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.payment.api.Payment;
 import com.ning.billing.payment.api.PaymentStatus;
+import com.ning.billing.util.dao.TableName;
 import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.dao.EntityModelDao;
 
-public class PaymentModelDao extends EntityBase {
+public class PaymentModelDao extends EntityBase implements EntityModelDao<Payment> {
 
     public static final Integer INVALID_PAYMENT_NUMBER = new Integer(-13);
 
-    private final UUID accountId;
-    private final UUID invoiceId;
-    private final UUID paymentMethodId;
-    private final BigDecimal amount;
-    private final Currency currency;
-    private final DateTime effectiveDate;
-    private final Integer paymentNumber;
-    private final PaymentStatus paymentStatus;
-    private final String extFirstPaymentRefId;
-    private final String extSecondPaymentRefId;
+    private UUID accountId;
+    private UUID invoiceId;
+    private UUID paymentMethodId;
+    private BigDecimal amount;
+    private Currency currency;
+    private DateTime effectiveDate;
+    private Integer paymentNumber;
+    private PaymentStatus paymentStatus;
+    private String extFirstPaymentRefId;
+    private String extSecondPaymentRefId;
+
+    public PaymentModelDao() { /* For the DAO mapper */ }
 
     public PaymentModelDao(final UUID id, @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate, final UUID accountId,
                            final UUID invoiceId, final UUID paymentMethodId,
@@ -113,4 +118,91 @@ public class PaymentModelDao extends EntityBase {
     public String getExtSecondPaymentRefId() {
         return extSecondPaymentRefId;
     }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("PaymentModelDao");
+        sb.append("{accountId=").append(accountId);
+        sb.append(", invoiceId=").append(invoiceId);
+        sb.append(", paymentMethodId=").append(paymentMethodId);
+        sb.append(", amount=").append(amount);
+        sb.append(", currency=").append(currency);
+        sb.append(", effectiveDate=").append(effectiveDate);
+        sb.append(", paymentNumber=").append(paymentNumber);
+        sb.append(", paymentStatus=").append(paymentStatus);
+        sb.append(", extFirstPaymentRefId='").append(extFirstPaymentRefId).append('\'');
+        sb.append(", extSecondPaymentRefId='").append(extSecondPaymentRefId).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 PaymentModelDao that = (PaymentModelDao) o;
+
+        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
+            return false;
+        }
+        if (amount != null ? !amount.equals(that.amount) : that.amount != null) {
+            return false;
+        }
+        if (currency != that.currency) {
+            return false;
+        }
+        if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
+            return false;
+        }
+        if (extFirstPaymentRefId != null ? !extFirstPaymentRefId.equals(that.extFirstPaymentRefId) : that.extFirstPaymentRefId != null) {
+            return false;
+        }
+        if (extSecondPaymentRefId != null ? !extSecondPaymentRefId.equals(that.extSecondPaymentRefId) : that.extSecondPaymentRefId != null) {
+            return false;
+        }
+        if (invoiceId != null ? !invoiceId.equals(that.invoiceId) : that.invoiceId != null) {
+            return false;
+        }
+        if (paymentMethodId != null ? !paymentMethodId.equals(that.paymentMethodId) : that.paymentMethodId != null) {
+            return false;
+        }
+        if (paymentNumber != null ? !paymentNumber.equals(that.paymentNumber) : that.paymentNumber != null) {
+            return false;
+        }
+        if (paymentStatus != that.paymentStatus) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (accountId != null ? accountId.hashCode() : 0);
+        result = 31 * result + (invoiceId != null ? invoiceId.hashCode() : 0);
+        result = 31 * result + (paymentMethodId != null ? paymentMethodId.hashCode() : 0);
+        result = 31 * result + (amount != null ? amount.hashCode() : 0);
+        result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
+        result = 31 * result + (paymentNumber != null ? paymentNumber.hashCode() : 0);
+        result = 31 * result + (paymentStatus != null ? paymentStatus.hashCode() : 0);
+        result = 31 * result + (extFirstPaymentRefId != null ? extFirstPaymentRefId.hashCode() : 0);
+        result = 31 * result + (extSecondPaymentRefId != null ? extSecondPaymentRefId.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.PAYMENTS;
+    }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
index cd19c70..f347534 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
@@ -17,115 +17,49 @@
 package com.ning.billing.payment.dao;
 
 import java.math.BigDecimal;
-import java.sql.ResultSet;
-import java.sql.SQLException;
 import java.util.List;
-import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.skife.jdbi.v2.SQLStatement;
-import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 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.mixins.CloseMe;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
-import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
-import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.payment.api.PaymentStatus;
+import com.ning.billing.payment.api.Payment;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.BinderBase;
-import com.ning.billing.util.dao.EntityHistory;
-import com.ning.billing.util.dao.MapperBase;
-import com.ning.billing.util.entity.dao.UpdatableEntitySqlDao;
+import com.ning.billing.util.entity.dao.Audited;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 
-@ExternalizedSqlViaStringTemplate3()
-@RegisterMapper(PaymentSqlDao.PaymentModelDaoMapper.class)
-public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, UpdatableEntitySqlDao<PaymentModelDao>, Transmogrifier, CloseMe {
-
-    @SqlUpdate
-    void insertPayment(@Bind(binder = PaymentModelDaoBinder.class) final PaymentModelDao paymentInfo,
-                       @InternalTenantContextBinder final InternalCallContext context);
+@EntitySqlDaoStringTemplate
+public interface PaymentSqlDao extends EntitySqlDao<PaymentModelDao, Payment> {
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     void updatePaymentStatusAndExtRef(@Bind("id") final String paymentId,
                                       @Bind("paymentStatus") final String paymentStatus,
                                       @Bind("extFirstPaymentRefId") final String extFirstPaymentRefId,
                                       @Bind("extSecondPaymentRefId") final String extSecondPaymentRefId,
-                                      @InternalTenantContextBinder final InternalCallContext context);
+                                      @BindBean final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     void updatePaymentAmount(@Bind("id") final String paymentId,
                              @Bind("amount") final BigDecimal amount,
-                             @InternalTenantContextBinder final InternalCallContext context);
-
-    @SqlQuery
-    PaymentModelDao getPayment(@Bind("id") final String paymentId,
-                               @InternalTenantContextBinder final InternalTenantContext context);
+                             @BindBean final InternalCallContext context);
 
     @SqlQuery
     PaymentModelDao getLastPaymentForAccountAndPaymentMethod(@Bind("accountId") final String accountId,
                                                              @Bind("paymentMethodId") final String paymentMethodId,
-                                                             @InternalTenantContextBinder final InternalTenantContext context);
+                                                             @BindBean final InternalTenantContext context);
 
     @SqlQuery
     List<PaymentModelDao> getPaymentsForInvoice(@Bind("invoiceId") final String invoiceId,
-                                                @InternalTenantContextBinder final InternalTenantContext context);
+                                                @BindBean final InternalTenantContext context);
 
     @SqlQuery
     List<PaymentModelDao> getPaymentsForAccount(@Bind("accountId") final String accountId,
-                                                @InternalTenantContextBinder final InternalTenantContext context);
-
-    @Override
-    @SqlUpdate
-    void insertHistoryFromTransaction(@PaymentHistoryBinder final EntityHistory<PaymentModelDao> payment,
-                                      @InternalTenantContextBinder final InternalCallContext context);
-
-    public static final class PaymentModelDaoBinder extends BinderBase implements Binder<Bind, PaymentModelDao> {
-
-        @Override
-        public void bind(@SuppressWarnings("rawtypes") final SQLStatement stmt, final Bind bind, final PaymentModelDao payment) {
-            stmt.bind("id", payment.getId().toString());
-            stmt.bind("accountId", payment.getAccountId().toString());
-            stmt.bind("invoiceId", payment.getInvoiceId().toString());
-            stmt.bind("paymentMethodId", payment.getPaymentMethodId().toString());
-            stmt.bind("amount", payment.getAmount());
-            stmt.bind("currency", payment.getCurrency().toString());
-            stmt.bind("effectiveDate", getDate(payment.getEffectiveDate()));
-            stmt.bind("paymentStatus", payment.getPaymentStatus().toString());
-            stmt.bind("extFirstPaymentRefId", payment.getExtFirstPaymentRefId());
-            stmt.bind("extSecondPaymentRefId", payment.getExtSecondPaymentRefId());
-        }
-    }
-
-    public static class PaymentModelDaoMapper extends MapperBase implements ResultSetMapper<PaymentModelDao> {
-
-        @Override
-        public PaymentModelDao map(final int index, final ResultSet rs, final StatementContext ctx)
-                throws SQLException {
-            final UUID id = getUUID(rs, "id");
-            final UUID accountId = getUUID(rs, "account_id");
-            final UUID invoiceId = getUUID(rs, "invoice_id");
-            final UUID paymentMethodId = getUUID(rs, "payment_method_id");
-            final Integer paymentNumber = rs.getInt("payment_number");
-            final BigDecimal amount = rs.getBigDecimal("amount");
-            final DateTime effectiveDate = getDateTime(rs, "effective_date");
-            final Currency currency = Currency.valueOf(rs.getString("currency"));
-            final PaymentStatus paymentStatus = PaymentStatus.valueOf(rs.getString("payment_status"));
-            final String extFirstPaymentRefId = rs.getString("ext_first_payment_ref_id");
-            final String extSecondPaymentRefId = rs.getString("ext_second_payment_ref_id");
-            final DateTime createdDate = getDateTime(rs, "created_date");
-            final DateTime updatedDate = getDateTime(rs, "updated_date");
-            return new PaymentModelDao(id, createdDate, updatedDate, accountId, invoiceId, paymentMethodId, paymentNumber,
-                                       amount, currency, paymentStatus, effectiveDate, extFirstPaymentRefId, extSecondPaymentRefId);
-        }
-    }
+                                                @BindBean final InternalTenantContext context);
 }
 
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/RefundModelDao.java b/payment/src/main/java/com/ning/billing/payment/dao/RefundModelDao.java
index 5c5c4a1..22c90eb 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/RefundModelDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/RefundModelDao.java
@@ -24,16 +24,21 @@ import javax.annotation.Nullable;
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.payment.api.Refund;
+import com.ning.billing.util.dao.TableName;
 import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.dao.EntityModelDao;
 
-public class RefundModelDao extends EntityBase {
+public class RefundModelDao extends EntityBase implements EntityModelDao<Refund> {
 
-    private final UUID accountId;
-    private final UUID paymentId;
-    private final BigDecimal amount;
-    private final Currency currency;
-    private final boolean isAdjusted;
-    private final RefundStatus refundStatus;
+    private UUID accountId;
+    private UUID paymentId;
+    private BigDecimal amount;
+    private Currency currency;
+    private boolean isAdjusted;
+    private RefundStatus refundStatus;
+
+    public RefundModelDao() { /* For the DAO mapper */ }
 
     public RefundModelDao(final UUID accountId, final UUID paymentId, final BigDecimal amount,
                           final Currency currency, final boolean isAdjusted) {
@@ -72,7 +77,13 @@ public class RefundModelDao extends EntityBase {
         return refundStatus;
     }
 
-    public boolean isAdjsuted() {
+    // TODO Required for making the BindBeanFactory with Introspector work
+    // see Introspector line 571; they look at public method.
+    public boolean getIsAdjusted() {
+        return isAdjusted;
+    }
+
+    public boolean isAdjusted() {
         return isAdjusted;
     }
 
@@ -149,4 +160,9 @@ public class RefundModelDao extends EntityBase {
         result = 31 * result + (updatedDate != null ? updatedDate.hashCode() : 0);
         return result;
     }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.REFUNDS;
+    }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/RefundSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/RefundSqlDao.java
index 2d21d2e..2936a80 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/RefundSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/RefundSqlDao.java
@@ -16,96 +16,35 @@
 
 package com.ning.billing.payment.dao;
 
-import java.math.BigDecimal;
-import java.sql.ResultSet;
-import java.sql.SQLException;
 import java.util.List;
-import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.skife.jdbi.v2.SQLStatement;
-import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 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.mixins.CloseMe;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
-import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
-import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
+import com.ning.billing.payment.api.Refund;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.BinderBase;
-import com.ning.billing.util.dao.EntityHistory;
-import com.ning.billing.util.dao.MapperBase;
-import com.ning.billing.util.entity.dao.UpdatableEntitySqlDao;
+import com.ning.billing.util.entity.dao.Audited;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 
-@ExternalizedSqlViaStringTemplate3()
-@RegisterMapper(RefundSqlDao.RefundModelDaoMapper.class)
-public interface RefundSqlDao extends Transactional<RefundSqlDao>, UpdatableEntitySqlDao<RefundModelDao>, Transmogrifier, CloseMe {
-
-    @SqlUpdate
-    void insertRefund(@Bind(binder = RefundModelDaoBinder.class) final RefundModelDao refundInfo,
-                      @InternalTenantContextBinder final InternalCallContext context);
+@EntitySqlDaoStringTemplate
+public interface RefundSqlDao extends EntitySqlDao<RefundModelDao, Refund> {
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     void updateStatus(@Bind("id") final String refundId,
                       @Bind("refundStatus") final String status,
-                      @InternalTenantContextBinder final InternalCallContext context);
-
-    @SqlQuery
-    RefundModelDao getRefund(@Bind("id") final String refundId,
-                             @InternalTenantContextBinder final InternalTenantContext context);
+                      @BindBean final InternalCallContext context);
 
     @SqlQuery
     List<RefundModelDao> getRefundsForPayment(@Bind("paymentId") final String paymentId,
-                                              @InternalTenantContextBinder final InternalTenantContext context);
+                                              @BindBean final InternalTenantContext context);
 
     @SqlQuery
     List<RefundModelDao> getRefundsForAccount(@Bind("accountId") final String accountId,
-                                              @InternalTenantContextBinder final InternalTenantContext context);
-
-    @Override
-    @SqlUpdate
-    public void insertHistoryFromTransaction(@RefundHistoryBinder final EntityHistory<RefundModelDao> payment,
-                                             @InternalTenantContextBinder final InternalCallContext context);
-
-    public static final class RefundModelDaoBinder extends BinderBase implements Binder<Bind, RefundModelDao> {
-
-        @Override
-        public void bind(final SQLStatement stmt, final Bind bind, final RefundModelDao refund) {
-            stmt.bind("id", refund.getId().toString());
-            stmt.bind("accountId", refund.getAccountId().toString());
-            stmt.bind("paymentId", refund.getPaymentId().toString());
-            stmt.bind("amount", refund.getAmount());
-            stmt.bind("currency", refund.getCurrency().toString());
-            stmt.bind("isAdjusted", refund.isAdjsuted());
-            stmt.bind("refundStatus", refund.getRefundStatus().toString());
-            // createdDate and updatedDate are populated by the @InternalTenantContextBinder
-        }
-    }
-
-    public static class RefundModelDaoMapper extends MapperBase implements ResultSetMapper<RefundModelDao> {
-
-        @Override
-        public RefundModelDao map(final int index, final ResultSet rs, final StatementContext ctx)
-                throws SQLException {
-            final UUID id = getUUID(rs, "id");
-            final UUID accountId = getUUID(rs, "account_id");
-            final UUID paymentId = getUUID(rs, "payment_id");
-            final BigDecimal amount = rs.getBigDecimal("amount");
-            final boolean isAdjusted = rs.getBoolean("is_adjusted");
-            final Currency currency = Currency.valueOf(rs.getString("currency"));
-            final RefundStatus refundStatus = RefundStatus.valueOf(rs.getString("refund_status"));
-            final DateTime createdDate = getDateTime(rs, "created_date");
-            final DateTime updatedDate = getDateTime(rs, "updated_date");
-            return new RefundModelDao(id, accountId, paymentId, amount, currency, isAdjusted, refundStatus, createdDate, updatedDate);
-        }
-    }
+                                              @BindBean final InternalTenantContext context);
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/glue/PaymentModule.java b/payment/src/main/java/com/ning/billing/payment/glue/PaymentModule.java
index 77ebc62..906a0b6 100644
--- a/payment/src/main/java/com/ning/billing/payment/glue/PaymentModule.java
+++ b/payment/src/main/java/com/ning/billing/payment/glue/PaymentModule.java
@@ -25,6 +25,7 @@ import org.skife.config.ConfigSource;
 import org.skife.config.ConfigurationObjectFactory;
 import org.skife.config.SimplePropertyConfigSource;
 
+import com.ning.billing.payment.dao.DefaultPaymentDao;
 import com.ning.billing.util.config.PaymentConfig;
 import com.ning.billing.payment.api.DefaultPaymentApi;
 import com.ning.billing.payment.api.PaymentApi;
@@ -35,7 +36,6 @@ import com.ning.billing.payment.bus.TagHandler;
 import com.ning.billing.payment.core.PaymentMethodProcessor;
 import com.ning.billing.payment.core.PaymentProcessor;
 import com.ning.billing.payment.core.RefundProcessor;
-import com.ning.billing.payment.dao.AuditedPaymentDao;
 import com.ning.billing.payment.dao.PaymentDao;
 import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
 import com.ning.billing.payment.retry.AutoPayRetryService;
@@ -72,7 +72,7 @@ public class PaymentModule extends AbstractModule {
     }
 
     protected void installPaymentDao() {
-        bind(PaymentDao.class).to(AuditedPaymentDao.class).asEagerSingleton();
+        bind(PaymentDao.class).to(DefaultPaymentDao.class).asEagerSingleton();
     }
 
     protected void installPaymentProviderPlugins(final PaymentConfig config) {
diff --git a/payment/src/main/java/com/ning/billing/payment/retry/BaseRetryService.java b/payment/src/main/java/com/ning/billing/payment/retry/BaseRetryService.java
index 6ec7286..2828c63 100644
--- a/payment/src/main/java/com/ning/billing/payment/retry/BaseRetryService.java
+++ b/payment/src/main/java/com/ning/billing/payment/retry/BaseRetryService.java
@@ -20,17 +20,18 @@ import java.io.IOException;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.ning.billing.ObjectType;
-import com.ning.billing.util.config.PaymentConfig;
 import com.ning.billing.payment.glue.DefaultPaymentService;
 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.config.PaymentConfig;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.notificationq.NotificationKey;
 import com.ning.billing.util.notificationq.NotificationQueue;
 import com.ning.billing.util.notificationq.NotificationQueueService;
@@ -52,8 +53,8 @@ public abstract class BaseRetryService implements RetryService {
     private NotificationQueue retryQueue;
 
     public BaseRetryService(final NotificationQueueService notificationQueueService,
-            final PaymentConfig config,
-            final InternalCallContextFactory internalCallContextFactory) {
+                            final PaymentConfig config,
+                            final InternalCallContextFactory internalCallContextFactory) {
         this.notificationQueueService = notificationQueueService;
         this.config = config;
         this.internalCallContextFactory = internalCallContextFactory;
@@ -62,20 +63,20 @@ public abstract class BaseRetryService implements RetryService {
     @Override
     public void initialize(final String svcName) throws NotificationQueueAlreadyExists {
         retryQueue = notificationQueueService.createNotificationQueue(svcName,
-                getQueueName(),
-                new NotificationQueueHandler() {
-            @Override
-            public void handleReadyNotification(final NotificationKey notificationKey, final DateTime eventDateTime, final Long accountRecordId, final Long tenantRecordId) {
-                if (!(notificationKey instanceof PaymentRetryNotificationKey)) {
-                    log.error("Payment service got an unexpected notification type {}", notificationKey.getClass().getName());
-                    return;
-                }
-                final PaymentRetryNotificationKey key = (PaymentRetryNotificationKey) notificationKey;
-                final InternalCallContext callContext = internalCallContextFactory.createInternalCallContext(tenantRecordId, accountRecordId, PAYMENT_RETRY_SERVICE, CallOrigin.INTERNAL, UserType.SYSTEM, null);
-                retry(key.getUuidKey(), callContext);
-            }
-        },
-        config);
+                                                                      getQueueName(),
+                                                                      new NotificationQueueHandler() {
+                                                                          @Override
+                                                                          public void handleReadyNotification(final NotificationKey notificationKey, final DateTime eventDateTime, final Long accountRecordId, final Long tenantRecordId) {
+                                                                              if (!(notificationKey instanceof PaymentRetryNotificationKey)) {
+                                                                                  log.error("Payment service got an unexpected notification type {}", notificationKey.getClass().getName());
+                                                                                  return;
+                                                                              }
+                                                                              final PaymentRetryNotificationKey key = (PaymentRetryNotificationKey) notificationKey;
+                                                                              final InternalCallContext callContext = internalCallContextFactory.createInternalCallContext(tenantRecordId, accountRecordId, PAYMENT_RETRY_SERVICE, CallOrigin.INTERNAL, UserType.SYSTEM, null);
+                                                                              retry(key.getUuidKey(), callContext);
+                                                                          }
+                                                                      },
+                                                                      config);
     }
 
     @Override
@@ -101,13 +102,13 @@ public abstract class BaseRetryService implements RetryService {
 
         @Inject
         public RetryServiceScheduler(final NotificationQueueService notificationQueueService,
-                final InternalCallContextFactory internalCallContextFactory) {
+                                     final InternalCallContextFactory internalCallContextFactory) {
             this.notificationQueueService = notificationQueueService;
             this.internalCallContextFactory = internalCallContextFactory;
         }
 
-        public boolean scheduleRetryFromTransaction(final UUID paymentId, final DateTime timeOfRetry, final Transmogrifier transactionalDao) {
-            return scheduleRetryInternal(paymentId, timeOfRetry, transactionalDao);
+        public boolean scheduleRetryFromTransaction(final UUID paymentId, final DateTime timeOfRetry, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) {
+            return scheduleRetryInternal(paymentId, timeOfRetry, entitySqlDaoWrapperFactory);
         }
 
         public boolean scheduleRetry(final UUID paymentId, final DateTime timeOfRetry) {
@@ -132,7 +133,7 @@ public abstract class BaseRetryService implements RetryService {
              */
         }
 
-        private boolean scheduleRetryInternal(final UUID paymentId, final DateTime timeOfRetry, final Transmogrifier transactionalDao) {
+        private boolean scheduleRetryInternal(final UUID paymentId, final DateTime timeOfRetry, final EntitySqlDaoWrapperFactory<EntitySqlDao> transactionalDao) {
             final InternalCallContext context = createCallContextFromPaymentId(paymentId);
 
             try {
diff --git a/payment/src/main/java/com/ning/billing/payment/retry/PluginFailureRetryService.java b/payment/src/main/java/com/ning/billing/payment/retry/PluginFailureRetryService.java
index a4610f2..9217abd 100644
--- a/payment/src/main/java/com/ning/billing/payment/retry/PluginFailureRetryService.java
+++ b/payment/src/main/java/com/ning/billing/payment/retry/PluginFailureRetryService.java
@@ -19,15 +19,16 @@ package com.ning.billing.payment.retry;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.ning.billing.util.config.PaymentConfig;
 import com.ning.billing.payment.core.PaymentProcessor;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.config.PaymentConfig;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.notificationq.NotificationQueueService;
 
 import com.google.inject.Inject;
@@ -82,7 +83,7 @@ public class PluginFailureRetryService extends BaseRetryService implements Retry
             return super.scheduleRetry(paymentId, nextRetryDate);
         }
 
-        public boolean scheduleRetryFromTransaction(final UUID paymentId, final int retryAttempt, final Transmogrifier transactionalDao) {
+        public boolean scheduleRetryFromTransaction(final UUID paymentId, final int retryAttempt, final EntitySqlDaoWrapperFactory<EntitySqlDao> transactionalDao) {
             final DateTime nextRetryDate = getNextRetryDate(retryAttempt);
             if (nextRetryDate == null) {
                 return false;
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
index c6edf85..2e2402a 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
@@ -1,131 +1,68 @@
-group PaymentAttemptSqlDao;
-
-CHECK_TENANT(prefix) ::= "<prefix>tenant_record_id = :tenantRecordId"
-AND_CHECK_TENANT(prefix) ::= "AND <CHECK_TENANT(prefix)>"
-
-paymentAttemptFields(prefix) ::= <<
-    <prefix>id,
-    <prefix>payment_id,    
-    <prefix>gateway_error_code,
-    <prefix>gateway_error_msg,    
-    <prefix>processing_status,
-    <prefix>requested_amount,
-    <prefix>created_by,
-    <prefix>created_date,
-    <prefix>updated_by,
-    <prefix>updated_date,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
-
-insertPaymentAttempt() ::= <<
-    INSERT INTO payment_attempts (<paymentAttemptFields()>)
-    VALUES (:id, :paymentId, :gatewayErrorCode, :gatewayErrorMsg, :processingStatus, :requestedAmount, :userName,
-            :createdDate, :userName, :createdDate, :accountRecordId, :tenantRecordId);
->>
-
-getPaymentAttempt() ::= <<
-    SELECT <paymentAttemptFields("pa.")>
-    , pa.created_date as effective_date
-    , p.account_id as account_id
-    , p.invoice_id as invoice_id
-      FROM payment_attempts pa join payments p
-     WHERE pa.id = :id 
-     AND pa.payment_id = p.id
-     <AND_CHECK_TENANT("pa.")>
-     <AND_CHECK_TENANT("p.")>
-     ;
+group PaymentAttemptSqlDao: EntitySqlDao;
+
+tableFields(prefix) ::= <<
+  <prefix>payment_id
+, <prefix>gateway_error_code
+, <prefix>gateway_error_msg
+, <prefix>processing_status
+, <prefix>requested_amount
+, <prefix>created_by
+, <prefix>created_date
+, <prefix>updated_by
+, <prefix>updated_date
 >>
 
-getPaymentAttempts() ::= <<
-    SELECT <paymentAttemptFields("pa.")>
-    , pa.created_date as effective_date
-    , p.account_id as account_id
-    , p.invoice_id as invoice_id
-      FROM payment_attempts pa join payments p
-     WHERE pa.payment_id = :paymentId
-     AND p.id = :paymentId
-     <AND_CHECK_TENANT("pa.")>
-     <AND_CHECK_TENANT("p.")>
-     ORDER BY effective_date ASC;
+tableValues() ::= <<
+  :paymentId
+, :gatewayErrorCode
+, :gatewayErrorMsg
+, :processingStatus
+, :requestedAmount
+, :createdBy
+, :createdDate
+, :updatedBy
+, :updatedDate
 >>
 
-updatePaymentAttemptStatus() ::= <<
-    UPDATE payment_attempts
-    SET processing_status = :processingStatus,
-        gateway_error_code = :gatewayErrorCode,
-        gateway_error_msg = :gatewayErrorMsg        
-    WHERE  id = :id
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-getRecordId() ::= <<
-    SELECT record_id
-    FROM payment_attempts
-    WHERE id = :id
-    <AND_CHECK_TENANT()>
-    ;
->>
+tableName() ::= "payment_attempts"
 
-getPaymentAttemptIdFromPaymentId() ::= <<
-    SELECT id
-    FROM payment_attempts
-    WHERE payment_id = :paymentId
-    <AND_CHECK_TENANT()>
-    ;
->>
+historyTableName() ::= "payment_attempt_history"
 
-historyFields(prefix) ::= <<
-    <prefix>record_id,
-    <prefix>id,
-    <prefix>payment_id,
-    <prefix>gateway_error_code,
-    <prefix>gateway_error_msg,    
-    <prefix>processing_status,
-    <prefix>requested_amount,    
-    <prefix>created_by,
-    <prefix>created_date,
-    <prefix>updated_by,
-    <prefix>updated_date,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
 
-insertHistoryFromTransaction() ::= <<
-    INSERT INTO payment_attempt_history (<historyFields()>)
-    VALUES (:recordId, :id, :paymentId, :gatewayErrorCode, :gatewayErrorMsg, :processingStatus, :requestedAmount,
-            :userName, :createdDate, :userName, :updatedDate, :accountRecordId, :tenantRecordId);
+getById(id) ::= <<
+select <allTableFields("pa.")>
+, pa.created_date as effective_date
+, p.account_id as account_id
+, p.invoice_id as invoice_id
+from <tableName()> pa join payments p
+where pa.id = :id
+and pa.payment_id = p.id
+<AND_CHECK_TENANT("pa.")>
+<AND_CHECK_TENANT("p.")>
+;
 >>
 
-getHistoryRecordId() ::= <<
-    SELECT MAX(history_record_id)
-    FROM payment_attempt_history
-    WHERE record_id = :recordId
-    <AND_CHECK_TENANT()>
-    ;
+getByPaymentId(paymentId) ::= <<
+select <allTableFields("pa.")>
+, pa.created_date as effective_date
+, p.account_id as account_id
+, p.invoice_id as invoice_id
+from <tableName()> pa join payments p
+where pa.payment_id = :paymentId
+and p.id = :paymentId
+<AND_CHECK_TENANT("pa.")>
+<AND_CHECK_TENANT("p.")>
+order by effective_date asc
+;
 >>
 
-auditFields(prefix) ::= <<
-    <prefix>table_name,
-    <prefix>record_id,
-    <prefix>change_type,
-    <prefix>change_date,
-    <prefix>changed_by,
-    <prefix>reason_code,
-    <prefix>comments,
-    <prefix>user_token,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
 
-insertAuditFromTransaction() ::= <<
-    INSERT INTO audit_log(<auditFields()>)
-    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken, :accountRecordId, :tenantRecordId);
+updatePaymentAttemptStatus() ::= <<
+update <tableName()>
+set processing_status = :processingStatus
+, gateway_error_code = :gatewayErrorCode
+, gateway_error_msg = :gatewayErrorMsg
+where id = :id
+<AND_CHECK_TENANT()>
+;
 >>
-
-
-
-
-
-
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentMethodSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentMethodSqlDao.sql.stg
index f7fcd4a..a242999 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentMethodSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentMethodSqlDao.sql.stg
@@ -1,117 +1,64 @@
-group PaymentMethodSqlDao;
+group PaymentMethodSqlDao: EntitySqlDao;
 
-CHECK_TENANT() ::= "tenant_record_id = :tenantRecordId"
-AND_CHECK_TENANT() ::= "AND <CHECK_TENANT()>"
 
-paymentMethodFields(prefix) ::= <<
-    <prefix>id,
-    <prefix>account_id,
-    <prefix>plugin_name,
-    <prefix>is_active,  
-    <prefix>external_id,
-    <prefix>created_by,
-    <prefix>created_date,
-    <prefix>updated_by,
-    <prefix>updated_date,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
 
-insertPaymentMethod() ::= <<
-    INSERT INTO payment_methods (<paymentMethodFields()>)
-    VALUES (:id, :accountId, :pluginName , :isActive, :externalId, :userName, :createdDate, :userName, :createdDate, :accountRecordId, :tenantRecordId);
->>
+tableName() ::= "payment_methods"
 
-markPaymentMethodAsDeleted() ::= <<
-    UPDATE payment_methods 
-    SET is_active = 0
-    WHERE  id = :id
-    <AND_CHECK_TENANT()>
-    ;
->>
+historyTableName() ::= "payment_method_history"
 
-unmarkPaymentMethodAsDeleted() ::= <<
-    UPDATE payment_methods
-    SET is_active = 1
-    WHERE  id = :id
-    <AND_CHECK_TENANT()>
-    ;
->>
+andCheckSoftDeletionWithComma(prefix) ::= "and <prefix>is_active"
 
-getPaymentMethod() ::= <<
-    SELECT <paymentMethodFields()>
-      FROM payment_methods
-    WHERE id = :id AND is_active = 1
-    <AND_CHECK_TENANT()>
-    ;
+tableFields(prefix) ::= <<
+  <prefix>account_id
+, <prefix>plugin_name
+, <prefix>is_active
+, <prefix>external_id
+, <prefix>created_by
+, <prefix>created_date
+, <prefix>updated_by
+, <prefix>updated_date
 >>
 
-getPaymentMethodIncludedDelete() ::= <<
-    SELECT <paymentMethodFields()>
-      FROM payment_methods
-    WHERE id = :id;
+tableValues() ::= <<
+  :accountId
+, :pluginName
+, :isActive
+, :externalId
+, :createdBy
+, :createdDate
+, :updatedBy
+, :updatedDate
 >>
 
-getPaymentMethods() ::= <<
-    SELECT <paymentMethodFields()>
-      FROM payment_methods
-    WHERE account_id = :accountId AND is_active = 1
-    <AND_CHECK_TENANT()>
-    ;
->>
 
-getRecordId() ::= <<
-    SELECT record_id
-    FROM payment_methods
-    WHERE id = :id
-    <AND_CHECK_TENANT()>
-    ;
+markPaymentMethodAsDeleted(id) ::= <<
+update <tableName()>
+set is_active = 0
+where  id = :id
+<AND_CHECK_TENANT()>
+;
 >>
 
-historyFields(prefix) ::= <<
-    <prefix>record_id,
-    <prefix>id,
-    <prefix>account_id,
-    <prefix>plugin_name,
-    <prefix>is_active,
-    <prefix>external_id,  
-    <prefix>created_by,
-    <prefix>created_date,
-    <prefix>updated_by,
-    <prefix>updated_date,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
+unmarkPaymentMethodAsDeleted(id) ::= <<
+update <tableName()>
+set is_active = 1
+where  id = :id
+<AND_CHECK_TENANT()>
+;
 >>
 
-insertHistoryFromTransaction() ::= <<
-    INSERT INTO payment_method_history (<historyFields()>)
-    VALUES (:recordId, :id, :accountId, :pluginName , :isActive, :externalId ,:userName, :createdDate, :userName, :createdDate, :accountRecordId, :tenantRecordId);
+getPaymentMethodIncludedDelete(accountId) ::= <<
+select <allTableFields()>
+from <tableName()>
+where id = :id
+;
 >>
 
-getHistoryRecordId() ::= <<
-    SELECT MAX(history_record_id)
-    FROM payment_method_history
-    WHERE record_id = :recordId
-    <AND_CHECK_TENANT()>
-    ;
+getByAccountId(accountId) ::= <<
+select
+<allTableFields()>
+from <tableName()>
+where account_id = :accountId
+and is_active = 1
+;
 >>
-
-auditFields(prefix) ::= <<
-    <prefix>table_name,
-    <prefix>record_id,
-    <prefix>change_type,
-    <prefix>change_date,
-    <prefix>changed_by,
-    <prefix>reason_code,
-    <prefix>comments,
-    <prefix>user_token,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
-
-insertAuditFromTransaction() ::= <<
-    INSERT INTO audit_log(<auditFields()>)
-    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken, :accountRecordId, :tenantRecordId);
->>
-
-
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
index f2170b3..f04f02e 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
@@ -1,147 +1,94 @@
-group PaymentSqlDao;
-
-CHECK_TENANT() ::= "tenant_record_id = :tenantRecordId"
-AND_CHECK_TENANT() ::= "AND <CHECK_TENANT()>"
-
-paymentFields(prefix) ::= <<
-    <prefix>id,
-    <prefix>account_id,
-    <prefix>invoice_id,   
-    <prefix>payment_method_id,
-    <prefix>amount,
-    <prefix>effective_date,
-    <prefix>currency,
-    <prefix>payment_status,
-    <prefix>ext_first_payment_ref_id,
-    <prefix>ext_second_payment_ref_id,    
-    <prefix>created_by,
-    <prefix>created_date,
-    <prefix>updated_by,
-    <prefix>updated_date,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
+group PaymentSqlDao: EntitySqlDao;
 
-insertPayment() ::= <<
-    INSERT INTO payments (<paymentFields()>)
-    VALUES (:id, :accountId, :invoiceId, :paymentMethodId, :amount, :effectiveDate, :currency, :paymentStatus,
-            :extFirstPaymentRefId, :extSecondPaymentRefId, :userName, :createdDate, :userName, :createdDate, :accountRecordId, :tenantRecordId);
->>
 
-getPayment() ::= <<
-    SELECT <paymentFields()>
-    , record_id as payment_number
-      FROM payments
-    WHERE id = :id
-    <AND_CHECK_TENANT()>
-    ;
+extraTableFieldsWithComma(prefix) ::= <<
+, <prefix>record_id as payment_number
 >>
 
-getLastPaymentForAccountAndPaymentMethod() ::= <<
-    SELECT <paymentFields()>
-    , record_id as payment_number
-      FROM payments
-    WHERE account_id = :accountId AND payment_method_id = :paymentMethodId
-    <AND_CHECK_TENANT()>
-    ORDER BY effective_date desc limit 1;
->> 
-
-getPaymentsForInvoice() ::= <<
-    SELECT <paymentFields()>
-    , record_id as payment_number
-      FROM payments
-    WHERE invoice_id = :invoiceId
-    <AND_CHECK_TENANT()>
-    ;
+tableFields(prefix) ::= <<
+  <prefix>account_id
+, <prefix>invoice_id
+, <prefix>payment_method_id
+, <prefix>amount
+, <prefix>effective_date
+, <prefix>currency
+, <prefix>payment_status
+, <prefix>ext_first_payment_ref_id
+, <prefix>ext_second_payment_ref_id
+, <prefix>created_by
+, <prefix>created_date
+, <prefix>updated_by
+, <prefix>updated_date
 >>
 
-getPaymentsForAccount() ::= <<
-    SELECT <paymentFields()>
-    , record_id as payment_number
-      FROM payments
-    WHERE account_id = :accountId
-    <AND_CHECK_TENANT()>
-    ;
+tableValues() ::= <<
+  :accountId
+, :invoiceId
+, :paymentMethodId
+, :amount
+, :effectiveDate
+, :currency
+, :paymentStatus
+, :extFirstPaymentRefId
+, :extSecondPaymentRefId
+, :createdBy
+, :createdDate
+, :updatedBy
+, :updatedDate
 >>
 
+tableName() ::= "payments"
 
-updatePaymentStatusAndExtRef() ::= <<
-    UPDATE payments
-    SET payment_status = :paymentStatus,
-        ext_first_payment_ref_id = :extFirstPaymentRefId,
-        ext_second_payment_ref_id = :extSecondPaymentRefId
-    WHERE id = :id
-    <AND_CHECK_TENANT()>
-    ;
->>
+historyTableName() ::= "payment_history"
 
-updatePaymentAmount() ::= <<
-    UPDATE payments
-    SET amount = :amount
-    WHERE id = :id
-    <AND_CHECK_TENANT()>
-    ;
->>
 
-getRecordId() ::= <<
-    SELECT record_id
-    FROM payments
-    WHERE id = :id
-    <AND_CHECK_TENANT()>
-    ;
+getPaymentsForAccount() ::= <<
+select <allTableFields()>
+, record_id as payment_number
+from payments
+where account_id = :accountId
+<AND_CHECK_TENANT()>
+;
 >>
 
-
-historyFields(prefix) ::= <<
-    <prefix>record_id,
-    <prefix>id,
-    <prefix>account_id,
-    <prefix>invoice_id,   
-    <prefix>payment_method_id,
-    <prefix>amount,
-    <prefix>effective_date,
-    <prefix>currency,
-    <prefix>payment_status,
-    <prefix>ext_first_payment_ref_id,
-    <prefix>ext_second_payment_ref_id,    
-    <prefix>created_by,
-    <prefix>created_date,
-    <prefix>updated_by,
-    <prefix>updated_date,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
+getPaymentsForInvoice() ::= <<
+select <allTableFields()>
+, record_id as payment_number
+from payments
+where invoice_id = :invoiceId
+<AND_CHECK_TENANT()>
+;
 >>
 
-insertHistoryFromTransaction() ::= <<
-    INSERT INTO payment_history (<historyFields()>)
-    VALUES (:recordId, :id, :accountId, :invoiceId, :paymentMethodId, :amount, :effectiveDate, :currency, :paymentStatus,
-            :extFirstPaymentRefId, :extSecondPaymentRefId, :userName, :createdDate, :userName, :updatedDate, :accountRecordId, :tenantRecordId);
+
+getLastPaymentForAccountAndPaymentMethod() ::= <<
+select <allTableFields()>
+, record_id as payment_number
+from payments
+where account_id = :accountId
+and payment_method_id = :paymentMethodId
+<AND_CHECK_TENANT()>
+order by effective_date desc limit 1
+;
 >>
 
 
-getHistoryRecordId() ::= <<
-    SELECT MAX(history_record_id)
-    FROM payment_history
-    WHERE record_id = :recordId
-    <AND_CHECK_TENANT()>
-    ;
+updatePaymentStatusAndExtRef() ::= <<
+update payments
+set payment_status = :paymentStatus
+, ext_first_payment_ref_id = :extFirstPaymentRefId
+, ext_second_payment_ref_id = :extSecondPaymentRefId
+where id = :id
+<AND_CHECK_TENANT()>
+;
 >>
 
-auditFields(prefix) ::= <<
-    <prefix>table_name,
-    <prefix>record_id,
-    <prefix>change_type,
-    <prefix>change_date,
-    <prefix>changed_by,
-    <prefix>reason_code,
-    <prefix>comments,
-    <prefix>user_token,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
+updatePaymentAmount() ::= <<
+update <tableName()>
+set amount = :amount
+where id = :id
+<AND_CHECK_TENANT()>
+;
 >>
 
-insertAuditFromTransaction() ::= <<
-    INSERT INTO audit_log(<auditFields()>)
-    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken, :accountRecordId, :tenantRecordId);
->>
 
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/RefundSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/RefundSqlDao.sql.stg
index e277a4e..68c06fb 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/RefundSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/RefundSqlDao.sql.stg
@@ -1,115 +1,55 @@
-group RefundSqlDao;
-
-CHECK_TENANT() ::= "tenant_record_id = :tenantRecordId"
-AND_CHECK_TENANT() ::= "AND <CHECK_TENANT()>"
-
-refundFields(prefix) ::= <<
-<prefix>id,
-<prefix>account_id,
-<prefix>payment_id,
-<prefix>amount,  
-<prefix>currency,
-<prefix>is_adjusted,
-<prefix>refund_status,    
-<prefix>created_by,
-<prefix>created_date,
-<prefix>updated_by,
-<prefix>updated_date,
-<prefix>account_record_id,
-<prefix>tenant_record_id
->>
-
-insertRefund() ::= <<
-    INSERT INTO refunds (<refundFields()>)
-    VALUES (:id, :accountId, :paymentId, :amount, :currency, :isAdjusted, :refundStatus, :userName, :createdDate,
-            :userName, :createdDate, :accountRecordId, :tenantRecordId);
->>
-
-updateStatus() ::= <<
-    UPDATE refunds
-    SET refund_status = :refundStatus
-    WHERE id = :id
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-getRefund() ::= <<
-    SELECT <refundFields()>
-    FROM refunds
-    WHERE id = :id
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-getRefundsForPayment()  ::= <<
-    SELECT <refundFields()>
-    FROM refunds
-    WHERE payment_id = :paymentId
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-getRefundsForAccount()  ::= <<
-    SELECT <refundFields()>
-    FROM refunds
-    WHERE account_id = :accountId
-    <AND_CHECK_TENANT()>
-    ;
->> 
-
-getRecordId() ::= <<
-    SELECT record_id
-    FROM refunds
-    WHERE id = :id
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-historyFields(prefix) ::= <<
-    <prefix>record_id,
-    <prefix>id,
-    <prefix>account_id,
-    <prefix>payment_id,
-    <prefix>amount,  
-    <prefix>currency,
-    <prefix>is_adjusted,
-    <prefix>refund_status,    
-    <prefix>created_by,
-    <prefix>created_date,
-    <prefix>updated_by,
-    <prefix>updated_date,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
-
-insertHistoryFromTransaction() ::= <<
-    INSERT INTO refund_history (<historyFields()>)
-    VALUES (:recordId, :id, :accountId, :paymentId, :amount, :currency,  :isAdjusted, :refundStatus, :userName,
-            :createdDate, :userName, :createdDate, :accountRecordId, :tenantRecordId);
->>
-
-getHistoryRecordId() ::= <<
-    SELECT MAX(history_record_id)
-    FROM payment_method_history
-    WHERE record_id = :recordId
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-auditFields(prefix) ::= <<
-    <prefix>table_name,
-    <prefix>record_id,
-    <prefix>change_type,
-    <prefix>change_date,
-    <prefix>changed_by,
-    <prefix>reason_code,
-    <prefix>comments,
-    <prefix>user_token,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
-
-insertAuditFromTransaction() ::= <<
-    INSERT INTO audit_log(<auditFields()>)
-    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken, :accountRecordId, :tenantRecordId);
+group RefundSqlDao: EntitySqlDao;
+
+tableName() ::= "refunds"
+
+historyTableName() ::= "refund_history"
+
+tableFields(prefix) ::= <<
+  <prefix>account_id
+, <prefix>payment_id
+, <prefix>amount
+, <prefix>currency
+, <prefix>is_adjusted
+, <prefix>refund_status
+, <prefix>created_by
+, <prefix>created_date
+, <prefix>updated_by
+, <prefix>updated_date
+>>
+
+tableValues() ::= <<
+:accountId
+, :paymentId
+, :amount
+, :currency
+, :isAdjusted
+, :refundStatus
+, :createdBy
+, :createdDate
+, :updatedBy
+, :updatedDate
+>>
+
+updateStatus(refundStatus) ::= <<
+update <tableName()>
+set refund_status = :refundStatus
+where id = :id
+<AND_CHECK_TENANT()>
+;
+>>
+
+getRefundsForPayment(paymentId)  ::= <<
+select <allTableFields()>
+from <tableName()>
+where payment_id = :paymentId
+<AND_CHECK_TENANT()>
+;
+>>
+
+getRefundsForAccount(accountId)  ::= <<
+select <allTableFields()>
+from <tableName()>
+where account_id = :accountId
+<AND_CHECK_TENANT()>
+;
 >>
diff --git a/payment/src/main/resources/com/ning/billing/payment/ddl.sql b/payment/src/main/resources/com/ning/billing/payment/ddl.sql
index dc71838..14c5246 100644
--- a/payment/src/main/resources/com/ning/billing/payment/ddl.sql
+++ b/payment/src/main/resources/com/ning/billing/payment/ddl.sql
@@ -26,9 +26,9 @@ CREATE INDEX payments_tenant_account_record_id ON payments(tenant_record_id, acc
 
 DROP TABLE IF EXISTS payment_history; 
 CREATE TABLE payment_history (
-    history_record_id int(11) unsigned NOT NULL AUTO_INCREMENT,    
-    record_id int(11) unsigned NOT NULL,
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
     id char(36) NOT NULL,
+    target_record_id int(11) unsigned NOT NULL,
     account_id char(36) COLLATE utf8_bin NOT NULL,
     invoice_id char(36) COLLATE utf8_bin NOT NULL,
     payment_method_id char(36) COLLATE utf8_bin NOT NULL,    
@@ -38,15 +38,16 @@ CREATE TABLE payment_history (
     payment_status varchar(50),
     ext_first_payment_ref_id varchar(128),
     ext_second_payment_ref_id varchar(128),    
+    change_type char(6) NOT NULL,
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
     updated_by varchar(50) NOT NULL,
     updated_date datetime NOT NULL,
     account_record_id int(11) unsigned default null,
     tenant_record_id int(11) unsigned default null,
-    PRIMARY KEY (history_record_id)
+    PRIMARY KEY(record_id)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-CREATE INDEX payment_history_record_id ON payment_history(record_id);
+CREATE INDEX payment_history_target_record_id ON payment_history(target_record_id);
 CREATE INDEX payment_history_tenant_account_record_id ON payment_history(tenant_record_id, account_record_id);
 
 DROP TABLE IF EXISTS payment_attempts;
@@ -72,23 +73,24 @@ CREATE INDEX payment_attempts_tenant_account_record_id ON payment_attempts(tenan
 
 DROP TABLE IF EXISTS payment_attempt_history;
 CREATE TABLE payment_attempt_history (
-    history_record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
-    record_id int(11) unsigned NOT NULL,
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
     id char(36) NOT NULL,
+    target_record_id int(11) unsigned NOT NULL,
     payment_id char(36) COLLATE utf8_bin NOT NULL,
     gateway_error_code varchar(32),              
     gateway_error_msg varchar(256),
     processing_status varchar(50),
     requested_amount numeric(10,4),
+    change_type char(6) NOT NULL,
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
     updated_by varchar(50) NOT NULL,
     updated_date datetime NOT NULL,
     account_record_id int(11) unsigned default null,
     tenant_record_id int(11) unsigned default null,
-    PRIMARY KEY (history_record_id)
+    PRIMARY KEY(record_id)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-CREATE INDEX payment_attempt_history_record_id ON payment_attempt_history(record_id);
+CREATE INDEX payment_attempt_history_target_record_id ON payment_attempt_history(target_record_id);
 CREATE INDEX payment_attempt_history_tenant_account_record_id ON payment_attempt_history(tenant_record_id, account_record_id);
 
 DROP TABLE IF EXISTS payment_methods;
@@ -113,22 +115,23 @@ CREATE INDEX payment_methods_tenant_account_record_id ON payment_methods(tenant_
 
 DROP TABLE IF EXISTS payment_method_history;
 CREATE TABLE payment_method_history (
-    history_record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
-    record_id int(11) unsigned NOT NULL,
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
     id char(36) NOT NULL,
+    target_record_id int(11) unsigned NOT NULL,
     account_id char(36) COLLATE utf8_bin NOT NULL,
     plugin_name varchar(20) DEFAULT NULL, 
     is_active bool DEFAULT true, 
     external_id varchar(64),                  
+    change_type char(6) NOT NULL,
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
     updated_by varchar(50) NOT NULL,
     updated_date datetime NOT NULL,
     account_record_id int(11) unsigned default null,
     tenant_record_id int(11) unsigned default null,
-    PRIMARY KEY (history_record_id)
+    PRIMARY KEY(record_id)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-CREATE UNIQUE INDEX payment_method_history_record_id ON payment_method_history(record_id);
+CREATE INDEX payment_method_history_target_record_id ON payment_method_history(target_record_id);
 CREATE INDEX payment_method_history_tenant_account_record_id ON payment_method_history(tenant_record_id, account_record_id);
 
 DROP TABLE IF EXISTS refunds; 
@@ -156,24 +159,25 @@ CREATE INDEX refunds_tenant_account_record_id ON refunds(tenant_record_id, accou
 
 DROP TABLE IF EXISTS refund_history; 
 CREATE TABLE refund_history (
-    history_record_id int(11) unsigned NOT NULL AUTO_INCREMENT, 
-    record_id int(11) unsigned NOT NULL,
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
     id char(36) NOT NULL,
+    target_record_id int(11) unsigned NOT NULL,
     account_id char(36) COLLATE utf8_bin NOT NULL,
     payment_id char(36) COLLATE utf8_bin NOT NULL,    
     amount numeric(10,4),
     currency char(3),   
     is_adjusted tinyint(1),
     refund_status varchar(50), 
+    change_type char(6) NOT NULL,
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
     updated_by varchar(50) NOT NULL,
     updated_date datetime NOT NULL,
     account_record_id int(11) unsigned default null,
     tenant_record_id int(11) unsigned default null,
-    PRIMARY KEY (history_record_id)
+    PRIMARY KEY(record_id)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-CREATE INDEX refund_history_record_id ON refund_history(record_id);
+CREATE INDEX refund_history_target_record_id ON refund_history(target_record_id);
 CREATE INDEX refund_history_tenant_account_record_id ON refund_history(tenant_record_id, account_record_id);
 
 
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
index e279a76..c574c78 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
@@ -54,10 +54,8 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
     @BeforeSuite(groups = "slow")
     public void setup() throws IOException {
         clock = new DefaultClock();
-
         setupDb();
-
-        paymentDao = new AuditedPaymentDao(dbi, null);
+        paymentDao = new DefaultPaymentDao(dbi);
     }
 
     private void setupDb() {
@@ -87,7 +85,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(refundCheck.getPaymentId(), paymentId1);
         assertEquals(refundCheck.getAmount().compareTo(amount1), 0);
         assertEquals(refundCheck.getCurrency(), currency);
-        assertEquals(refundCheck.isAdjsuted(), true);
+        assertEquals(refundCheck.isAdjusted(), true);
         assertEquals(refundCheck.getRefundStatus(), RefundStatus.CREATED);
 
         final BigDecimal amount2 = new BigDecimal(7.00);
@@ -155,7 +153,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(savedAttempt.getPaymentId(), payment.getId());
         assertEquals(savedAttempt.getAccountId(), accountId);
         assertEquals(savedAttempt.getInvoiceId(), invoiceId);
-        assertEquals(savedAttempt.getPaymentStatus(), PaymentStatus.SUCCESS);
+        assertEquals(savedAttempt.getProcessingStatus(), PaymentStatus.SUCCESS);
         assertEquals(savedAttempt.getGatewayErrorCode(), gatewayErrorCode);
         assertEquals(savedAttempt.getRequestedAmount().compareTo(amount), 0);
     }
@@ -187,7 +185,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(savedAttempt.getPaymentId(), payment.getId());
         assertEquals(savedAttempt.getAccountId(), accountId);
         assertEquals(savedAttempt.getInvoiceId(), invoiceId);
-        assertEquals(savedAttempt.getPaymentStatus(), PaymentStatus.UNKNOWN);
+        assertEquals(savedAttempt.getProcessingStatus(), PaymentStatus.UNKNOWN);
 
         final List<PaymentModelDao> payments = paymentDao.getPaymentsForInvoice(invoiceId, internalCallContext);
         assertEquals(payments.size(), 1);
@@ -208,7 +206,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(savedAttempt.getPaymentId(), payment.getId());
         assertEquals(savedAttempt.getAccountId(), accountId);
         assertEquals(savedAttempt.getInvoiceId(), invoiceId);
-        assertEquals(savedAttempt.getPaymentStatus(), PaymentStatus.UNKNOWN);
+        assertEquals(savedAttempt.getProcessingStatus(), PaymentStatus.UNKNOWN);
 
     }
 
@@ -258,7 +256,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(savedAttempt1.getPaymentId(), payment.getId());
         assertEquals(savedAttempt1.getAccountId(), accountId);
         assertEquals(savedAttempt1.getInvoiceId(), invoiceId);
-        assertEquals(savedAttempt1.getPaymentStatus(), PaymentStatus.UNKNOWN);
+        assertEquals(savedAttempt1.getProcessingStatus(), PaymentStatus.UNKNOWN);
         assertEquals(savedAttempt1.getGatewayErrorCode(), null);
         assertEquals(savedAttempt1.getGatewayErrorMsg(), null);
         assertEquals(savedAttempt1.getRequestedAmount().compareTo(amount), 0);
@@ -267,7 +265,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(savedAttempt2.getPaymentId(), payment.getId());
         assertEquals(savedAttempt2.getAccountId(), accountId);
         assertEquals(savedAttempt2.getInvoiceId(), invoiceId);
-        assertEquals(savedAttempt2.getPaymentStatus(), PaymentStatus.UNKNOWN);
+        assertEquals(savedAttempt2.getProcessingStatus(), PaymentStatus.UNKNOWN);
         assertEquals(savedAttempt2.getGatewayErrorCode(), null);
         assertEquals(savedAttempt2.getGatewayErrorMsg(), null);
         assertEquals(savedAttempt2.getRequestedAmount().compareTo(newAmount), 0);
diff --git a/payment/src/test/java/com/ning/billing/payment/glue/PaymentTestModuleWithMocks.java b/payment/src/test/java/com/ning/billing/payment/glue/PaymentTestModuleWithMocks.java
index 8eb910c..a01dda5 100644
--- a/payment/src/test/java/com/ning/billing/payment/glue/PaymentTestModuleWithMocks.java
+++ b/payment/src/test/java/com/ning/billing/payment/glue/PaymentTestModuleWithMocks.java
@@ -45,6 +45,7 @@ import com.ning.billing.util.svcapi.account.AccountInternalApi;
 import com.ning.billing.util.svcapi.tag.TagInternalApi;
 import com.ning.billing.util.tag.Tag;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
 public class PaymentTestModuleWithMocks extends PaymentModule {
@@ -93,7 +94,7 @@ public class PaymentTestModuleWithMocks extends PaymentModule {
 
         final TagInternalApi tagUserApi = Mockito.mock(TagInternalApi.class);
         bind(TagInternalApi.class).toInstance(tagUserApi);
-        Mockito.when(tagUserApi.getTags(Mockito.<UUID>any(), Mockito.<ObjectType>any(), Mockito.<InternalTenantContext>any())).thenReturn(ImmutableMap.<String, Tag>of());
+        Mockito.when(tagUserApi.getTags(Mockito.<UUID>any(), Mockito.<ObjectType>any(), Mockito.<InternalTenantContext>any())).thenReturn(ImmutableList.<Tag>of());
 
         bind(GlobalLocker.class).to(MockGlobalLocker.class).asEagerSingleton();
     }
diff --git a/payment/src/test/java/com/ning/billing/payment/MockInvoice.java b/payment/src/test/java/com/ning/billing/payment/MockInvoice.java
index 2cde1b1..6aaac26 100644
--- a/payment/src/test/java/com/ning/billing/payment/MockInvoice.java
+++ b/payment/src/test/java/com/ning/billing/payment/MockInvoice.java
@@ -16,12 +16,14 @@
 
 package com.ning.billing.payment;
 
-import javax.annotation.Nullable;
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
+
 import org.joda.time.LocalDate;
 
 import com.ning.billing.catalog.api.Currency;
@@ -64,7 +66,7 @@ public class MockInvoice extends EntityBase implements Invoice {
     }
 
     @Override
-    public boolean addInvoiceItems(final List<InvoiceItem> items) {
+    public boolean addInvoiceItems(final Collection<InvoiceItem> items) {
         return this.invoiceItems.addAll(items);
     }
 
@@ -95,7 +97,7 @@ public class MockInvoice extends EntityBase implements Invoice {
     }
 
     @Override
-    public boolean addPayments(final List<InvoicePayment> payments) {
+    public boolean addPayments(final Collection<InvoicePayment> payments) {
         return this.payments.addAll(payments);
     }
 

pom.xml 1(+0 -1)

diff --git a/pom.xml b/pom.xml
index ddd39cd..c5d52cb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -322,7 +322,6 @@
                 <groupId>org.antlr</groupId>
                 <artifactId>stringtemplate</artifactId>
                 <version>3.2.1</version>
-                <scope>runtime</scope>
             </dependency>
             <dependency>
                 <groupId>org.jdbi</groupId>
diff --git a/server/src/main/java/com/ning/billing/server/modules/DBIProvider.java b/server/src/main/java/com/ning/billing/server/modules/DBIProvider.java
new file mode 100644
index 0000000..5d988df
--- /dev/null
+++ b/server/src/main/java/com/ning/billing/server/modules/DBIProvider.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2010-2012 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.server.modules;
+
+import java.util.concurrent.TimeUnit;
+
+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;
+import com.ning.billing.util.dao.EnumArgumentFactory;
+import com.ning.billing.util.dao.LocalDateArgumentFactory;
+import com.ning.billing.util.dao.UUIDArgumentFactory;
+import com.ning.billing.util.dao.UuidMapper;
+import com.ning.jetty.jdbi.config.DaoConfig;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.jolbox.bonecp.BoneCPConfig;
+import com.jolbox.bonecp.BoneCPDataSource;
+import com.yammer.metrics.core.MetricsRegistry;
+import com.yammer.metrics.jdbi.InstrumentedTimingCollector;
+import com.yammer.metrics.jdbi.strategies.BasicSqlNameStrategy;
+
+public class DBIProvider implements Provider<DBI> {
+
+    private static final Logger logger = LoggerFactory.getLogger(DBIProvider.class);
+
+    private final MetricsRegistry metricsRegistry;
+    private final DaoConfig config;
+    private SQLLog sqlLog;
+
+    @Inject
+    public DBIProvider(final MetricsRegistry metricsRegistry, final DaoConfig config) {
+        this.metricsRegistry = metricsRegistry;
+        this.config = config;
+    }
+
+    @Inject(optional = true)
+    public void setSqlLog(final SQLLog sqlLog) {
+        this.sqlLog = sqlLog;
+    }
+
+    @Override
+    public DBI get() {
+        final BoneCPConfig dbConfig = new BoneCPConfig();
+        dbConfig.setJdbcUrl(config.getJdbcUrl());
+        dbConfig.setUsername(config.getUsername());
+        dbConfig.setPassword(config.getPassword());
+        dbConfig.setMinConnectionsPerPartition(config.getMinIdle());
+        dbConfig.setMaxConnectionsPerPartition(config.getMaxActive());
+        dbConfig.setConnectionTimeout(config.getConnectionTimeout().getPeriod(), config.getConnectionTimeout().getUnit());
+        dbConfig.setIdleMaxAge(config.getIdleMaxAge().getPeriod(), config.getIdleMaxAge().getUnit());
+        dbConfig.setMaxConnectionAge(config.getMaxConnectionAge().getPeriod(), config.getMaxConnectionAge().getUnit());
+        dbConfig.setIdleConnectionTestPeriod(config.getIdleConnectionTestPeriod().getPeriod(), config.getIdleConnectionTestPeriod().getUnit());
+        dbConfig.setPartitionCount(1);
+        dbConfig.setDefaultTransactionIsolation("READ_COMMITTED");
+        dbConfig.setDisableJMX(false);
+
+        final BoneCPDataSource ds = new BoneCPDataSource(dbConfig);
+        final DBI dbi = new DBI(ds);
+        dbi.registerArgumentFactory(new UUIDArgumentFactory());
+        dbi.registerArgumentFactory(new DateTimeZoneArgumentFactory());
+        dbi.registerArgumentFactory(new DateTimeArgumentFactory());
+        dbi.registerArgumentFactory(new LocalDateArgumentFactory());
+        dbi.registerArgumentFactory(new EnumArgumentFactory());
+        dbi.registerMapper(new UuidMapper());
+
+        if (sqlLog != null) {
+            dbi.setSQLLog(sqlLog);
+        }
+
+        if (config.getTransactionHandlerClass() != null) {
+            logger.info("Using " + config.getTransactionHandlerClass() + " as a transaction handler class");
+            try {
+                dbi.setTransactionHandler((TransactionHandler) Class.forName(config.getTransactionHandlerClass()).newInstance());
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        final BasicSqlNameStrategy basicSqlNameStrategy = new BasicSqlNameStrategy();
+        final TimingCollector timingCollector = new InstrumentedTimingCollector(metricsRegistry, basicSqlNameStrategy, TimeUnit.MILLISECONDS, TimeUnit.SECONDS);
+        dbi.setTimingCollector(timingCollector);
+
+        return dbi;
+    }
+}
diff --git a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
index 5a25746..188644c 100644
--- a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
+++ b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
@@ -16,7 +16,6 @@
 
 package com.ning.billing.server.modules;
 
-
 import org.skife.jdbi.v2.DBI;
 import org.skife.jdbi.v2.IDBI;
 
@@ -55,11 +54,11 @@ import com.ning.billing.util.glue.ExportModule;
 import com.ning.billing.util.glue.GlobalLockerModule;
 import com.ning.billing.util.glue.NotificationQueueModule;
 import com.ning.billing.util.glue.TagStoreModule;
-import com.ning.jetty.jdbi.guice.providers.DBIProvider;
 
 import com.google.inject.AbstractModule;
 
 public class KillbillServerModule extends AbstractModule {
+
     @Override
     protected void configure() {
         configureDao();
@@ -68,17 +67,15 @@ public class KillbillServerModule extends AbstractModule {
         configurePushNotification();
     }
 
-
     protected void configurePushNotification() {
         bind(ServerService.class).to(DefaultServerService.class).asEagerSingleton();
         bind(PushNotificationListener.class).asEagerSingleton();
     }
 
-
     protected void configureDao() {
         // Load mysql driver if needed
         try {
-            Class.forName ("com.mysql.jdbc.Driver").newInstance ();
+            Class.forName("com.mysql.jdbc.Driver").newInstance();
         } catch (final Exception ignore) {
         }
         bind(IDBI.class).to(DBI.class).asEagerSingleton();
@@ -107,7 +104,6 @@ public class KillbillServerModule extends AbstractModule {
         install(new EmailModule());
         install(new GlobalLockerModule());
         install(new CustomFieldModule());
-        install(new TagStoreModule());
         install(new AuditModule());
         install(new CatalogModule());
         install(new BusModule());
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestAccountEmail.java b/server/src/test/java/com/ning/billing/jaxrs/TestAccountEmail.java
index 581053a..cf28890 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestAccountEmail.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestAccountEmail.java
@@ -70,8 +70,8 @@ public class TestAccountEmail extends TestJaxrsBase {
         Assert.assertEquals(fourthEmails.get(0).getAccountId(), accountId);
         Assert.assertEquals(fourthEmails.get(0).getEmail(), email2);
 
-        // Try to add the same email
-        addEmailToAccount(accountId, accountEmailJson2);
+        // Try to add the same email -- that works because we removed the unique constraints for soft deletion.
+        // addEmailToAccount(accountId, accountEmailJson2);
         Assert.assertEquals(getEmailsForAccount(accountId), fourthEmails);
     }
 }
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestAccountTimeline.java b/server/src/test/java/com/ning/billing/jaxrs/TestAccountTimeline.java
index eba983c..7d369b8 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestAccountTimeline.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestAccountTimeline.java
@@ -40,7 +40,7 @@ import com.ning.billing.util.audit.ChangeType;
 public class TestAccountTimeline extends TestJaxrsBase {
 
     private static final String PAYMENT_REQUEST_PROCESSOR = "PaymentRequestProcessor";
-    private static final String TRANSITION = "Transition";
+    private static final String TRANSITION = "SubscriptionTransition";
 
     @Test(groups = "slow")
     public void testAccountTimeline() throws Exception {
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
index d6b1a68..d6ae88a 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
@@ -16,8 +16,6 @@
 
 package com.ning.billing.jaxrs;
 
-import static org.testng.Assert.assertNotNull;
-
 import java.io.IOException;
 import java.net.URL;
 import java.util.EventListener;
@@ -40,7 +38,6 @@ import com.ning.billing.analytics.setup.AnalyticsModule;
 import com.ning.billing.api.TestApiListener;
 import com.ning.billing.beatrix.glue.BeatrixModule;
 import com.ning.billing.catalog.glue.CatalogModule;
-import com.ning.billing.util.config.PaymentConfig;
 import com.ning.billing.dbi.DBIProvider;
 import com.ning.billing.dbi.DbiConfig;
 import com.ning.billing.dbi.MysqlTestingHelper;
@@ -57,6 +54,7 @@ import com.ning.billing.server.modules.KillbillServerModule;
 import com.ning.billing.tenant.glue.TenantModule;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.config.PaymentConfig;
 import com.ning.billing.util.email.EmailModule;
 import com.ning.billing.util.email.templates.TemplateModule;
 import com.ning.billing.util.glue.AuditModule;
@@ -78,6 +76,8 @@ import com.fasterxml.jackson.datatype.joda.JodaModule;
 import com.google.common.collect.ImmutableList;
 import com.google.inject.Module;
 
+import static org.testng.Assert.assertNotNull;
+
 public class TestJaxrsBase extends KillbillClient {
 
     protected static final String PLUGIN_NAME = "noop";
diff --git a/server/src/test/java/com/ning/billing/server/security/TestKillbillJdbcRealm.java b/server/src/test/java/com/ning/billing/server/security/TestKillbillJdbcRealm.java
index b3ac99d..7f1fc99 100644
--- a/server/src/test/java/com/ning/billing/server/security/TestKillbillJdbcRealm.java
+++ b/server/src/test/java/com/ning/billing/server/security/TestKillbillJdbcRealm.java
@@ -24,7 +24,6 @@ import org.apache.shiro.authc.UsernamePasswordToken;
 import org.apache.shiro.mgt.DefaultSecurityManager;
 import org.apache.shiro.mgt.SecurityManager;
 import org.apache.shiro.subject.support.DelegatingSubject;
-import org.mockito.Mockito;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -33,7 +32,7 @@ import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.server.ServerTestSuiteWithEmbeddedDB;
 import com.ning.billing.tenant.api.DefaultTenant;
 import com.ning.billing.tenant.dao.DefaultTenantDao;
-import com.ning.billing.util.svcsapi.bus.InternalBus;
+import com.ning.billing.tenant.dao.TenantModelDao;
 
 import com.jolbox.bonecp.BoneCPConfig;
 import com.jolbox.bonecp.BoneCPDataSource;
@@ -46,10 +45,10 @@ public class TestKillbillJdbcRealm extends ServerTestSuiteWithEmbeddedDB {
     @BeforeMethod(groups = "slow")
     public void setUp() throws Exception {
         // Create the tenant
-        final DefaultTenantDao tenantDao = new DefaultTenantDao(getMysqlTestingHelper().getDBI(), Mockito.mock(InternalBus.class));
+        final DefaultTenantDao tenantDao = new DefaultTenantDao(getMysqlTestingHelper().getDBI());
         tenant = new DefaultTenant(UUID.randomUUID(), null, null, UUID.randomUUID().toString(),
                                    UUID.randomUUID().toString(), UUID.randomUUID().toString());
-        tenantDao.create(tenant, internalCallContext);
+        tenantDao.create(new TenantModelDao(tenant), internalCallContext);
 
         // Setup the security manager
         final BoneCPConfig dbConfig = new BoneCPConfig();
diff --git a/tenant/src/main/java/com/ning/billing/tenant/api/DefaultTenant.java b/tenant/src/main/java/com/ning/billing/tenant/api/DefaultTenant.java
index fbc405a..e054a76 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/api/DefaultTenant.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/api/DefaultTenant.java
@@ -22,6 +22,7 @@ import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
 
+import com.ning.billing.tenant.dao.TenantModelDao;
 import com.ning.billing.util.entity.EntityBase;
 
 public class DefaultTenant extends EntityBase implements Tenant {
@@ -58,6 +59,11 @@ public class DefaultTenant extends EntityBase implements Tenant {
         this.apiSecret = apiSecret;
     }
 
+    public DefaultTenant(final TenantModelDao tenant) {
+        this(tenant.getId(), tenant.getCreatedDate(), tenant.getUpdatedDate(), tenant.getExternalKey(), tenant.getApiKey(),
+             tenant.getApiSecret());
+    }
+
     @Override
     public String getExternalKey() {
         return externalKey;
diff --git a/tenant/src/main/java/com/ning/billing/tenant/api/user/DefaultTenantUserApi.java b/tenant/src/main/java/com/ning/billing/tenant/api/user/DefaultTenantUserApi.java
index 5c00812..e0a19de 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/api/user/DefaultTenantUserApi.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/api/user/DefaultTenantUserApi.java
@@ -26,12 +26,12 @@ import com.ning.billing.tenant.api.TenantApiException;
 import com.ning.billing.tenant.api.TenantData;
 import com.ning.billing.tenant.api.TenantUserApi;
 import com.ning.billing.tenant.dao.TenantDao;
+import com.ning.billing.tenant.dao.TenantModelDao;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.callcontext.TenantContext;
-import com.ning.billing.util.entity.EntityPersistenceException;
 
 import com.google.inject.Inject;
 
@@ -46,14 +46,13 @@ public class DefaultTenantUserApi implements TenantUserApi {
         this.internalCallContextFactory = internalCallContextFactory;
     }
 
-
     @Override
     public Tenant createTenant(final TenantData data, final CallContext context) throws TenantApiException {
         final Tenant tenant = new DefaultTenant(data);
 
         try {
-            tenantDao.create(tenant, internalCallContextFactory.createInternalCallContext(context));
-        } catch (final EntityPersistenceException e) {
+            tenantDao.create(new TenantModelDao(tenant), internalCallContextFactory.createInternalCallContext(context));
+        } catch (final TenantApiException e) {
             throw new TenantApiException(e, ErrorCode.TENANT_CREATION_FAILED);
         }
 
@@ -62,36 +61,35 @@ public class DefaultTenantUserApi implements TenantUserApi {
 
     @Override
     public Tenant getTenantByApiKey(final String key) throws TenantApiException {
-        final Tenant tenant = tenantDao.getTenantByApiKey(key);
+        final TenantModelDao tenant = tenantDao.getTenantByApiKey(key);
         if (tenant == null) {
             throw new TenantApiException(ErrorCode.TENANT_DOES_NOT_EXIST_FOR_API_KEY, key);
         }
-        return tenant;
+        return new DefaultTenant(tenant);
     }
 
     @Override
     public Tenant getTenantById(final UUID id) throws TenantApiException {
         // TODO - API cleanup?
-        final Tenant tenant = tenantDao.getById(id, new InternalTenantContext(null, null));
+        final TenantModelDao tenant = tenantDao.getById(id, new InternalTenantContext(null, null));
         if (tenant == null) {
             throw new TenantApiException(ErrorCode.TENANT_DOES_NOT_EXIST_FOR_ID, id);
         }
-        return tenant;
+        return new DefaultTenant(tenant);
     }
 
     @Override
     public List<String> getTenantValueForKey(final String key, final TenantContext context)
             throws TenantApiException {
-        final InternalTenantContext internalContext =  internalCallContextFactory.createInternalTenantContext(context);
-        final List<String> value = tenantDao.getTenantValueForKey(key, internalContext);
-        return value;
+        final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
+        return tenantDao.getTenantValueForKey(key, internalContext);
     }
 
     @Override
     public void addTenantKeyValue(final String key, final String value, final CallContext context)
             throws TenantApiException {
 
-        final InternalCallContext internalContext =  internalCallContextFactory.createInternalCallContext(context);
+        final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(context);
         // TODO Figure out the exact verification if nay
         /*
         final Tenant tenant = tenantDao.getById(context.getTenantId(), internalContext);
@@ -102,7 +100,6 @@ public class DefaultTenantUserApi implements TenantUserApi {
         tenantDao.addTenantKeyValue(key, value, internalContext);
     }
 
-
     @Override
     public void deleteTenantKey(final String key, final CallContext context)
             throws TenantApiException {
@@ -112,7 +109,7 @@ public class DefaultTenantUserApi implements TenantUserApi {
             throw new TenantApiException(ErrorCode.TENANT_DOES_NOT_EXIST_FOR_ID, tenantId);
         }
         */
-        final InternalCallContext internalContext =  internalCallContextFactory.createInternalCallContext(context);
+        final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(context);
         tenantDao.deleteTenantKey(key, internalContext);
     }
 }
diff --git a/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java b/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java
index f17d01b..959d8bd 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java
@@ -19,23 +19,26 @@ package com.ning.billing.tenant.dao;
 import java.util.List;
 import java.util.UUID;
 
-import javax.annotation.Nullable;
-
 import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.codec.Base64;
 import org.apache.shiro.crypto.RandomNumberGenerator;
 import org.apache.shiro.crypto.SecureRandomNumberGenerator;
-import org.apache.shiro.crypto.hash.Sha256Hash;
+import org.apache.shiro.crypto.hash.SimpleHash;
 import org.apache.shiro.util.ByteSource;
 import org.skife.jdbi.v2.IDBI;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
+import com.ning.billing.ErrorCode;
 import com.ning.billing.tenant.api.Tenant;
-import com.ning.billing.tenant.api.TenantKV;
+import com.ning.billing.tenant.api.TenantApiException;
+import com.ning.billing.tenant.security.KillbillCredentialsMatcher;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.entity.EntityPersistenceException;
-import com.ning.billing.util.svcsapi.bus.InternalBus;
+import com.ning.billing.util.entity.dao.EntityDaoBase;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
@@ -43,88 +46,106 @@ import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
-public class DefaultTenantDao implements TenantDao {
-
-    private static final Logger log = LoggerFactory.getLogger(DefaultTenantDao.class);
+public class DefaultTenantDao extends EntityDaoBase<TenantModelDao, Tenant, TenantApiException> implements TenantDao {
 
     private final RandomNumberGenerator rng = new SecureRandomNumberGenerator();
 
-    private final TenantSqlDao tenantSqlDao;
-    private final TenantKVSqlDao tenantKVSqlDao;
-    private final InternalBus eventBus;
-
     @Inject
-    public DefaultTenantDao(final IDBI dbi, final InternalBus eventBus) {
-        this.eventBus = eventBus;
-        this.tenantSqlDao = dbi.onDemand(TenantSqlDao.class);
-        this.tenantKVSqlDao = dbi.onDemand(TenantKVSqlDao.class);
-    }
-
-    @Override
-    public Tenant getTenantByApiKey(final String apiKey) {
-        return tenantSqlDao.getByApiKey(apiKey);
-    }
-
-    @Override
-    public void create(final Tenant entity, final InternalCallContext context) throws EntityPersistenceException {
-        // Create the salt and password
-        final ByteSource salt = rng.nextBytes();
-        // Hash the plain-text password with the random salt and multiple
-        // iterations and then Base64-encode the value (requires less space than Hex):
-        final String hashedPasswordBase64 = new Sha256Hash(entity.getApiSecret(), salt, 1024).toBase64();
-
-        tenantSqlDao.create(entity, hashedPasswordBase64, salt.toBase64(), context);
+    public DefaultTenantDao(final IDBI dbi) {
+        super(new EntitySqlDaoTransactionalJdbiWrapper(dbi), TenantSqlDao.class);
     }
 
     @Override
-    public Long getRecordId(final UUID id, final InternalTenantContext context) {
-        return tenantSqlDao.getRecordId(id.toString(), context);
+    protected TenantApiException generateAlreadyExistsException(final TenantModelDao entity, final InternalCallContext context) {
+        return new TenantApiException(ErrorCode.TENANT_ALREADY_EXISTS, entity.getExternalKey());
     }
 
     @Override
-    public Tenant getById(final UUID id, final InternalTenantContext context) {
-        return tenantSqlDao.getById(id.toString(), context);
+    public TenantModelDao getTenantByApiKey(final String apiKey) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<TenantModelDao>() {
+            @Override
+            public TenantModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(TenantSqlDao.class).getByApiKey(apiKey);
+            }
+        });
     }
 
     @Override
-    public List<Tenant> get(final InternalTenantContext context) {
-        return tenantSqlDao.get(context);
-    }
+    public void create(final TenantModelDao entity, final InternalCallContext context) throws TenantApiException {
+        // Create the salt and password
+        final ByteSource salt = rng.nextBytes();
+        // Hash the plain-text password with the random salt and multiple iterations and then Base64-encode the value (requires less space than Hex)
+        final String hashedPasswordBase64 = new SimpleHash(KillbillCredentialsMatcher.HASH_ALGORITHM_NAME,
+                                                           entity.getApiSecret(), salt, KillbillCredentialsMatcher.HASH_ITERATIONS).toBase64();
 
-    @Override
-    public void test(final InternalTenantContext context) {
-        tenantSqlDao.test(context);
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final TenantModelDao tenantModelDaoWithSecret = new TenantModelDao(entity.getId(), context.getCreatedDate(), context.getUpdatedDate(),
+                                                                                   entity.getExternalKey(), entity.getApiKey(),
+                                                                                   hashedPasswordBase64, salt.toBase64());
+                entitySqlDaoWrapperFactory.become(TenantSqlDao.class).create(tenantModelDaoWithSecret, context);
+                return null;
+            }
+        });
     }
 
     @VisibleForTesting
     AuthenticationInfo getAuthenticationInfoForTenant(final UUID id) {
-        return tenantSqlDao.getSecrets(id.toString()).toAuthenticationInfo();
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<AuthenticationInfo>() {
+            @Override
+            public AuthenticationInfo inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final TenantModelDao tenantModelDao = entitySqlDaoWrapperFactory.become(TenantSqlDao.class).getSecrets(id.toString());
+
+                final SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(tenantModelDao.getApiKey(), tenantModelDao.getApiSecret().toCharArray(), getClass().getSimpleName());
+                authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(Base64.decode(tenantModelDao.getApiSalt())));
+
+                return authenticationInfo;
+            }
+        });
     }
 
     @Override
     public List<String> getTenantValueForKey(final String key, final InternalTenantContext context) {
-        final List<TenantKV> tenantKV = tenantKVSqlDao.getTenantValueForKey(key, context.getTenantRecordId());
-        return ImmutableList.copyOf(Collections2.transform(tenantKV, new Function<TenantKV, String>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<String>>() {
             @Override
-            @Nullable
-            public String apply(final @Nullable TenantKV in) {
-                return in.getValue();
+            public List<String> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final List<TenantKVModelDao> tenantKV = entitySqlDaoWrapperFactory.become(TenantKVSqlDao.class).getTenantValueForKey(key, context);
+                return ImmutableList.copyOf(Collections2.transform(tenantKV, new Function<TenantKVModelDao, String>() {
+                    @Override
+                    public String apply(final TenantKVModelDao in) {
+                        return in.getTenantValue();
+                    }
+                }));
             }
-        }));
+        });
     }
 
     @Override
     public void addTenantKeyValue(final String key, final String value, final InternalCallContext context) {
-        tenantKVSqlDao.insertTenantKeyValue(UUID.randomUUID().toString(), key, value, context.getTenantRecordId(), context);
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final TenantKVModelDao tenantKVModelDao = new TenantKVModelDao(UUID.randomUUID(), context.getCreatedDate(), context.getUpdatedDate(), key, value);
+                entitySqlDaoWrapperFactory.become(TenantKVSqlDao.class).create(tenantKVModelDao, context);
+                return null;
+            }
+        });
     }
 
     @Override
     public void deleteTenantKey(final String key, final InternalCallContext context) {
-        tenantKVSqlDao.deleteTenantKey(key, context.getTenantRecordId());
-    }
-
-    @Override
-    public Tenant getByRecordId(final Long recordId, final InternalTenantContext context) {
-        throw new UnsupportedOperationException();
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final List<TenantKVModelDao> tenantKVs = entitySqlDaoWrapperFactory.become(TenantKVSqlDao.class).getTenantValueForKey(key, context);
+                for (TenantKVModelDao cur : tenantKVs) {
+                    if (cur.getTenantKey().equals(key)) {
+                        entitySqlDaoWrapperFactory.become(TenantKVSqlDao.class).markTenantKeyAsDeleted(cur.getId().toString(), context);
+                    }
+                }
+                return null;
+            }
+        });
     }
 }
diff --git a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantDao.java b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantDao.java
index 0d0add0..6c2f339 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantDao.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantDao.java
@@ -19,13 +19,14 @@ package com.ning.billing.tenant.dao;
 import java.util.List;
 
 import com.ning.billing.tenant.api.Tenant;
+import com.ning.billing.tenant.api.TenantApiException;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.entity.dao.EntityDao;
 
-public interface TenantDao extends EntityDao<Tenant> {
+public interface TenantDao extends EntityDao<TenantModelDao, Tenant, TenantApiException> {
 
-    public Tenant getTenantByApiKey(final String key);
+    public TenantModelDao getTenantByApiKey(final String key);
 
     public List<String> getTenantValueForKey(final String key, final InternalTenantContext context);
 
diff --git a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVModelDao.java b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVModelDao.java
new file mode 100644
index 0000000..362bbbe
--- /dev/null
+++ b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVModelDao.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2010-2012 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.tenant.dao;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.tenant.api.TenantKV;
+import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.dao.EntityModelDao;
+
+public class TenantKVModelDao extends EntityBase implements EntityModelDao<TenantKV> {
+
+    private String tenantKey;
+    private String tenantValue;
+
+    private Boolean isActive;
+
+    public TenantKVModelDao() { /* For the DAO mapper */ }
+
+    public TenantKVModelDao(final UUID id, final DateTime createdDate, final DateTime updatedDate, final String key, final String value) {
+        super(id, createdDate, updatedDate);
+        this.tenantKey = key;
+        this.tenantValue = value;
+        this.isActive = true;
+    }
+
+    public String getTenantKey() {
+        return tenantKey;
+    }
+
+    public String getTenantValue() {
+        return tenantValue;
+    }
+
+    public Boolean getIsActive() {
+        return isActive;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("TenantKVModelDao");
+        sb.append("{key='").append(tenantKey).append('\'');
+        sb.append(", value='").append(tenantValue).append('\'');
+        sb.append(", isActive=").append(isActive);
+        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 TenantKVModelDao that = (TenantKVModelDao) o;
+
+        if (isActive != null ? !isActive.equals(that.isActive) : that.isActive != null) {
+            return false;
+        }
+        if (tenantKey != null ? !tenantKey.equals(that.tenantKey) : that.tenantKey != null) {
+            return false;
+        }
+        if (tenantValue != null ? !tenantValue.equals(that.tenantValue) : that.tenantValue != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (tenantKey != null ? tenantKey.hashCode() : 0);
+        result = 31 * result + (tenantValue != null ? tenantValue.hashCode() : 0);
+        result = 31 * result + (isActive != null ? isActive.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.TENANT_KVS;
+    }
+}
diff --git a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVSqlDao.java b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVSqlDao.java
index ae58d87..0ff9119 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVSqlDao.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVSqlDao.java
@@ -13,55 +13,33 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
+
 package com.ning.billing.tenant.dao;
 
-import java.sql.ResultSet;
-import java.sql.SQLException;
 import java.util.List;
-import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 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.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
-import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
-import com.ning.billing.tenant.api.DefaultTenantKV;
 import com.ning.billing.tenant.api.TenantKV;
-import com.ning.billing.tenant.dao.TenantKVSqlDao.TenantKVMapper;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.MapperBase;
-import com.ning.billing.util.dao.UuidMapper;
+import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.entity.dao.Audited;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 
-@ExternalizedSqlViaStringTemplate3
-@RegisterMapper({UuidMapper.class, TenantKVMapper.class})
-public interface TenantKVSqlDao extends Transactional<TenantKVSqlDao> {
+@EntitySqlDaoStringTemplate
+public interface TenantKVSqlDao extends EntitySqlDao<TenantKVModelDao, TenantKV> {
 
     @SqlQuery
-    public List<TenantKV> getTenantValueForKey(@Bind("key") final String key, @Bind("tenantRecordId") Long tenantRecordId);
-
-    @SqlUpdate
-    public void insertTenantKeyValue(@Bind("id") String id, @Bind("key") final String key, @Bind("value") final String value, @Bind("tenantRecordId") Long tenantRecordId, @InternalTenantContextBinder final InternalCallContext context);
+    public List<TenantKVModelDao> getTenantValueForKey(@Bind("tenantKey") final String key,
+                                                       @BindBean final InternalTenantContext context);
 
     @SqlUpdate
-    public void deleteTenantKey(@Bind("key") final String key, @Bind("tenantRecordId") Long tenantRecordId);
-
-
-    public class TenantKVMapper extends MapperBase implements ResultSetMapper<TenantKV> {
-
-        @Override
-        public TenantKV map(final int index, final ResultSet result, final StatementContext context) throws SQLException {
-            final UUID id = getUUID(result, "id");
-            final String key = result.getString("t_key");
-            final String value = result.getString("t_value");
-            final DateTime createdDate = getDateTime(result, "created_date");
-            final DateTime updatedDate = getDateTime(result, "updated_date");
-            return new DefaultTenantKV(id, key, value, createdDate, updatedDate);
-        }
-    }
+    @Audited(ChangeType.DELETE)
+    public void markTenantKeyAsDeleted(@Bind("id")final String id,
+                                       @BindBean final InternalCallContext context);
 }
diff --git a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantSqlDao.java b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantSqlDao.java
index 8ce298b..6f7b6a2 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantSqlDao.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantSqlDao.java
@@ -18,34 +18,17 @@ package com.ning.billing.tenant.dao;
 
 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.Mapper;
-import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 
 import com.ning.billing.tenant.api.Tenant;
-import com.ning.billing.util.callcontext.InternalCallContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.UuidMapper;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 
-@ExternalizedSqlViaStringTemplate3
-@RegisterMapper({UuidMapper.class, TenantMapper.class})
-public interface TenantSqlDao extends EntitySqlDao<Tenant>, Transactional<TenantSqlDao>, Transmogrifier {
+@EntitySqlDaoStringTemplate
+public interface TenantSqlDao extends EntitySqlDao<TenantModelDao, Tenant> {
 
     @SqlQuery
-    public Tenant getByApiKey(@Bind("apiKey") final String apiKey);
-
-    @SqlUpdate
-    public void create(@TenantBinder final Tenant tenant,
-                       @Bind("apiSecret") final String apiSecret,
-                       @Bind("apiSalt") final String apiSalt,
-                       @InternalTenantContextBinder final InternalCallContext context);
+    public TenantModelDao getByApiKey(@Bind("apiKey") final String apiKey);
 
     @SqlQuery
-    @Mapper(TenantSecretsMapper.class)
-    public TenantSecrets getSecrets(@Bind("id") final String id);
-
+    public TenantModelDao getSecrets(@Bind("id") final String id);
 }
diff --git a/tenant/src/main/java/com/ning/billing/tenant/security/KillbillCredentialsMatcher.java b/tenant/src/main/java/com/ning/billing/tenant/security/KillbillCredentialsMatcher.java
index eb91eec..9c5dbbb 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/security/KillbillCredentialsMatcher.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/security/KillbillCredentialsMatcher.java
@@ -18,19 +18,22 @@ package com.ning.billing.tenant.security;
 
 import org.apache.shiro.authc.credential.CredentialsMatcher;
 import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
-import org.apache.shiro.crypto.hash.Sha256Hash;
+import org.apache.shiro.crypto.hash.Sha512Hash;
 
 public class KillbillCredentialsMatcher {
 
-    private KillbillCredentialsMatcher() {
-    }
+    // See http://www.stormpath.com/blog/strong-password-hashing-apache-shiro and https://issues.apache.org/jira/browse/SHIRO-290
+    public static final String HASH_ALGORITHM_NAME = Sha512Hash.ALGORITHM_NAME;
+    public static final int HASH_ITERATIONS = 500000;
+
+    private KillbillCredentialsMatcher() {}
 
     public static CredentialsMatcher getCredentialsMatcher() {
         // This needs to be in sync with DefaultTenantDao
-        final HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(Sha256Hash.ALGORITHM_NAME);
+        final HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(HASH_ALGORITHM_NAME);
         // base64 encoding, not hex
         credentialsMatcher.setStoredCredentialsHexEncoded(false);
-        credentialsMatcher.setHashIterations(1024);
+        credentialsMatcher.setHashIterations(HASH_ITERATIONS);
 
         return credentialsMatcher;
     }
diff --git a/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantKVSqlDao.sql.stg b/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantKVSqlDao.sql.stg
index ce66a1a..c42eaca 100644
--- a/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantKVSqlDao.sql.stg
+++ b/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantKVSqlDao.sql.stg
@@ -1,58 +1,48 @@
-group TenantKVSqlDao;
-
-
-tenantKVFields(prefix) ::= <<
-    <prefix>record_id,
-    <prefix>id,
-    <prefix>tenant_record_id,
-    <prefix>t_key,
-    <prefix>t_value,
-    <prefix>created_date,
-    <prefix>created_by,
-    <prefix>updated_date,
-    <prefix>updated_by
+group TenantKVSqlDao: EntitySqlDao;
+
+tableName() ::= "tenant_kvs"
+
+andCheckSoftDeletionWithComma(prefix) ::= "and <prefix>is_active"
+
+tableFields(prefix) ::= <<
+  <prefix>tenant_key
+, <prefix>tenant_value
+, <prefix>is_active
+, <prefix>created_date
+, <prefix>created_by
+, <prefix>updated_date
+, <prefix>updated_by
 >>
 
-insertTenantKeyValue() ::= <<
-   INSERT INTO tenant_kvs (
-      id
-      , tenant_record_id
-      , t_key
-      , t_value
-      , created_date
-      , created_by
-      , updated_date
-      , updated_by
-    ) VALUES (
-      :id
-      , :tenantRecordId
-      , :key
-      , :value
-      , :createdDate
-      , :userName
-      , :updatedDate
-      , :userName
-    );
+tableValues() ::= <<
+  :tenantKey
+, :tenantValue
+, :isActive
+, :createdDate
+, :createdBy
+, :updatedDate
+, :updatedBy
 >>
 
+accountRecordIdFieldWithComma(prefix) ::= ""
 
-getTenantValueForKey() ::= <<
-    SELECT <tenantKVFields()>
-    FROM tenant_kvs
-    WHERE
-        tenant_record_id = :tenantRecordId AND t_key = :key
-    ;
->>
+accountRecordIdValueWithComma() ::= ""
 
 
-deleteTenantKey() ::= <<
-    DELETE FROM tenant_kvs
-    WHERE
-        tenant_record_id = :tenantRecordId AND t_key = :key
-    ;
+getTenantValueForKey() ::= <<
+select
+  <allTableFields("t.")>
+from <tableName()> t
+where t.tenant_key = :tenantKey
+and  t.is_active
+<AND_CHECK_TENANT("t.")>
+;
 >>
 
-
-test() ::= <<
-    SELECT 1 FROM tenants;
+markTenantKeyAsDeleted() ::= <<
+update <tableName()> t
+set t.is_active = 0
+where t.id = :id
+<AND_CHECK_TENANT("t.")>
+;
 >>
diff --git a/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantSqlDao.sql.stg b/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantSqlDao.sql.stg
index d48780a..56c8986 100644
--- a/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantSqlDao.sql.stg
+++ b/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantSqlDao.sql.stg
@@ -1,63 +1,66 @@
-group TenantDaoSql;
-
-tenantFields(prefix) ::= <<
-    <prefix>record_id,
-    <prefix>id,
-    <prefix>external_key,
-    <prefix>api_key,
-    <prefix>created_date,
-    <prefix>created_by,
-    <prefix>updated_date,
-    <prefix>updated_by
->>
+group TenantDaoSql: EntitySqlDao;
 
-create() ::= <<
-    INSERT INTO tenants (
-        id
-      , external_key
-      , api_key
-      , api_secret
-      , api_salt
-      , created_date
-      , created_by
-      , updated_date
-      , updated_by
-    ) VALUES (
-        :id
-      , :externalKey
-      , :apiKey
-      , :apiSecret
-      , :apiSalt
-      , :createdDate
-      , :userName
-      , :updatedDate
-      , :userName
-    );
+tableName() ::= "tenants"
+
+/* Don't add api_secret and api_salt in these fields, we shouldn't need to retrieve them */
+tableFields(prefix) ::= <<
+  <prefix>external_key
+, <prefix>api_key
+, <prefix>created_date
+, <prefix>created_by
+, <prefix>updated_date
+, <prefix>updated_by
 >>
 
-get() ::= <<
-    SELECT <tenantFields()>
-    FROM tenants;
+tableValues() ::= <<
+  :externalKey
+, :apiKey
+, :createdDate
+, :createdBy
+, :updatedDate
+, :updatedBy
 >>
 
-getById() ::= <<
-    SELECT <tenantFields()>
-    FROM tenants
-    WHERE id = :id;
+/* No account_record_id field */
+accountRecordIdFieldWithComma(prefix) ::= ""
+accountRecordIdValueWithComma(prefix) ::= ""
+
+/* No tenant_record_id field */
+tenantRecordIdFieldWithComma(prefix) ::= ""
+tenantRecordIdValueWithComma(prefix) ::= ""
+CHECK_TENANT(prefix) ::= "1 = 1"
+
+/* Override default create call to include secrets */
+create() ::= <<
+insert into <tableName()> (
+  <idField()>
+, <tableFields()>
+, api_secret
+, api_salt
+)
+values (
+  <idValue()>
+, <tableValues()>
+, :apiSecret
+, :apiSalt
+)
+;
 >>
 
 getByApiKey() ::= <<
-    SELECT <tenantFields()>
-    FROM tenants
-    WHERE api_key = :apiKey;
+select
+  <allTableFields("t.")>
+from <tableName()> t
+where api_key = :apiKey
+;
 >>
 
 getSecrets() ::= <<
-    SELECT api_key, api_secret, api_salt
-    FROM tenants
-    WHERE id = :id;
->>
-
-test() ::= <<
-    SELECT 1 FROM tenants;
+select
+  <allTableFields("t.")>
+, t.api_secret
+, t.api_salt
+from <tableName()> t
+where <idField("t.")> = <idValue()>
+;
 >>
diff --git a/tenant/src/main/resources/com/ning/billing/tenant/ddl.sql b/tenant/src/main/resources/com/ning/billing/tenant/ddl.sql
index 4096d11..664a2e0 100644
--- a/tenant/src/main/resources/com/ning/billing/tenant/ddl.sql
+++ b/tenant/src/main/resources/com/ning/billing/tenant/ddl.sql
@@ -21,12 +21,13 @@ CREATE TABLE tenant_kvs (
    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
    id char(36) NOT NULL,
    tenant_record_id int(11) unsigned default null,
-   t_key varchar(64) NOT NULL,
-   t_value varchar(1024) NOT NULL,
+   tenant_key varchar(64) NOT NULL,
+   tenant_value varchar(1024) NOT NULL,
+   is_active bool DEFAULT 1,
    created_date datetime NOT NULL,
    created_by varchar(50) NOT NULL,
    updated_date datetime DEFAULT NULL,
    updated_by varchar(50) DEFAULT NULL,
    PRIMARY KEY(record_id)
 ) ENGINE=innodb;
-CREATE INDEX tenant_kvs_key ON tenant_kvs(tenant_record_id, t_key);
+CREATE INDEX tenant_kvs_key ON tenant_kvs(tenant_key);
diff --git a/tenant/src/test/java/com/ning/billing/tenant/dao/TestDefaultTenantDao.java b/tenant/src/test/java/com/ning/billing/tenant/dao/TestDefaultTenantDao.java
index f069f59..b5e95ed 100644
--- a/tenant/src/test/java/com/ning/billing/tenant/dao/TestDefaultTenantDao.java
+++ b/tenant/src/test/java/com/ning/billing/tenant/dao/TestDefaultTenantDao.java
@@ -22,24 +22,22 @@ import java.util.UUID;
 import org.apache.shiro.authc.AuthenticationInfo;
 import org.apache.shiro.authc.AuthenticationToken;
 import org.apache.shiro.authc.UsernamePasswordToken;
-import org.mockito.Mockito;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import com.ning.billing.tenant.TenantTestSuiteWithEmbeddedDb;
 import com.ning.billing.tenant.api.DefaultTenant;
 import com.ning.billing.tenant.security.KillbillCredentialsMatcher;
-import com.ning.billing.util.svcsapi.bus.InternalBus;
 
 public class TestDefaultTenantDao extends TenantTestSuiteWithEmbeddedDb {
 
     @Test(groups = "slow")
     public void testWeCanStoreAndMatchCredentials() throws Exception {
-        final DefaultTenantDao tenantDao = new DefaultTenantDao(getMysqlTestingHelper().getDBI(), Mockito.mock(InternalBus.class));
+        final DefaultTenantDao tenantDao = new DefaultTenantDao(getMysqlTestingHelper().getDBI());
 
         final DefaultTenant tenant = new DefaultTenant(UUID.randomUUID(), null, null, UUID.randomUUID().toString(),
-                UUID.randomUUID().toString(), UUID.randomUUID().toString());
-        tenantDao.create(tenant, internalCallContext);
+                                                       UUID.randomUUID().toString(), UUID.randomUUID().toString());
+        tenantDao.create(new TenantModelDao(tenant), internalCallContext);
 
         // Verify we can retrieve it
         Assert.assertEquals(tenantDao.getTenantByApiKey(tenant.getApiKey()).getId(), tenant.getId());
@@ -58,20 +56,19 @@ public class TestDefaultTenantDao extends TenantTestSuiteWithEmbeddedDb {
 
     @Test(groups = "slow")
     public void testTenantKeyValue() throws Exception {
-
-        final DefaultTenantDao tenantDao = new DefaultTenantDao(getMysqlTestingHelper().getDBI(), Mockito.mock(InternalBus.class));
+        final DefaultTenantDao tenantDao = new DefaultTenantDao(getMysqlTestingHelper().getDBI());
         final DefaultTenant tenant = new DefaultTenant(UUID.randomUUID(), null, null, UUID.randomUUID().toString(),
-                UUID.randomUUID().toString(), UUID.randomUUID().toString());
-        tenantDao.create(tenant, internalCallContext);
+                                                       UUID.randomUUID().toString(), UUID.randomUUID().toString());
+        tenantDao.create(new TenantModelDao(tenant), internalCallContext);
 
         tenantDao.addTenantKeyValue("TheKey", "TheValue", internalCallContext);
 
-        List<String> value  = tenantDao.getTenantValueForKey("TheKey", internalCallContext);
+        List<String> value = tenantDao.getTenantValueForKey("TheKey", internalCallContext);
         Assert.assertEquals(value.size(), 1);
         Assert.assertEquals(value.get(0), "TheValue");
 
         tenantDao.addTenantKeyValue("TheKey", "TheSecondValue", internalCallContext);
-        value  = tenantDao.getTenantValueForKey("TheKey", internalCallContext);
+        value = tenantDao.getTenantValueForKey("TheKey", internalCallContext);
         Assert.assertEquals(value.size(), 2);
 
         tenantDao.deleteTenantKey("TheKey", internalCallContext);
diff --git a/usage/src/main/java/com/ning/billing/usage/timeline/samples/HalfFloat.java b/usage/src/main/java/com/ning/billing/usage/timeline/samples/HalfFloat.java
index 90f9e77..f058414 100644
--- a/usage/src/main/java/com/ning/billing/usage/timeline/samples/HalfFloat.java
+++ b/usage/src/main/java/com/ning/billing/usage/timeline/samples/HalfFloat.java
@@ -29,7 +29,7 @@ public class HalfFloat {
     }
 
     // These two static methods were pinched from http://stackoverflow.com/questions/6162651/half-precision-floating-point-in-java/6162687#6162687
-    // The last comment on that page is the author saying "I hereby commit these to the public domain"
+    // The last comments on that page is the author saying "I hereby commit these to the public domain"
 
     // Ignores the higher 16 bits
     public static float toFloat(final int hbits) {

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

diff --git a/util/pom.xml b/util/pom.xml
index e3fb81a..a767483 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -85,7 +85,6 @@
         <dependency>
             <groupId>org.antlr</groupId>
             <artifactId>stringtemplate</artifactId>
-            <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>com.jayway.awaitility</groupId>
diff --git a/util/src/main/java/com/ning/billing/util/audit/api/DefaultAuditUserApi.java b/util/src/main/java/com/ning/billing/util/audit/api/DefaultAuditUserApi.java
index 0429e67..3cb03c2 100644
--- a/util/src/main/java/com/ning/billing/util/audit/api/DefaultAuditUserApi.java
+++ b/util/src/main/java/com/ning/billing/util/audit/api/DefaultAuditUserApi.java
@@ -24,7 +24,10 @@ import java.util.UUID;
 import javax.inject.Inject;
 
 import com.ning.billing.ObjectType;
+import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.entitlement.api.timeline.BundleTimeline;
+import com.ning.billing.entitlement.api.timeline.EntitlementRepairException;
+import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
 import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline;
 import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline.ExistingEvent;
 import com.ning.billing.invoice.api.Invoice;
@@ -35,6 +38,7 @@ 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.audit.AuditLog;
+import com.ning.billing.util.audit.AuditLogsForAccount;
 import com.ning.billing.util.audit.AuditLogsForBundles;
 import com.ning.billing.util.audit.AuditLogsForInvoicePayments;
 import com.ning.billing.util.audit.AuditLogsForInvoices;
@@ -55,15 +59,27 @@ import com.google.common.collect.ImmutableList;
 public class DefaultAuditUserApi implements AuditUserApi {
 
     private final AuditDao auditDao;
+    private final EntitlementTimelineApi timelineApi;
     private final InternalCallContextFactory internalCallContextFactory;
 
     @Inject
-    public DefaultAuditUserApi(final AuditDao auditDao, final InternalCallContextFactory internalCallContextFactory) {
+    public DefaultAuditUserApi(final AuditDao auditDao, final EntitlementTimelineApi timelineApi, final InternalCallContextFactory internalCallContextFactory) {
         this.auditDao = auditDao;
+        this.timelineApi = timelineApi;
         this.internalCallContextFactory = internalCallContextFactory;
     }
 
     @Override
+    public AuditLogsForAccount getAuditLogsForAccount(final UUID accountId, final AuditLevel auditLevel, final TenantContext context){
+        return new DefaultAuditLogsForAccount(getAuditLogs(accountId, ObjectType.ACCOUNT, auditLevel, context));
+    }
+
+    @Override
+    public AuditLogsForBundles getAuditLogsForBundle(final UUID bundleId, final AuditLevel auditLevel, final TenantContext context) throws EntitlementRepairException {
+        return getAuditLogsForBundles(ImmutableList.<BundleTimeline>of(timelineApi.getBundleTimeline(bundleId, context)), auditLevel, context);
+    }
+
+    @Override
     public AuditLogsForBundles getAuditLogsForBundles(final List<BundleTimeline> bundles, final AuditLevel auditLevel, final TenantContext context) {
         final Map<UUID, List<AuditLog>> bundlesAuditLogs = new HashMap<UUID, List<AuditLog>>();
         final Map<UUID, List<AuditLog>> subscriptionsAuditLogs = new HashMap<UUID, List<AuditLog>>();
diff --git a/util/src/main/java/com/ning/billing/util/audit/dao/DefaultAuditDao.java b/util/src/main/java/com/ning/billing/util/audit/dao/DefaultAuditDao.java
index de92fc6..fdfd8a8 100644
--- a/util/src/main/java/com/ning/billing/util/audit/dao/DefaultAuditDao.java
+++ b/util/src/main/java/com/ning/billing/util/audit/dao/DefaultAuditDao.java
@@ -64,9 +64,11 @@ public class DefaultAuditDao implements AuditDao {
     private List<AuditLog> doGetAuditLogsViaHistoryForId(final TableName tableName, final UUID objectId, final AuditLevel auditLevel, final InternalTenantContext context) {
         final List<AuditLog> auditLogs = new ArrayList<AuditLog>();
 
+        Long targetRecordId = auditSqlDao.getRecordIdForTable(tableName.getTableName().toLowerCase(), objectId.toString(), context);
+
         // Look at the history table and gather all the history_record_id for that objectId
         final List<Long> recordIds = auditSqlDao.getHistoryRecordIdsForTable(tableName.getHistoryTableName().getTableName().toLowerCase(),
-                                                                             objectId.toString(), context);
+                                                                             targetRecordId, context);
         if (recordIds == null) {
             return auditLogs;
         } else {
@@ -78,8 +80,8 @@ public class DefaultAuditDao implements AuditDao {
         }
     }
 
-    private List<AuditLog> getAuditLogsForRecordId(final TableName tableName, final Long recordId, final AuditLevel auditLevel, final InternalTenantContext context) {
-        final List<AuditLog> allAuditLogs = auditSqlDao.getAuditLogsForRecordId(tableName, recordId, context);
+    private List<AuditLog> getAuditLogsForRecordId(final TableName tableName, final Long targetRecordId, final AuditLevel auditLevel, final InternalTenantContext context) {
+        final List<AuditLog> allAuditLogs = auditSqlDao.getAuditLogsForTargetRecordId(tableName, targetRecordId, context);
         if (AuditLevel.FULL.equals(auditLevel)) {
             return allAuditLogs;
         } else if (AuditLevel.MINIMAL.equals(auditLevel) && allAuditLogs.size() > 0) {
diff --git a/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLog.java b/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLog.java
index ebc7dfd..e9e9bad 100644
--- a/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLog.java
+++ b/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLog.java
@@ -21,20 +21,15 @@ import org.joda.time.DateTime;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.dao.EntityAudit;
 
-public class DefaultAuditLog implements AuditLog {
+public class DefaultAuditLog extends EntityAudit implements AuditLog {
 
-    private final EntityAudit entityAudit;
     private final CallContext callContext;
 
     public DefaultAuditLog(final EntityAudit entityAudit, final CallContext callContext) {
-        this.entityAudit = entityAudit;
+        super(entityAudit.getId(), entityAudit.getTableName(), entityAudit.getTargetRecordId(), entityAudit.getChangeType(), entityAudit.getCreatedDate());
         this.callContext = callContext;
     }
 
-    @Override
-    public ChangeType getChangeType() {
-        return entityAudit.getChangeType();
-    }
 
     @Override
     public String getUserName() {
@@ -62,14 +57,14 @@ public class DefaultAuditLog implements AuditLog {
 
     @Override
     public String getComment() {
-        return callContext.getComment();
+        return callContext.getComments();
     }
 
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
-        sb.append("DefaultAuditLog");
-        sb.append("{entityAudit=").append(entityAudit);
+        sb.append("DefaultAuditLog {");
+        sb.append(super.toString());
         sb.append(", callContext=").append(callContext);
         sb.append('}');
         return sb.toString();
@@ -83,22 +78,22 @@ public class DefaultAuditLog implements AuditLog {
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
+        if (!super.equals(o)) {
+            return false;
+        }
 
         final DefaultAuditLog that = (DefaultAuditLog) o;
 
         if (callContext != null ? !callContext.equals(that.callContext) : that.callContext != null) {
             return false;
         }
-        if (entityAudit != null ? !entityAudit.equals(that.entityAudit) : that.entityAudit != null) {
-            return false;
-        }
 
         return true;
     }
 
     @Override
     public int hashCode() {
-        int result = entityAudit != null ? entityAudit.hashCode() : 0;
+        int result = super.hashCode();
         result = 31 * result + (callContext != null ? callContext.hashCode() : 0);
         return result;
     }
diff --git a/util/src/main/java/com/ning/billing/util/bus/dao/PersistentBusSqlDao.java b/util/src/main/java/com/ning/billing/util/bus/dao/PersistentBusSqlDao.java
index e9e70de..0f2ad73 100644
--- a/util/src/main/java/com/ning/billing/util/bus/dao/PersistentBusSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/bus/dao/PersistentBusSqlDao.java
@@ -24,6 +24,7 @@ import org.joda.time.DateTime;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.Binder;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
@@ -35,7 +36,6 @@ import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
 import com.ning.billing.util.dao.BinderBase;
 import com.ning.billing.util.dao.MapperBase;
 import com.ning.billing.util.queue.PersistentQueueEntryLifecycle.PersistentQueueEntryLifecycleState;
@@ -48,33 +48,33 @@ public interface PersistentBusSqlDao extends Transactional<PersistentBusSqlDao>,
     public BusEventEntry getNextBusEventEntry(@Bind("max") int max,
                                               @Bind("owner") String owner,
                                               @Bind("now") Date now,
-                                              @InternalTenantContextBinder final InternalTenantContext context);
+                                              @BindBean final InternalTenantContext context);
 
     @SqlUpdate
     public int claimBusEvent(@Bind("owner") String owner,
                              @Bind("nextAvailable") Date nextAvailable,
                              @Bind("recordId") Long id,
                              @Bind("now") Date now,
-                             @InternalTenantContextBinder final InternalCallContext context);
+                             @BindBean final InternalCallContext context);
 
     @SqlUpdate
     public void clearBusEvent(@Bind("recordId") Long id,
                               @Bind("owner") String owner,
-                              @InternalTenantContextBinder final InternalCallContext context);
+                              @BindBean final InternalCallContext context);
 
     @SqlUpdate
     public void removeBusEventsById(@Bind("recordId") Long id,
-                                    @InternalTenantContextBinder final InternalCallContext context);
+                                    @BindBean final InternalCallContext context);
 
     @SqlUpdate
     public void insertBusEvent(@Bind(binder = PersistentBusSqlBinder.class) BusEventEntry evt,
-                               @InternalTenantContextBinder final InternalCallContext context);
+                               @BindBean final InternalCallContext context);
 
     @SqlUpdate
     public void insertClaimedHistory(@Bind("ownerId") String owner,
                                      @Bind("claimedDate") Date claimedDate,
                                      @Bind("busEventId") long id,
-                                     @InternalTenantContextBinder final InternalCallContext context);
+                                     @BindBean final InternalCallContext context);
 
     public static class PersistentBusSqlBinder extends BinderBase implements Binder<Bind, BusEventEntry> {
 
diff --git a/util/src/main/java/com/ning/billing/util/bus/InMemoryInternalBus.java b/util/src/main/java/com/ning/billing/util/bus/InMemoryInternalBus.java
index 150c6d6..5770322 100644
--- a/util/src/main/java/com/ning/billing/util/bus/InMemoryInternalBus.java
+++ b/util/src/main/java/com/ning/billing/util/bus/InMemoryInternalBus.java
@@ -26,6 +26,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.events.BusInternalEvent;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
 
@@ -97,7 +99,7 @@ public class InMemoryInternalBus implements InternalBus {
     }
 
     @Override
-    public void postFromTransaction(final BusInternalEvent event, final Transmogrifier dao, final InternalCallContext context) throws EventBusException {
+    public void postFromTransaction(final BusInternalEvent event, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalCallContext context) throws EventBusException {
         checkInitialized("postFromTransaction");
         delegate.post(event);
     }
diff --git a/util/src/main/java/com/ning/billing/util/bus/PersistentInternalBus.java b/util/src/main/java/com/ning/billing/util/bus/PersistentInternalBus.java
index c84d1b0..859fce8 100644
--- a/util/src/main/java/com/ning/billing/util/bus/PersistentInternalBus.java
+++ b/util/src/main/java/com/ning/billing/util/bus/PersistentInternalBus.java
@@ -37,6 +37,8 @@ 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.clock.Clock;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.events.BusInternalEvent;
 import com.ning.billing.util.queue.PersistentQueueBase;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
@@ -171,9 +173,9 @@ public class PersistentInternalBus extends PersistentQueueBase implements Intern
     }
 
     @Override
-    public void postFromTransaction(final BusInternalEvent event, final Transmogrifier transmogrifier, final InternalCallContext context)
+    public void postFromTransaction(final BusInternalEvent event, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalCallContext context)
             throws EventBusException {
-        final PersistentBusSqlDao transactional = transmogrifier.become(PersistentBusSqlDao.class);
+        final PersistentBusSqlDao transactional = entitySqlDaoWrapperFactory.transmogrify(PersistentBusSqlDao.class);
         postFromTransaction(event, context, transactional);
     }
 
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/CallContextBase.java b/util/src/main/java/com/ning/billing/util/callcontext/CallContextBase.java
index c5dd11f..de3c833 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/CallContextBase.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/CallContextBase.java
@@ -28,7 +28,7 @@ public abstract class CallContextBase implements CallContext {
     protected final CallOrigin callOrigin;
     protected final UserType userType;
     protected final String reasonCode;
-    protected final String comment;
+    protected final String comments;
 
     public CallContextBase(@Nullable final UUID tenantId, final String userName, final CallOrigin callOrigin, final UserType userType) {
         this(tenantId, userName, callOrigin, userType, null);
@@ -45,7 +45,7 @@ public abstract class CallContextBase implements CallContext {
         this.callOrigin = callOrigin;
         this.userType = userType;
         this.reasonCode = reasonCode;
-        this.comment = comment;
+        this.comments = comment;
         this.userToken = userToken;
     }
 
@@ -75,8 +75,8 @@ public abstract class CallContextBase implements CallContext {
     }
 
     @Override
-    public String getComment() {
-        return comment;
+    public String getComments() {
+        return comments;
     }
 
     @Override
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java b/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java
index 0cb9858..e4dd038 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java
@@ -75,7 +75,7 @@ public class DefaultCallContext extends CallContextBase {
         sb.append(", callOrigin=").append(callOrigin);
         sb.append(", userType=").append(userType);
         sb.append(", reasonCode='").append(reasonCode).append('\'');
-        sb.append(", comment='").append(comment).append('\'');
+        sb.append(", comments='").append(comments).append('\'');
         sb.append(", createdDate='").append(createdDate).append('\'');
         sb.append(", updatedDate='").append(createdDate).append('\'');
         sb.append('}');
@@ -96,7 +96,7 @@ public class DefaultCallContext extends CallContextBase {
         if (callOrigin != that.callOrigin) {
             return false;
         }
-        if (comment != null ? !comment.equals(that.comment) : that.comment != null) {
+        if (comments != null ? !comments.equals(that.comments) : that.comments != null) {
             return false;
         }
         if (reasonCode != null ? !reasonCode.equals(that.reasonCode) : that.reasonCode != null) {
@@ -125,7 +125,7 @@ public class DefaultCallContext extends CallContextBase {
         result = 31 * result + (callOrigin != null ? callOrigin.hashCode() : 0);
         result = 31 * result + (userType != null ? userType.hashCode() : 0);
         result = 31 * result + (reasonCode != null ? reasonCode.hashCode() : 0);
-        result = 31 * result + (comment != null ? comment.hashCode() : 0);
+        result = 31 * result + (comments != null ? comments.hashCode() : 0);
         result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
         return result;
     }
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContext.java b/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContext.java
index 9f6da10..1e90911 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContext.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContext.java
@@ -28,11 +28,12 @@ import org.joda.time.DateTime;
 public class InternalCallContext extends InternalTenantContext {
 
     private final UUID userToken;
-    private final String userName;
+    private final String createdBy;
+    private final String updatedBy;
     private final CallOrigin callOrigin;
-    private final UserType userType;
+    private final UserType contextUserType;
     private final String reasonCode;
-    private final String comment;
+    private final String comments;
     private final DateTime createdDate;
     private final DateTime updatedDate;
 
@@ -41,18 +42,19 @@ public class InternalCallContext extends InternalTenantContext {
                                final DateTime createdDate, final DateTime updatedDate) {
         super(tenantRecordId, accountRecordId);
         this.userToken = userToken;
-        this.userName = userName;
+        this.createdBy = userName;
+        this.updatedBy = userName;
         this.callOrigin = callOrigin;
-        this.userType = userType;
+        this.contextUserType = userType;
         this.reasonCode = reasonCode;
-        this.comment = comment;
+        this.comments = comment;
         this.createdDate = createdDate;
         this.updatedDate = updatedDate;
     }
 
     public InternalCallContext(final Long tenantRecordId, @Nullable final Long accountRecordId, final CallContext callContext) {
         this(tenantRecordId, accountRecordId, callContext.getUserToken(), callContext.getUserName(), callContext.getCallOrigin(),
-             callContext.getUserType(), callContext.getReasonCode(), callContext.getComment(), callContext.getCreatedDate(),
+             callContext.getUserType(), callContext.getReasonCode(), callContext.getComments(), callContext.getCreatedDate(),
              callContext.getUpdatedDate());
     }
 
@@ -61,31 +63,35 @@ public class InternalCallContext extends InternalTenantContext {
     // Unfortunately not true as some APIs ae hidden in object-- e.g OverdueStateApplicator is doing subscription.cancelWithPolicy(polciy, context);
     //
     public CallContext toCallContext() {
-        return new DefaultCallContext(null, userName, callOrigin, userType, reasonCode, comment, userToken, createdDate, updatedDate);
+        return new DefaultCallContext(null, createdBy, callOrigin, contextUserType, reasonCode, comments, userToken, createdDate, updatedDate);
     }
 
     public UUID getUserToken() {
         return userToken;
     }
 
-    public String getUserName() {
-        return userName;
+    public String getCreatedBy() {
+        return createdBy;
+    }
+
+    public String getUpdatedBy() {
+        return updatedBy;
     }
 
     public CallOrigin getCallOrigin() {
         return callOrigin;
     }
 
-    public UserType getUserType() {
-        return userType;
+    public UserType getContextUserType() {
+        return contextUserType;
     }
 
     public String getReasonCode() {
         return reasonCode;
     }
 
-    public String getComment() {
-        return comment;
+    public String getComments() {
+        return comments;
     }
 
     public DateTime getCreatedDate() {
@@ -101,11 +107,12 @@ public class InternalCallContext extends InternalTenantContext {
         final StringBuilder sb = new StringBuilder();
         sb.append("InternalCallContext");
         sb.append("{userToken=").append(userToken);
-        sb.append(", userName='").append(userName).append('\'');
+        sb.append(", createdBy='").append(createdBy).append('\'');
+        sb.append(", updatedBy='").append(updatedBy).append('\'');
         sb.append(", callOrigin=").append(callOrigin);
-        sb.append(", userType=").append(userType);
+        sb.append(", contextUserType=").append(contextUserType);
         sb.append(", reasonCode='").append(reasonCode).append('\'');
-        sb.append(", comment='").append(comment).append('\'');
+        sb.append(", comments='").append(comments).append('\'');
         sb.append(", createdDate=").append(createdDate);
         sb.append(", updatedDate=").append(updatedDate);
         sb.append('}');
@@ -129,7 +136,10 @@ public class InternalCallContext extends InternalTenantContext {
         if (callOrigin != that.callOrigin) {
             return false;
         }
-        if (comment != null ? !comment.equals(that.comment) : that.comment != null) {
+        if (comments != null ? !comments.equals(that.comments) : that.comments != null) {
+            return false;
+        }
+        if (createdBy != null ? !createdBy.equals(that.createdBy) : that.createdBy != null) {
             return false;
         }
         if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
@@ -138,16 +148,16 @@ public class InternalCallContext extends InternalTenantContext {
         if (reasonCode != null ? !reasonCode.equals(that.reasonCode) : that.reasonCode != null) {
             return false;
         }
-        if (updatedDate != null ? !updatedDate.equals(that.updatedDate) : that.updatedDate != null) {
+        if (updatedBy != null ? !updatedBy.equals(that.updatedBy) : that.updatedBy != null) {
             return false;
         }
-        if (userName != null ? !userName.equals(that.userName) : that.userName != null) {
+        if (updatedDate != null ? !updatedDate.equals(that.updatedDate) : that.updatedDate != null) {
             return false;
         }
         if (userToken != null ? !userToken.equals(that.userToken) : that.userToken != null) {
             return false;
         }
-        if (userType != that.userType) {
+        if (contextUserType != that.contextUserType) {
             return false;
         }
 
@@ -158,11 +168,12 @@ public class InternalCallContext extends InternalTenantContext {
     public int hashCode() {
         int result = super.hashCode();
         result = 31 * result + (userToken != null ? userToken.hashCode() : 0);
-        result = 31 * result + (userName != null ? userName.hashCode() : 0);
+        result = 31 * result + (createdBy != null ? createdBy.hashCode() : 0);
+        result = 31 * result + (updatedBy != null ? updatedBy.hashCode() : 0);
         result = 31 * result + (callOrigin != null ? callOrigin.hashCode() : 0);
-        result = 31 * result + (userType != null ? userType.hashCode() : 0);
+        result = 31 * result + (contextUserType != null ? contextUserType.hashCode() : 0);
         result = 31 * result + (reasonCode != null ? reasonCode.hashCode() : 0);
-        result = 31 * result + (comment != null ? comment.hashCode() : 0);
+        result = 31 * result + (comments != null ? comments.hashCode() : 0);
         result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
         result = 31 * result + (updatedDate != null ? updatedDate.hashCode() : 0);
         return result;
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContextFactory.java b/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContextFactory.java
index 97202bd..f72d061 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContextFactory.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContextFactory.java
@@ -91,7 +91,7 @@ public class InternalCallContextFactory {
         //                         "tenant of the pointed object (%s) and the context (%s) don't match!", tenantRecordIdFromObject, tenantRecordIdFromContext);
 
         return createInternalCallContext(objectId, objectType, context.getUserName(), context.getCallOrigin(),
-                                         context.getUserType(), context.getUserToken(), context.getReasonCode(), context.getComment(),
+                                         context.getUserType(), context.getUserToken(), context.getReasonCode(), context.getComments(),
                                          context.getCreatedDate(), context.getUpdatedDate());
     }
 
@@ -106,7 +106,7 @@ public class InternalCallContextFactory {
      */
     public InternalCallContext createInternalCallContext(final UUID accountId, final CallContext context) {
         return createInternalCallContext(accountId, ObjectType.ACCOUNT, context.getUserName(), context.getCallOrigin(),
-                                         context.getUserType(), context.getUserToken(), context.getReasonCode(), context.getComment(),
+                                         context.getUserType(), context.getUserToken(), context.getReasonCode(), context.getComments(),
                                          context.getCreatedDate(), context.getUpdatedDate());
     }
 
@@ -198,15 +198,15 @@ public class InternalCallContextFactory {
 
     // Used when we need to re-hydrate the context with the account_record_id (when creating the account)
     public InternalCallContext createInternalCallContext(final Long accountRecordId, final InternalCallContext context) {
-        return new InternalCallContext(context.getTenantRecordId(), accountRecordId, context.getUserToken(), context.getUserName(),
-                                       context.getCallOrigin(), context.getUserType(), context.getReasonCode(), context.getComment(),
+        return new InternalCallContext(context.getTenantRecordId(), accountRecordId, context.getUserToken(), context.getCreatedBy(),
+                                       context.getCallOrigin(), context.getContextUserType(), context.getReasonCode(), context.getComments(),
                                        context.getCreatedDate(), context.getUpdatedDate());
     }
 
     // Used when we need to re-hydrate the context with the tenant_record_id and account_record_id (when claiming bus events)
     public InternalCallContext createInternalCallContext(final Long tenantRecordId, final Long accountRecordId, final InternalCallContext context) {
-        return new InternalCallContext(tenantRecordId, accountRecordId, context.getUserToken(), context.getUserName(),
-                                       context.getCallOrigin(), context.getUserType(), context.getReasonCode(), context.getComment(),
+        return new InternalCallContext(tenantRecordId, accountRecordId, context.getUserToken(), context.getCreatedBy(),
+                                       context.getCallOrigin(), context.getContextUserType(), context.getReasonCode(), context.getComments(),
                                        context.getCreatedDate(), context.getUpdatedDate());
     }
 
@@ -283,6 +283,4 @@ public class InternalCallContextFactory {
         }
         return tenantRecordId;
     }
-
-
 }
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/InternalTenantContextBinder.java b/util/src/main/java/com/ning/billing/util/callcontext/InternalTenantContextBinder.java
index 0349be5..efdddde 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/InternalTenantContextBinder.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/InternalTenantContextBinder.java
@@ -57,7 +57,7 @@ public @interface InternalTenantContextBinder {
 
                     if (context instanceof InternalCallContext) {
                         final InternalCallContext callContext = (InternalCallContext) context;
-                        q.bind("userName", callContext.getUserName());
+                        q.bind("userName", callContext.getCreatedBy());
                         if (callContext.getCreatedDate() == null) {
                             q.bindNull("createdDate", Types.DATE);
                         } else {
@@ -69,7 +69,7 @@ public @interface InternalTenantContextBinder {
                             q.bind("updatedDate", callContext.getUpdatedDate().toDate());
                         }
                         q.bind("reasonCode", callContext.getReasonCode());
-                        q.bind("comment", callContext.getComment());
+                        q.bind("comments", callContext.getComments());
                         q.bind("userToken", (callContext.getUserToken() != null) ? callContext.getUserToken().toString() : null);
                     }
                 }
diff --git a/util/src/main/java/com/ning/billing/util/customfield/api/DefaultCustomFieldUserApi.java b/util/src/main/java/com/ning/billing/util/customfield/api/DefaultCustomFieldUserApi.java
index dcd771a..b1f0a38 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/api/DefaultCustomFieldUserApi.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/api/DefaultCustomFieldUserApi.java
@@ -17,17 +17,22 @@
 package com.ning.billing.util.customfield.api;
 
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
 import com.ning.billing.ObjectType;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.customfield.StringCustomField;
 import com.ning.billing.util.customfield.dao.CustomFieldDao;
+import com.ning.billing.util.customfield.dao.CustomFieldModelDao;
 
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
 public class DefaultCustomFieldUserApi implements CustomFieldUserApi {
@@ -42,12 +47,21 @@ public class DefaultCustomFieldUserApi implements CustomFieldUserApi {
     }
 
     @Override
-    public Map<String, CustomField> getCustomFields(final UUID objectId, final ObjectType objectType, final TenantContext context) {
-        return customFieldDao.loadEntities(objectId, objectType, internalCallContextFactory.createInternalTenantContext(context));
+    public List<CustomField> getCustomFields(final UUID objectId, final ObjectType objectType, final TenantContext context) {
+        return ImmutableList.<CustomField>copyOf(Collections2.transform(customFieldDao.getCustomFields(objectId, objectType, internalCallContextFactory.createInternalTenantContext(context)),
+                                                                        new Function<CustomFieldModelDao, CustomField>() {
+                                                                            @Override
+                                                                            public CustomField apply(final CustomFieldModelDao input) {
+                                                                                return new StringCustomField(input);
+                                                                            }
+                                                                        }));
     }
 
     @Override
-    public void saveCustomFields(final UUID objectId, final ObjectType objectType, final List<CustomField> fields, final CallContext context) {
-        customFieldDao.saveEntities(objectId, objectType, fields, internalCallContextFactory.createInternalCallContext(objectId, objectType, context));
+    public void addCustomFields(final List<CustomField> fields, final CallContext context) throws CustomFieldApiException {
+        // TODO make it transactional
+        for (final CustomField cur : fields) {
+            customFieldDao.create(new CustomFieldModelDao(cur), internalCallContextFactory.createInternalCallContext(cur.getObjectId(), cur.getObjectType(), context));
+        }
     }
 }
diff --git a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldDao.java b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldDao.java
index 66cd734..ead8b57 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldDao.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldDao.java
@@ -16,8 +16,16 @@
 
 package com.ning.billing.util.customfield.dao;
 
+import java.util.List;
+import java.util.UUID;
+
+import com.ning.billing.ObjectType;
+import com.ning.billing.util.api.CustomFieldApiException;
+import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.customfield.CustomField;
-import com.ning.billing.util.dao.AuditedCollectionDao;
+import com.ning.billing.util.entity.dao.EntityDao;
+
+public interface CustomFieldDao extends EntityDao<CustomFieldModelDao, CustomField, CustomFieldApiException> {
 
-public interface CustomFieldDao extends AuditedCollectionDao<CustomField> {
+    public List<CustomFieldModelDao> getCustomFields(final UUID objectId, final ObjectType objectType, final InternalTenantContext context);
 }
diff --git a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldModelDao.java b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldModelDao.java
new file mode 100644
index 0000000..1f166d5
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldModelDao.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.customfield.dao;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.ObjectType;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.dao.EntityModelDao;
+
+public class CustomFieldModelDao extends EntityBase implements EntityModelDao<CustomField> {
+
+    private String fieldName;
+    private String fieldValue;
+    private UUID objectId;
+    private ObjectType objectType;
+
+    public CustomFieldModelDao() {  /* For the DAO mapper */ }
+
+    public CustomFieldModelDao(final UUID id, final DateTime createdDate, final DateTime updatedDate, final String fieldName,
+                               final String fieldValue, final UUID objectId, final ObjectType objectType) {
+        super(id, createdDate, updatedDate);
+        this.fieldName = fieldName;
+        this.fieldValue = fieldValue;
+        this.objectId = objectId;
+        this.objectType = objectType;
+    }
+
+    public CustomFieldModelDao(final CustomField customField) {
+        this(customField.getId(), customField.getCreatedDate(), customField.getUpdatedDate(), customField.getFieldName(),
+             customField.getFieldValue(), customField.getObjectId(), customField.getObjectType());
+    }
+
+    public String getFieldName() {
+        return fieldName;
+    }
+
+    public String getFieldValue() {
+        return fieldValue;
+    }
+
+    public UUID getObjectId() {
+        return objectId;
+    }
+
+    public ObjectType getObjectType() {
+        return objectType;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("CustomFieldModelDao");
+        sb.append("{fieldName='").append(fieldName).append('\'');
+        sb.append(", fieldValue='").append(fieldValue).append('\'');
+        sb.append(", objectId=").append(objectId);
+        sb.append(", objectType=").append(objectType);
+        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 CustomFieldModelDao that = (CustomFieldModelDao) o;
+
+        if (fieldName != null ? !fieldName.equals(that.fieldName) : that.fieldName != null) {
+            return false;
+        }
+        if (fieldValue != null ? !fieldValue.equals(that.fieldValue) : that.fieldValue != null) {
+            return false;
+        }
+        if (objectId != null ? !objectId.equals(that.objectId) : that.objectId != null) {
+            return false;
+        }
+        if (objectType != that.objectType) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (fieldName != null ? fieldName.hashCode() : 0);
+        result = 31 * result + (fieldValue != null ? fieldValue.hashCode() : 0);
+        result = 31 * result + (objectId != null ? objectId.hashCode() : 0);
+        result = 31 * result + (objectType != null ? objectType.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.CUSTOM_FIELD;
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.java b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.java
index 215795e..34a8059 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.java
@@ -16,54 +16,24 @@
 
 package com.ning.billing.util.customfield.dao;
 
-import java.util.Collection;
 import java.util.List;
+import java.util.UUID;
 
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.SqlBatch;
-import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+import org.skife.jdbi.v2.sqlobject.BindBean;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
 
 import com.ning.billing.ObjectType;
-import com.ning.billing.util.callcontext.InternalCallContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
+import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.customfield.CustomField;
-import com.ning.billing.util.dao.EntityHistory;
-import com.ning.billing.util.dao.ObjectTypeBinder;
-import com.ning.billing.util.entity.collection.dao.UpdatableEntityCollectionSqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 
-@ExternalizedSqlViaStringTemplate3
-@RegisterMapper(CustomFieldMapper.class)
-public interface CustomFieldSqlDao extends UpdatableEntityCollectionSqlDao<CustomField>,
-                                           Transactional<CustomFieldSqlDao>, Transmogrifier {
+@EntitySqlDaoStringTemplate
+public interface CustomFieldSqlDao extends EntitySqlDao<CustomFieldModelDao, CustomField> {
 
-    @Override
-    @SqlBatch
-    public void insertFromTransaction(@Bind("objectId") final String objectId,
-                                      @ObjectTypeBinder final ObjectType objectType,
-                                      @CustomFieldBinder final Collection<CustomField> entities,
-                                      @InternalTenantContextBinder final InternalCallContext context);
-
-    @Override
-    @SqlBatch
-    public void updateFromTransaction(@Bind("objectId") final String objectId,
-                                      @ObjectTypeBinder final ObjectType objectType,
-                                      @CustomFieldBinder final Collection<CustomField> entities,
-                                      @InternalTenantContextBinder final InternalCallContext context);
-
-    @Override
-    @SqlBatch
-    public void deleteFromTransaction(@Bind("objectId") final String objectId,
-                                      @ObjectTypeBinder final ObjectType objectType,
-                                      @CustomFieldBinder final Collection<CustomField> entities,
-                                      @InternalTenantContextBinder final InternalCallContext context);
-
-    @Override
-    @SqlBatch
-    public void addHistoryFromTransaction(@Bind("objectId") final String objectId,
-                                          @ObjectTypeBinder final ObjectType objectType,
-                                          @CustomFieldHistoryBinder final List<EntityHistory<CustomField>> entities,
-                                          @InternalTenantContextBinder final InternalCallContext context);
+    @SqlQuery
+    List<CustomFieldModelDao> getCustomFieldsForObject(@Bind("objectId") UUID objectId,
+                                                       @Bind("objectType") ObjectType objectType,
+                                                       @BindBean InternalTenantContext internalTenantContext);
 }
diff --git a/util/src/main/java/com/ning/billing/util/customfield/dao/DefaultCustomFieldDao.java b/util/src/main/java/com/ning/billing/util/customfield/dao/DefaultCustomFieldDao.java
new file mode 100644
index 0000000..172229b
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/DefaultCustomFieldDao.java
@@ -0,0 +1,59 @@
+/*
+ * 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.util.customfield.dao;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.skife.jdbi.v2.IDBI;
+
+import com.ning.billing.ErrorCode;
+import com.ning.billing.ObjectType;
+import com.ning.billing.util.api.CustomFieldApiException;
+import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.entity.dao.EntityDaoBase;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+
+import com.google.inject.Inject;
+
+public class DefaultCustomFieldDao extends EntityDaoBase<CustomFieldModelDao, CustomField, CustomFieldApiException> implements CustomFieldDao {
+
+    @Inject
+    public DefaultCustomFieldDao(final IDBI dbi) {
+        super(new EntitySqlDaoTransactionalJdbiWrapper(dbi), CustomFieldSqlDao.class);
+    }
+
+    @Override
+    public List<CustomFieldModelDao> getCustomFields(final UUID objectId, final ObjectType objectType, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<CustomFieldModelDao>>() {
+            @Override
+            public List<CustomFieldModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(CustomFieldSqlDao.class).getCustomFieldsForObject(objectId, objectType, context);
+            }
+        });
+    }
+
+    @Override
+    protected CustomFieldApiException generateAlreadyExistsException(final CustomFieldModelDao entity, final InternalCallContext context) {
+        return new CustomFieldApiException(ErrorCode.CUSTOM_FIELD_ALREADY_EXISTS, entity.getId());
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/customfield/StringCustomField.java b/util/src/main/java/com/ning/billing/util/customfield/StringCustomField.java
index 4458801..ec58bed 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/StringCustomField.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/StringCustomField.java
@@ -18,81 +18,103 @@ package com.ning.billing.util.customfield;
 
 import java.util.UUID;
 
-import com.ning.billing.util.entity.Entity;
+import org.joda.time.DateTime;
+
+import com.ning.billing.ObjectType;
+import com.ning.billing.util.customfield.dao.CustomFieldModelDao;
 import com.ning.billing.util.entity.EntityBase;
 
-public class StringCustomField extends EntityBase implements CustomField, Entity {
-    private final String name;
-    private String value;
+public class StringCustomField extends EntityBase implements CustomField {
+
+    private final String fieldName;
+    private final String fieldValue;
+    private final UUID objectId;
+    private final ObjectType objectType;
 
-    public StringCustomField(final String name, final String value) {
-        super();
-        this.name = name;
-        this.value = value;
+    public StringCustomField(final String name, final String value, final ObjectType objectType, final UUID objectId, final DateTime createdDate) {
+        this(UUID.randomUUID(), name, value, objectType, objectId, createdDate);
     }
 
-    public StringCustomField(final UUID id, final String name, final String value) {
-        super(id);
-        this.name = name;
-        this.value = value;
+    public StringCustomField(final UUID id, final String name, final String value, final ObjectType objectType, final UUID objectId, final DateTime createdDate) {
+        super(id, createdDate, createdDate);
+        this.fieldName = name;
+        this.fieldValue = value;
+        this.objectId = objectId;
+        this.objectType = objectType;
+
     }
 
-    @Override
-    public String getName() {
-        return name;
+    public StringCustomField(final CustomFieldModelDao input) {
+        this(input.getId(), input.getFieldName(), input.getFieldValue(), input.getObjectType(), input.getObjectId(), input.getCreatedDate());
     }
 
     @Override
-    public String getValue() {
-        return value;
+    public String getFieldName() {
+        return fieldName;
     }
 
     @Override
-    public void setValue(final String value) {
-        this.value = value;
+    public String getFieldValue() {
+        return fieldValue;
     }
 
-    @Override
-    public String toString() {
-        return "StringCustomField [name=" + name + ", value=" + value + ", id=" + id + "]";
+    public ObjectType getObjectType() {
+        return objectType;
+    }
+
+    public UUID getObjectId() {
+        return objectId;
     }
 
     @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((name == null) ? 0 : name.hashCode());
-        result = prime * result + ((value == null) ? 0 : value.hashCode());
-        return result;
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("StringCustomField");
+        sb.append("{fieldName='").append(fieldName).append('\'');
+        sb.append(", fieldValue='").append(fieldValue).append('\'');
+        sb.append(", objectId=").append(objectId);
+        sb.append(", objectType=").append(objectType);
+        sb.append('}');
+        return sb.toString();
     }
 
     @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
+    public boolean equals(final Object o) {
+        if (this == o) {
             return true;
         }
-        if (obj == null) {
+        if (o == null || getClass() != o.getClass()) {
             return false;
         }
-        if (getClass() != obj.getClass()) {
+        if (!super.equals(o)) {
             return false;
         }
-        final StringCustomField other = (StringCustomField) obj;
-        if (name == null) {
-            if (other.name != null) {
-                return false;
-            }
-        } else if (!name.equals(other.name)) {
+
+        final StringCustomField that = (StringCustomField) o;
+
+        if (fieldName != null ? !fieldName.equals(that.fieldName) : that.fieldName != null) {
+            return false;
+        }
+        if (fieldValue != null ? !fieldValue.equals(that.fieldValue) : that.fieldValue != null) {
+            return false;
+        }
+        if (objectId != null ? !objectId.equals(that.objectId) : that.objectId != null) {
             return false;
         }
-        if (value == null) {
-            if (other.value != null) {
-                return false;
-            }
-        } else if (!value.equals(other.value)) {
+        if (objectType != that.objectType) {
             return false;
         }
+
         return true;
     }
 
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (fieldName != null ? fieldName.hashCode() : 0);
+        result = 31 * result + (fieldValue != null ? fieldValue.hashCode() : 0);
+        result = 31 * result + (objectId != null ? objectId.hashCode() : 0);
+        result = 31 * result + (objectType != null ? objectType.hashCode() : 0);
+        return result;
+    }
 }
diff --git a/util/src/main/java/com/ning/billing/util/dao/AuditLogMapper.java b/util/src/main/java/com/ning/billing/util/dao/AuditLogMapper.java
index 94e8e92..23dd0ea 100644
--- a/util/src/main/java/com/ning/billing/util/dao/AuditLogMapper.java
+++ b/util/src/main/java/com/ning/billing/util/dao/AuditLogMapper.java
@@ -34,18 +34,19 @@ public class AuditLogMapper extends MapperBase implements ResultSetMapper<AuditL
 
     @Override
     public AuditLog map(final int index, final ResultSet r, final StatementContext ctx) throws SQLException {
+        final UUID id = getUUID(r, "id");
         final String tableName = r.getString("table_name");
-        final long recordId = r.getLong("record_id");
+        final long targetRecordId = r.getLong("target_record_id");
         final String changeType = r.getString("change_type");
-        final DateTime changeDate = getDateTime(r, "change_date");
-        final String changedBy = r.getString("changed_by");
+        final DateTime createdDate = getDateTime(r, "created_date");
+        final String createdBy = r.getString("created_by");
         final String reasonCode = r.getString("reason_code");
         final String comments = r.getString("comments");
         final UUID userToken = getUUID(r, "user_token");
 
-        final EntityAudit entityAudit = new EntityAudit(TableName.valueOf(tableName), recordId, ChangeType.valueOf(changeType));
+        final EntityAudit entityAudit = new EntityAudit(id, TableName.valueOf(tableName), targetRecordId, ChangeType.valueOf(changeType), createdDate);
         // TODO - we have the tenant_record_id but not the tenant id here
-        final CallContext callContext = new DefaultCallContext(null, changedBy, changeDate, reasonCode, comments, userToken);
+        final CallContext callContext = new DefaultCallContext(null, createdBy, createdDate, reasonCode, comments, userToken);
         return new DefaultAuditLog(entityAudit, callContext);
     }
 }
diff --git a/util/src/main/java/com/ning/billing/util/dao/AuditSqlDao.java b/util/src/main/java/com/ning/billing/util/dao/AuditSqlDao.java
index a4540b6..5f13d83 100644
--- a/util/src/main/java/com/ning/billing/util/dao/AuditSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/dao/AuditSqlDao.java
@@ -19,49 +19,49 @@ package com.ning.billing.util.dao;
 import java.util.List;
 
 import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlBatch;
 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.customizers.RegisterMapper;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 
 import com.ning.billing.util.audit.AuditLog;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 
-@ExternalizedSqlViaStringTemplate3
+@EntitySqlDaoStringTemplate("/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg")
 @RegisterMapper(AuditLogMapper.class)
 public interface AuditSqlDao {
 
     @SqlUpdate
-    public void insertAuditFromTransaction(@AuditBinder final EntityAudit audit,
-                                           @InternalTenantContextBinder final InternalCallContext context);
+    public void insertAuditFromTransaction(@BindBean final EntityAudit audit,
+                                           @BindBean final InternalCallContext context);
 
     @SqlBatch(transactional = false)
-    public void insertAuditFromTransaction(@AuditBinder final List<EntityAudit> audit,
-                                           @InternalTenantContextBinder final InternalCallContext context);
+    public void insertAuditFromTransaction(@BindBean final List<EntityAudit> audit,
+                                           @BindBean final InternalCallContext context);
 
     @SqlQuery
-    public List<AuditLog> getAuditLogsForRecordId(@TableNameBinder final TableName tableName,
-                                                  @Bind("recordId") final long recordId,
-                                                  @InternalTenantContextBinder final InternalTenantContext context);
+    public List<AuditLog> getAuditLogsForTargetRecordId(@BindBean final TableName tableName,
+                                                        @Bind("targetRecordId") final long targetRecordId,
+                                                        @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    public Long getRecordId(@Bind("id") final String id, @InternalTenantContextBinder final InternalTenantContext context);
+    public Long getRecordId(@Bind("id") final String id, @BindBean final InternalTenantContext context);
 
     @SqlQuery
     public Long getRecordIdForTable(@Define("tableName") final String tableName,
                                     @Bind("id") final String id,
-                                    @InternalTenantContextBinder final InternalTenantContext context);
+                                    @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    public List<Long> getHistoryRecordIdsForTable(@Define("tableName") final String tableName,
-                                                  @Bind("id") final String id,
-                                                  @InternalTenantContextBinder final InternalTenantContext context);
+    public List<Long> getHistoryRecordIdsForTable(@Define("historyTableName") final String historyTableName,
+                                                  @Bind("targetRecordId") final Long targetRecordId,
+                                                  @BindBean final InternalTenantContext context);
 
     @SqlQuery
     public Long getHistoryRecordId(@Bind("recordId") final Long recordId,
-                                   @InternalTenantContextBinder final InternalTenantContext context);
+                                   @BindBean final InternalTenantContext context);
 }
diff --git a/util/src/main/java/com/ning/billing/util/dao/BinderBase.java b/util/src/main/java/com/ning/billing/util/dao/BinderBase.java
index 7949fb7..27cb985 100644
--- a/util/src/main/java/com/ning/billing/util/dao/BinderBase.java
+++ b/util/src/main/java/com/ning/billing/util/dao/BinderBase.java
@@ -17,7 +17,6 @@
 package com.ning.billing.util.dao;
 
 import java.util.Date;
-import java.util.UUID;
 
 import org.joda.time.DateTime;
 
@@ -25,12 +24,4 @@ public abstract class BinderBase {
     protected Date getDate(final DateTime dateTime) {
         return dateTime == null ? null : dateTime.toDate();
     }
-
-    protected String uuidToString(final UUID uuid) {
-        if (uuid == null) {
-            return null;
-        } else {
-            return uuid.toString();
-        }
-    }
 }
diff --git a/util/src/main/java/com/ning/billing/util/dao/DateTimeArgumentFactory.java b/util/src/main/java/com/ning/billing/util/dao/DateTimeArgumentFactory.java
new file mode 100644
index 0000000..1e0cfc6
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/dao/DateTimeArgumentFactory.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.dao;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.sql.Types;
+
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.tweak.Argument;
+import org.skife.jdbi.v2.tweak.ArgumentFactory;
+
+public class DateTimeArgumentFactory implements ArgumentFactory<DateTime> {
+
+    @Override
+    public boolean accepts(final Class<?> expectedType, final Object value, final StatementContext ctx) {
+        return value instanceof DateTime;
+    }
+
+    @Override
+    public Argument build(final Class<?> expectedType, final DateTime value, final StatementContext ctx) {
+        return new DateTimeArgument(value);
+    }
+
+    public static class DateTimeArgument implements Argument {
+
+        private final DateTime value;
+
+        public DateTimeArgument(final DateTime value) {
+            this.value = value;
+        }
+
+        @Override
+        public void apply(final int position, final PreparedStatement statement, final StatementContext ctx) throws SQLException {
+            if (value != null) {
+                statement.setTimestamp(position, new Timestamp(value.toDate().getTime()));
+            } else {
+                statement.setNull(position, Types.TIMESTAMP);
+            }
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("DateTimeArgument");
+            sb.append("{value=").append(value);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+}
diff --git a/util/src/main/java/com/ning/billing/util/dao/DateTimeZoneArgumentFactory.java b/util/src/main/java/com/ning/billing/util/dao/DateTimeZoneArgumentFactory.java
new file mode 100644
index 0000000..97fe381
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/dao/DateTimeZoneArgumentFactory.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.dao;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.joda.time.DateTimeZone;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.tweak.Argument;
+import org.skife.jdbi.v2.tweak.ArgumentFactory;
+
+public class DateTimeZoneArgumentFactory implements ArgumentFactory<DateTimeZone> {
+
+    @Override
+    public boolean accepts(final Class<?> expectedType, final Object value, final StatementContext ctx) {
+        return value instanceof DateTimeZone;
+    }
+
+    @Override
+    public Argument build(final Class<?> expectedType, final DateTimeZone value, final StatementContext ctx) {
+        return new DateTimeZoneArgument(value);
+    }
+
+    public class DateTimeZoneArgument implements Argument {
+
+        private final DateTimeZone value;
+
+        public DateTimeZoneArgument(final DateTimeZone value) {
+            this.value = value;
+        }
+
+        @Override
+        public void apply(final int position, final PreparedStatement statement, final StatementContext ctx) throws SQLException {
+            if (value != null) {
+                statement.setString(position, value.toString());
+            } else {
+                statement.setNull(position, Types.VARCHAR);
+            }
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("DateTimeZoneArgument");
+            sb.append("{value=").append(value);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/dao/EntityAudit.java b/util/src/main/java/com/ning/billing/util/dao/EntityAudit.java
index 25930c7..a41b882 100644
--- a/util/src/main/java/com/ning/billing/util/dao/EntityAudit.java
+++ b/util/src/main/java/com/ning/billing/util/dao/EntityAudit.java
@@ -16,28 +16,83 @@
 
 package com.ning.billing.util.dao;
 
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
 import com.ning.billing.util.audit.ChangeType;
+import com.ning.billing.util.entity.EntityBase;
 
-public class EntityAudit {
+public class EntityAudit extends EntityBase {
+    
     private final TableName tableName;
-    private final Long recordId;
+    private final Long targetRecordId;
     private final ChangeType changeType;
 
-    public EntityAudit(final TableName tableName, final Long recordId, final ChangeType changeType) {
+    public EntityAudit(final UUID entityId, final TableName tableName, final Long targetRecordId, final ChangeType changeType, final DateTime createdDate) {
+        super(entityId, createdDate, null);
         this.tableName = tableName;
-        this.recordId = recordId;
+        this.targetRecordId = targetRecordId;
         this.changeType = changeType;
+
+    }
+    public EntityAudit(final TableName tableName, final Long targetRecordId, final ChangeType changeType, final DateTime createdDate) {
+        this(UUID.randomUUID(), tableName, targetRecordId, changeType, createdDate);
     }
 
     public TableName getTableName() {
         return tableName;
     }
 
-    public Long getRecordId() {
-        return recordId;
+    public Long getTargetRecordId() {
+        return targetRecordId;
     }
 
     public ChangeType getChangeType() {
         return changeType;
     }
+
+    @Override
+    public String toString() {
+        return "EntityAudit{" +
+               "tableName=" + tableName +
+               ", targetRecordId=" + targetRecordId +
+               ", changeType=" + changeType +
+               '}';
+    }
+
+    @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 EntityAudit that = (EntityAudit) o;
+
+        if (changeType != that.changeType) {
+            return false;
+        }
+        if (tableName != that.tableName) {
+            return false;
+        }
+        if (targetRecordId != null ? !targetRecordId.equals(that.targetRecordId) : that.targetRecordId != null) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (tableName != null ? tableName.hashCode() : 0);
+        result = 31 * result + (targetRecordId != null ? targetRecordId.hashCode() : 0);
+        result = 31 * result + (changeType != null ? changeType.hashCode() : 0);
+        return result;
+    }
 }
diff --git a/util/src/main/java/com/ning/billing/util/dao/EnumArgumentFactory.java b/util/src/main/java/com/ning/billing/util/dao/EnumArgumentFactory.java
new file mode 100644
index 0000000..d47cb64
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/dao/EnumArgumentFactory.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.dao;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.tweak.Argument;
+import org.skife.jdbi.v2.tweak.ArgumentFactory;
+
+public class EnumArgumentFactory implements ArgumentFactory<Enum> {
+
+    @Override
+    public Argument build(final Class<?> expectedType, final Enum value, final StatementContext ctx) {
+        return new StringArgument(value.toString());
+    }
+
+    class StringArgument implements Argument {
+
+        private final String value;
+
+        StringArgument(final String value) {
+            this.value = value;
+        }
+
+        public void apply(final int position, final PreparedStatement statement, final StatementContext ctx) throws SQLException {
+            if (value != null) {
+                statement.setString(position, value);
+            } else {
+                statement.setNull(position, Types.VARCHAR);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "'" + value + "'";
+        }
+    }
+
+    @Override
+    public boolean accepts(final Class expectedType, final Object value, final StatementContext ctx) {
+        return value != null && (value instanceof Enum /* Works for Enum inside classes */ || value.getClass().isEnum());
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/dao/HistorySqlDao.java b/util/src/main/java/com/ning/billing/util/dao/HistorySqlDao.java
index 15a2ac5..c69684f 100644
--- a/util/src/main/java/com/ning/billing/util/dao/HistorySqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/dao/HistorySqlDao.java
@@ -16,21 +16,16 @@
 
 package com.ning.billing.util.dao;
 
-import java.util.List;
-
-import org.skife.jdbi.v2.sqlobject.SqlBatch;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 
 import com.ning.billing.util.callcontext.InternalCallContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
 import com.ning.billing.util.entity.Entity;
+import com.ning.billing.util.entity.dao.EntityModelDao;
 
-public interface HistorySqlDao<T extends Entity> {
-    @SqlBatch(transactional = false)
-    public void batchAddHistoryFromTransaction(List<EntityHistory<T>> histories,
-                                               @InternalTenantContextBinder InternalCallContext context);
+public interface HistorySqlDao<M extends EntityModelDao<E>, E extends Entity> {
 
     @SqlUpdate
-    public void addHistoryFromTransaction(EntityHistory<T> history,
-                                          @InternalTenantContextBinder InternalCallContext context);
+    public void addHistoryFromTransaction(@EntityHistoryBinder EntityHistoryModelDao<M, E> history,
+                                          @BindBean InternalCallContext context);
 }
diff --git a/util/src/main/java/com/ning/billing/util/dao/LocalDateArgumentFactory.java b/util/src/main/java/com/ning/billing/util/dao/LocalDateArgumentFactory.java
new file mode 100644
index 0000000..6bb2e6d
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/dao/LocalDateArgumentFactory.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.dao;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.joda.time.LocalDate;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.tweak.Argument;
+import org.skife.jdbi.v2.tweak.ArgumentFactory;
+
+public class LocalDateArgumentFactory implements ArgumentFactory<LocalDate> {
+
+    @Override
+    public boolean accepts(final Class<?> expectedType, final Object value, final StatementContext ctx) {
+        return value instanceof LocalDate;
+    }
+
+    @Override
+    public Argument build(final Class<?> expectedType, final LocalDate value, final StatementContext ctx) {
+        return new LocalDateArgument(value);
+    }
+
+    public static class LocalDateArgument implements Argument {
+
+        private final LocalDate value;
+
+        public LocalDateArgument(final LocalDate value) {
+            this.value = value;
+        }
+
+        @Override
+        public void apply(final int position, final PreparedStatement statement, final StatementContext ctx) throws SQLException {
+            if (value != null) {
+                // ISO8601 format
+                statement.setString(position, value.toString());
+            } else {
+                statement.setNull(position, Types.VARCHAR);
+            }
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("LocalDateArgument");
+            sb.append("{value=").append(value);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+}
diff --git a/util/src/main/java/com/ning/billing/util/dao/LowerToCamelBeanMapper.java b/util/src/main/java/com/ning/billing/util/dao/LowerToCamelBeanMapper.java
new file mode 100644
index 0000000..67c66e5
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/dao/LowerToCamelBeanMapper.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.dao;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.sql.Date;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+import com.google.common.base.CaseFormat;
+
+// Identical to org.skife.jdbi.v2.BeanMapper but maps created_date to createdDate
+public class LowerToCamelBeanMapper<T> implements ResultSetMapper<T> {
+
+    private final Class<T> type;
+    private final Map<String, PropertyDescriptor> properties = new HashMap<String, PropertyDescriptor>();
+
+    public LowerToCamelBeanMapper(final Class<T> type) {
+        this.type = type;
+        try {
+            final BeanInfo info = Introspector.getBeanInfo(type);
+
+            for (final PropertyDescriptor descriptor : info.getPropertyDescriptors()) {
+                properties.put(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, descriptor.getName()).toLowerCase(), descriptor);
+            }
+        } catch (IntrospectionException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    public T map(final int row, final ResultSet rs, final StatementContext ctx) throws SQLException {
+        final T bean;
+        try {
+            bean = type.newInstance();
+        } catch (Exception e) {
+            throw new IllegalArgumentException(String.format("A bean, %s, was mapped " +
+                                                             "which was not instantiable", type.getName()),
+                                               e);
+        }
+
+        final Class beanClass = bean.getClass();
+        final ResultSetMetaData metadata = rs.getMetaData();
+
+        for (int i = 1; i <= metadata.getColumnCount(); ++i) {
+            final String name = metadata.getColumnLabel(i).toLowerCase();
+
+            final PropertyDescriptor descriptor = properties.get(name);
+
+            if (descriptor != null) {
+                final Class<?> type = descriptor.getPropertyType();
+
+                Object value;
+
+                if (type.isAssignableFrom(Boolean.class) || type.isAssignableFrom(boolean.class)) {
+                    value = rs.getBoolean(i);
+                } else if (type.isAssignableFrom(Byte.class) || type.isAssignableFrom(byte.class)) {
+                    value = rs.getByte(i);
+                } else if (type.isAssignableFrom(Short.class) || type.isAssignableFrom(short.class)) {
+                    value = rs.getShort(i);
+                } else if (type.isAssignableFrom(Integer.class) || type.isAssignableFrom(int.class)) {
+                    value = rs.getInt(i);
+                } else if (type.isAssignableFrom(Long.class) || type.isAssignableFrom(long.class)) {
+                    value = rs.getLong(i);
+                } else if (type.isAssignableFrom(Float.class) || type.isAssignableFrom(float.class)) {
+                    value = rs.getFloat(i);
+                } else if (type.isAssignableFrom(Double.class) || type.isAssignableFrom(double.class)) {
+                    value = rs.getDouble(i);
+                } else if (type.isAssignableFrom(BigDecimal.class)) {
+                    value = rs.getBigDecimal(i);
+                } else if (type.isAssignableFrom(DateTime.class)) {
+                    final Timestamp timestamp = rs.getTimestamp(i);
+                    value = timestamp == null ? null : new DateTime(timestamp).toDateTime(DateTimeZone.UTC);
+                } else if (type.isAssignableFrom(Time.class)) {
+                    value = rs.getTime(i);
+                } else if (type.isAssignableFrom(LocalDate.class)) {
+                    final Date date = rs.getDate(i);
+                    value = date == null ? null : new LocalDate(date, DateTimeZone.UTC);
+                } else if (type.isAssignableFrom(DateTimeZone.class)) {
+                    final String dateTimeZoneString = rs.getString(i);
+                    value = dateTimeZoneString == null ? null : DateTimeZone.forID(dateTimeZoneString);
+                } else if (type.isAssignableFrom(String.class)) {
+                    value = rs.getString(i);
+                } else if (type.isAssignableFrom(UUID.class)) {
+                    final String uuidString = rs.getString(i);
+                    value = uuidString == null ? null : UUID.fromString(uuidString);
+                } else if (type.isEnum()) {
+                    final String enumString = rs.getString(i);
+                    //noinspection unchecked
+                    value = enumString == null ? null : Enum.valueOf((Class<Enum>) type, enumString);
+                } else {
+                    value = rs.getObject(i);
+                }
+
+                if (rs.wasNull() && !type.isPrimitive()) {
+                    value = null;
+                }
+
+                try {
+                    final Method writeMethod = descriptor.getWriteMethod();
+                    if (writeMethod != null) {
+                        writeMethod.invoke(bean, value);
+                    } else {
+                        final String camelCasedName = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name);
+                        final Field field = getField(beanClass, camelCasedName);
+                        field.setAccessible(true); // Often private...
+                        field.set(bean, value);
+                    }
+                } catch (NoSuchFieldException e) {
+                    throw new IllegalArgumentException(String.format("Unable to find field for " +
+                                                                     "property, %s", name), e);
+                } catch (IllegalAccessException e) {
+                    throw new IllegalArgumentException(String.format("Unable to access setter for " +
+                                                                     "property, %s", name), e);
+                } catch (InvocationTargetException e) {
+                    throw new IllegalArgumentException(String.format("Invocation target exception trying to " +
+                                                                     "invoker setter for the %s property", name), e);
+                } catch (NullPointerException e) {
+                    throw new IllegalArgumentException(String.format("No appropriate method to " +
+                                                                     "write value %s ", value.toString()), e);
+                }
+            }
+        }
+
+        return bean;
+    }
+
+    private static Field getField(final Class clazz, final String fieldName) throws NoSuchFieldException {
+        try {
+            return clazz.getDeclaredField(fieldName);
+        } catch (NoSuchFieldException e) {
+            // Go up in the hierarchy
+            final Class superClass = clazz.getSuperclass();
+            if (superClass == null) {
+                throw e;
+            } else {
+                return getField(superClass, fieldName);
+            }
+        }
+    }
+}
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 82cc375..0e4da16 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
@@ -20,19 +20,23 @@ import javax.annotation.Nullable;
 
 import com.ning.billing.ObjectType;
 
+/**
+ * Map table names to entity object types and classes, and history tables (if exists)
+ */
 public enum TableName {
     ACCOUNT_HISTORY("account_history"),
     ACCOUNT("accounts", ObjectType.ACCOUNT, ACCOUNT_HISTORY),
     ACCOUNT_EMAIL_HISTORY("account_email_history"),
     ACCOUNT_EMAIL("account_emails", ObjectType.ACCOUNT_EMAIL, ACCOUNT_EMAIL_HISTORY),
     BUNDLES("bundles", ObjectType.BUNDLE),
+    BLOCKING_STATES("blocking_states"),
     CUSTOM_FIELD_HISTORY("custom_field_history"),
-    CUSTOM_FIELD("custom_fields", CUSTOM_FIELD_HISTORY),
+    CUSTOM_FIELD("custom_fields", ObjectType.CUSTOM_FIELD, CUSTOM_FIELD_HISTORY),
     INVOICE_ITEMS("invoice_items", ObjectType.INVOICE_ITEM),
     INVOICE_PAYMENTS("invoice_payments", ObjectType.INVOICE_PAYMENT),
     INVOICES("invoices", ObjectType.INVOICE),
     PAYMENT_ATTEMPT_HISTORY("payment_attempt_history"),
-    PAYMENT_ATTEMPTS("payment_attempts", PAYMENT_ATTEMPT_HISTORY),
+    PAYMENT_ATTEMPTS("payment_attempts", ObjectType.PAYMENT_ATTEMPT, PAYMENT_ATTEMPT_HISTORY),
     PAYMENT_HISTORY("payment_history"),
     PAYMENTS("payments", ObjectType.PAYMENT, PAYMENT_HISTORY),
     PAYMENT_METHOD_HISTORY("payment_method_history"),
@@ -45,7 +49,8 @@ public enum TableName {
     TAG_DEFINITIONS("tag_definitions", ObjectType.TAG_DEFINITION, TAG_DEFINITION_HISTORY),
     TAG_HISTORY("tag_history"),
     TENANT("tenants", ObjectType.TENANT),
-    TAG("tags", TAG_HISTORY);
+    TENANT_KVS("tenant_kvs", ObjectType.TENANT_KVS),
+    TAG("tags", ObjectType.TAG, TAG_HISTORY);
 
     private final String tableName;
     private final ObjectType objectType;
@@ -57,14 +62,10 @@ public enum TableName {
         this.historyTableName = historyTableName;
     }
 
-    TableName(final String tableName, @Nullable final ObjectType objectType) {
+    TableName(final String tableName, final ObjectType objectType) {
         this(tableName, objectType, null);
     }
 
-    TableName(final String tableName, @Nullable final TableName historyTableName) {
-        this(tableName, null, historyTableName);
-    }
-
     TableName(final String tableName) {
         this(tableName, null, null);
     }
diff --git a/util/src/main/java/com/ning/billing/util/dao/UUIDArgumentFactory.java b/util/src/main/java/com/ning/billing/util/dao/UUIDArgumentFactory.java
new file mode 100644
index 0000000..385314b
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/dao/UUIDArgumentFactory.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.dao;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.UUID;
+
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.tweak.Argument;
+import org.skife.jdbi.v2.tweak.ArgumentFactory;
+
+public class UUIDArgumentFactory implements ArgumentFactory<UUID> {
+
+    @Override
+    public boolean accepts(final Class<?> expectedType, final Object value, final StatementContext ctx) {
+        return value instanceof UUID;
+    }
+
+    @Override
+    public Argument build(final Class<?> expectedType, final UUID value, final StatementContext ctx) {
+        return new UUIDArgument(value);
+    }
+
+    public class UUIDArgument implements Argument {
+
+        private final UUID value;
+
+        public UUIDArgument(final UUID value) {
+            this.value = value;
+        }
+
+        @Override
+        public void apply(final int position, final PreparedStatement statement, final StatementContext ctx) throws SQLException {
+            if (value != null) {
+                statement.setString(position, value.toString());
+            } else {
+                statement.setNull(position, Types.VARCHAR);
+            }
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("UUIDArgument");
+            sb.append("{value=").append(value);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/Audited.java b/util/src/main/java/com/ning/billing/util/entity/dao/Audited.java
new file mode 100644
index 0000000..dc4b578
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/Audited.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.entity.dao;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.ning.billing.util.audit.ChangeType;
+
+/**
+ * The <code>Audited</code> annotation wraps a Sql dao method and
+ * create Audit and History entries as needed. Every r/w
+ * database operation on any Entity should have this annotation.
+ * <p/>
+ * To create a audit entries automatically for some method <code>updateChargedThroughDate</code>:
+ * <pre>
+ *         @Audited(type = ChangeType.UPDATE)
+ *         @SqlUpdate public void updateChargedThroughDate(@Bind("id") String id,
+ *                                                         @Bind("chargedThroughDate") Date chargedThroughDate,
+ *                                                         @InternalTenantContextBinder final InternalCallContext context);
+ * </pre>
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface Audited {
+
+    /**
+     * @return the type of operation
+     */
+    ChangeType value();
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntityDao.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntityDao.java
index 370f0e9..cb5ec2a 100644
--- a/util/src/main/java/com/ning/billing/util/entity/dao/EntityDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntityDao.java
@@ -19,22 +19,22 @@ package com.ning.billing.util.entity.dao;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.BillingExceptionBase;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.entity.Entity;
-import com.ning.billing.util.entity.EntityPersistenceException;
 
-public interface EntityDao<T extends Entity> {
+public interface EntityDao<M extends EntityModelDao<E>, E extends Entity, U extends BillingExceptionBase> {
 
-    public void create(T entity, InternalCallContext context) throws EntityPersistenceException;
+    public void create(M entity, InternalCallContext context) throws U;
 
     public Long getRecordId(UUID id, InternalTenantContext context);
 
-    public T getByRecordId(Long recordId, InternalTenantContext context);
+    public M getByRecordId(Long recordId, InternalTenantContext context);
 
-    public T getById(UUID id, InternalTenantContext context);
+    public M getById(UUID id, InternalTenantContext context);
 
-    public List<T> get(InternalTenantContext context);
+    public List<M> get(InternalTenantContext context);
 
     public void test(InternalTenantContext context);
 }
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntityDaoBase.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntityDaoBase.java
new file mode 100644
index 0000000..e4718c3
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntityDaoBase.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.entity.dao;
+
+import java.util.List;
+import java.util.UUID;
+
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.util.audit.ChangeType;
+import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.entity.Entity;
+
+public abstract class EntityDaoBase<M extends EntityModelDao<E>, E extends Entity, U extends BillingExceptionBase> implements EntityDao<M, E, U> {
+
+    protected final EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao;
+
+    private final Class<? extends EntitySqlDao<M, E>> realSqlDao;
+
+    public EntityDaoBase(final EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao, final Class<? extends EntitySqlDao<M, E>> realSqlDao) {
+        this.transactionalSqlDao = transactionalSqlDao;
+        this.realSqlDao = realSqlDao;
+    }
+
+    @Override
+    public void create(final M entity, final InternalCallContext context) throws U {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final EntitySqlDao<M, E> transactional = entitySqlDaoWrapperFactory.become(realSqlDao);
+
+                if (transactional.getById(entity.getId().toString(), context) != null) {
+                    throw generateAlreadyExistsException(entity, context);
+                }
+                transactional.create(entity, context);
+
+                final M refreshedEntity = transactional.getById(entity.getId().toString(), context);
+
+                postBusEventFromTransaction(entity, refreshedEntity, ChangeType.INSERT, entitySqlDaoWrapperFactory, context);
+                return null;
+            }
+        });
+    }
+
+    protected void postBusEventFromTransaction(final M entity, final M savedEntity, final ChangeType changeType,
+                                               final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory,
+                                               final InternalCallContext context) throws BillingExceptionBase {
+    }
+
+    protected abstract U generateAlreadyExistsException(final M entity, final InternalCallContext context);
+
+    @Override
+    public Long getRecordId(final UUID id, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Long>() {
+
+            @Override
+            public Long inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final EntitySqlDao<M, E> transactional = entitySqlDaoWrapperFactory.become(realSqlDao);
+                return transactional.getRecordId(id.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public M getByRecordId(final Long recordId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<M>() {
+
+            @Override
+            public M inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final EntitySqlDao<M, E> transactional = entitySqlDaoWrapperFactory.become(realSqlDao);
+                return transactional.getByRecordId(recordId, context);
+            }
+        });
+    }
+
+    @Override
+    public M getById(final UUID id, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<M>() {
+
+            @Override
+            public M inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final EntitySqlDao<M, E> transactional = entitySqlDaoWrapperFactory.become(realSqlDao);
+                return transactional.getById(id.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public List<M> get(final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<M>>() {
+
+            @Override
+            public List<M> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final EntitySqlDao<M, E> transactional = entitySqlDaoWrapperFactory.become(realSqlDao);
+                return transactional.get(context);
+            }
+        });
+    }
+
+    @Override
+    public void test(final InternalTenantContext context) {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final EntitySqlDao<M, E> transactional = entitySqlDaoWrapperFactory.become(realSqlDao);
+                transactional.test(context);
+                return null;
+            }
+        });
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntityModelDao.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntityModelDao.java
new file mode 100644
index 0000000..5e0e02f
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntityModelDao.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.entity.dao;
+
+import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.Entity;
+
+/**
+ * ModelDao classes represent the lowest level of Entity objects. There are used to generate
+ * SQL statements and retrieve objects from the database.
+ *
+ * @param <E> associated Entity object (used in EntitySqlDaoWrapperInvocationHandler)
+ */
+@SuppressWarnings("UnusedDeclaration")
+public interface EntityModelDao<E extends Entity> extends Entity {
+
+    /**
+     * Retrieve the TableName associated with this entity. This is used in
+     * EntitySqlDaoWrapperInvocationHandler for history and auditing purposes.
+     *
+     * @return the TableName object associated with this ModelDao entity
+     */
+    public TableName getTableName();
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java
index 9dd5589..58243d3 100644
--- a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java
@@ -22,38 +22,56 @@ import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
+import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
+import com.ning.billing.util.dao.AuditSqlDao;
+import com.ning.billing.util.dao.HistorySqlDao;
 import com.ning.billing.util.entity.Entity;
 import com.ning.billing.util.entity.EntityPersistenceException;
 
-public interface EntitySqlDao<T extends Entity> {
+// TODO get rid of Transmogrifier, but code does not compile even if we create the
+// method  public <T> T become(Class<T> typeToBecome); ?
+//
+@EntitySqlDaoStringTemplate
+public interface EntitySqlDao<M extends EntityModelDao<E>, E extends Entity> extends AuditSqlDao, HistorySqlDao<M, E>, Transmogrifier, Transactional<EntitySqlDao<M, E>>, CloseMe {
 
     @SqlUpdate
-    public void create(@BindBean final T entity,
-                       @InternalTenantContextBinder final InternalCallContext context) throws EntityPersistenceException;
+    @Audited(ChangeType.INSERT)
+    public void create(@BindBean final M entity,
+                       @BindBean final InternalCallContext context) throws EntityPersistenceException;
 
     @SqlQuery
-    public T getById(@Bind("id") final String id,
-                     @InternalTenantContextBinder final InternalTenantContext context);
+    public M getById(@Bind("id") final String id,
+                     @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    public T getByRecordId(@Bind("recordId") final Long recordId,
-                           @InternalTenantContextBinder final InternalTenantContext context);
+    public M getByRecordId(@Bind("recordId") final Long recordId,
+                           @BindBean final InternalTenantContext context);
 
     @SqlQuery
     public Long getRecordId(@Bind("id") final String id,
-                            @InternalTenantContextBinder final InternalTenantContext context);
+                            @BindBean final InternalTenantContext context);
 
+    // Given entity recordId find the history recordId (targetRecordId for history table = entity recordId)
     @SqlQuery
-    public Long getHistoryRecordId(@Bind("recordId") final Long recordId,
-                                   @InternalTenantContextBinder final InternalTenantContext context);
+    public Long getHistoryRecordId(@Bind("targetRecordId") final Long targetRecordId,
+                                   @BindBean final InternalTenantContext context);
 
+    // Given history recordId find the entity recordId (targetRecordId for history table = entity recordId)
     @SqlQuery
-    public List<T> get(@InternalTenantContextBinder final InternalTenantContext context);
+    public Long getHistoryTargetRecordId(@Bind("recordId") final Long recordId,
+                                         @BindBean final InternalTenantContext context);
+
+    @SqlQuery
+    public List<M> get(@BindBean final InternalTenantContext context);
 
     @SqlUpdate
-    public void test(@InternalTenantContextBinder final InternalTenantContext context);
+    public void test(@BindBean final InternalTenantContext context);
+
+
 }
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoStringTemplate.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoStringTemplate.java
new file mode 100644
index 0000000..f96ea42
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoStringTemplate.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.entity.dao;
+
+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;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+
+import org.skife.jdbi.v2.Query;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.sqlobject.SqlStatementCustomizer;
+import org.skife.jdbi.v2.sqlobject.SqlStatementCustomizingAnnotation;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.StringTemplate3StatementLocator;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.UseStringTemplate3StatementLocator;
+import org.skife.jdbi.v2.tweak.StatementLocator;
+
+import com.ning.billing.util.dao.LowerToCamelBeanMapperFactory;
+import com.ning.billing.util.entity.Entity;
+
+@SqlStatementCustomizingAnnotation(EntitySqlDaoStringTemplate.EntitySqlDaoLocatorFactory.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface EntitySqlDaoStringTemplate {
+
+    static final String DEFAULT_VALUE = " ~ ";
+
+    String value() default DEFAULT_VALUE;
+
+    public static class EntitySqlDaoLocatorFactory extends UseStringTemplate3StatementLocator.LocatorFactory {
+
+        public SqlStatementCustomizer createForType(final Annotation annotation, final Class sqlObjectType) {
+            // From http://www.antlr.org/wiki/display/ST/ST+condensed+--+Templates+and+groups#STcondensed--Templatesandgroups-Withsupergroupfile:
+            //     there is no mechanism for automatically loading a mentioned super-group file
+            new StringTemplate3StatementLocator(EntitySqlDao.class, true, true);
+
+            final EntitySqlDaoStringTemplate a = (EntitySqlDaoStringTemplate) annotation;
+            final StatementLocator l;
+            if (DEFAULT_VALUE.equals(a.value())) {
+                l = new StringTemplate3StatementLocator(sqlObjectType, true, true);
+            } else {
+                l = new StringTemplate3StatementLocator(a.value(), true, true);
+            }
+
+            return new SqlStatementCustomizer() {
+                public void apply(final SQLStatement statement) {
+                    statement.setStatementLocator(l);
+
+                    if (statement instanceof Query) {
+                        final Query query = (Query) statement;
+
+                        // Find the model class associated with this sqlObjectType (which is a SqlDao class) to register its mapper
+                        // If a custom mapper is defined via @RegisterMapper, don't register our generic one
+                        if (sqlObjectType.getGenericInterfaces() != null &&
+                            sqlObjectType.getAnnotation(RegisterMapper.class) == null) {
+                            for (int i = 0; i < sqlObjectType.getGenericInterfaces().length; i++) {
+                                if (sqlObjectType.getGenericInterfaces()[i] instanceof ParameterizedType) {
+                                    final ParameterizedType type = (ParameterizedType) sqlObjectType.getGenericInterfaces()[i];
+                                    for (int j = 0; j < type.getActualTypeArguments().length; j++) {
+                                        final Class modelClazz = (Class) type.getActualTypeArguments()[j];
+                                        if (Entity.class.isAssignableFrom(modelClazz)) {
+                                            query.registerMapper(new LowerToCamelBeanMapperFactory(modelClazz));
+                                        }
+                                    }
+                                }
+
+                            }
+                        }
+                    }
+                }
+            };
+        }
+
+        public SqlStatementCustomizer createForMethod(final Annotation annotation,
+                                                      final Class sqlObjectType,
+                                                      final Method method) {
+            throw new UnsupportedOperationException("Not Defined on Method");
+        }
+
+        public SqlStatementCustomizer createForParameter(final Annotation annotation,
+                                                         final Class sqlObjectType,
+                                                         final Method method,
+                                                         final Object arg) {
+            throw new UnsupportedOperationException("Not defined on parameter");
+        }
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
new file mode 100644
index 0000000..13ba96c
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.entity.dao;
+
+import org.skife.jdbi.v2.IDBI;
+import org.skife.jdbi.v2.Transaction;
+import org.skife.jdbi.v2.TransactionStatus;
+
+import com.ning.billing.util.entity.Entity;
+
+/**
+ * Transaction manager for EntitySqlDao queries
+ */
+public class EntitySqlDaoTransactionalJdbiWrapper {
+
+    private final IDBI dbi;
+
+    public EntitySqlDaoTransactionalJdbiWrapper(final IDBI dbi) {
+        this.dbi = dbi;
+    }
+
+    class JdbiTransaction<ReturnType, M extends EntityModelDao<E>, E extends Entity> implements Transaction<ReturnType, EntitySqlDao<M, E>> {
+
+        private final EntitySqlDaoTransactionWrapper<ReturnType> entitySqlDaoTransactionWrapper;
+
+        JdbiTransaction(final EntitySqlDaoTransactionWrapper<ReturnType> entitySqlDaoTransactionWrapper) {
+            this.entitySqlDaoTransactionWrapper = entitySqlDaoTransactionWrapper;
+        }
+
+        @Override
+        public ReturnType inTransaction(final EntitySqlDao<M, E> transactionalSqlDao, final TransactionStatus status) throws Exception {
+            final EntitySqlDaoWrapperFactory<EntitySqlDao> factoryEntitySqlDao = new EntitySqlDaoWrapperFactory<EntitySqlDao>(transactionalSqlDao);
+            return entitySqlDaoTransactionWrapper.inTransaction(factoryEntitySqlDao);
+        }
+    }
+
+    // To handle warnings only
+    interface InitialEntitySqlDao extends EntitySqlDao<EntityModelDao<Entity>, Entity> {}
+
+    /**
+     * @param entitySqlDaoTransactionWrapper transaction to execute
+     * @param <ReturnType>                   object type to return from the transaction
+     * @return result from the transaction fo type ReturnType
+     */
+    public <ReturnType> ReturnType execute(final EntitySqlDaoTransactionWrapper<ReturnType> entitySqlDaoTransactionWrapper) {
+        final EntitySqlDao<EntityModelDao<Entity>, Entity> entitySqlDao = dbi.onDemand(InitialEntitySqlDao.class);
+        return entitySqlDao.inTransaction(new JdbiTransaction<ReturnType, EntityModelDao<Entity>, Entity>(entitySqlDaoTransactionWrapper));
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperFactory.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperFactory.java
new file mode 100644
index 0000000..b9dd2d4
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperFactory.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.entity.dao;
+
+import java.lang.reflect.Proxy;
+
+import com.ning.billing.util.entity.Entity;
+
+/**
+ * Factory to create wrapped EntitySqlDao objects. During a transaction, make sure
+ * to create other EntitySqlDao objects via the #become call.
+ *
+ * @param <InitialSqlDao> EntitySqlDao type to create
+ * @see EntitySqlDaoWrapperInvocationHandler
+ */
+public class EntitySqlDaoWrapperFactory<InitialSqlDao extends EntitySqlDao> {
+
+    private final InitialSqlDao sqlDao;
+
+    public EntitySqlDaoWrapperFactory(final InitialSqlDao sqlDao) {
+        this.sqlDao = sqlDao;
+    }
+
+    /**
+     * Get an instance of a specified EntitySqlDao class, sharing the same database session as the
+     * initial sql dao class with which this wrapper factory was created.
+     *
+     * @param newSqlDaoClass the class to instantiate
+     * @param <NewSqlDao>    EntitySqlDao type to create
+     * @return instance of NewSqlDao
+     */
+    public <NewSqlDao extends EntitySqlDao<NewEntityModelDao, NewEntity>,
+            NewEntityModelDao extends EntityModelDao<NewEntity>,
+            NewEntity extends Entity> NewSqlDao become(final Class<NewSqlDao> newSqlDaoClass) {
+        return create(newSqlDaoClass, sqlDao.become(newSqlDaoClass));
+    }
+
+    public <SelfType> SelfType transmogrify(final Class<SelfType> newTransactionalClass) {
+        return sqlDao.become(newTransactionalClass);
+    }
+
+    private <NewSqlDao extends EntitySqlDao<NewEntityModelDao, NewEntity>,
+            NewEntityModelDao extends EntityModelDao<NewEntity>,
+            NewEntity extends Entity> NewSqlDao create(final Class<NewSqlDao> newSqlDaoClass, final NewSqlDao newSqlDao) {
+        final ClassLoader classLoader = newSqlDao.getClass().getClassLoader();
+        final Class[] interfacesToImplement = {newSqlDaoClass};
+        final EntitySqlDaoWrapperInvocationHandler<NewSqlDao, NewEntityModelDao, NewEntity> wrapperInvocationHandler =
+                new EntitySqlDaoWrapperInvocationHandler<NewSqlDao, NewEntityModelDao, NewEntity>(newSqlDaoClass, newSqlDao);
+
+        final Object newSqlDaoObject = Proxy.newProxyInstance(classLoader, interfacesToImplement, wrapperInvocationHandler);
+        return newSqlDaoClass.cast(newSqlDaoObject);
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
new file mode 100644
index 0000000..1ded618
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.entity.dao;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.skife.jdbi.v2.Binding;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.exceptions.DBIException;
+import org.skife.jdbi.v2.exceptions.StatementException;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.ning.billing.util.audit.ChangeType;
+import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.dao.EntityAudit;
+import com.ning.billing.util.dao.EntityHistoryModelDao;
+import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.Entity;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+
+/**
+ * Wraps an instance of EntitySqlDao, performing extra work around each method (Sql query)
+ *
+ * @param <S> EntitySqlDao type of the wrapped instance
+ * @param <M> EntityModel associated with S
+ * @param <E> Entity associated with M
+ */
+public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>, M extends EntityModelDao<E>, E extends Entity> implements InvocationHandler {
+
+    private final Logger logger = LoggerFactory.getLogger(EntitySqlDaoWrapperInvocationHandler.class);
+
+    private final Class<S> sqlDaoClass;
+    private final S sqlDao;
+
+    public EntitySqlDaoWrapperInvocationHandler(final Class<S> sqlDaoClass, final S sqlDao) {
+        this.sqlDaoClass = sqlDaoClass;
+        this.sqlDao = sqlDao;
+    }
+
+    @Override
+    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+        try {
+            return invokeSafely(proxy, method, args);
+        } catch (Throwable t) {
+            if (t.getCause() != null && t.getCause().getCause() != null && DBIException.class.isAssignableFrom(t.getCause().getClass())) {
+                // Likely a JDBC error, try to extract the SQL statement and JDBI bindings
+                if (t.getCause() instanceof StatementException) {
+                    final StatementContext statementContext = ((StatementException) t.getCause()).getStatementContext();
+
+                    if (statementContext != null) {
+                        // Grumble, we need to rely on the suxxor toString() method as nothing is exposed
+                        final Binding binding = statementContext.getBinding();
+
+                        final PreparedStatement statement = statementContext.getStatement();
+                        if (statement != null) {
+                            // Note: we rely on the JDBC driver to have a sane toString() method...
+                            errorDuringTransaction(t.getCause().getCause(), method, statement.toString() + "\n" + binding.toString());
+                        } else {
+                            errorDuringTransaction(t.getCause().getCause(), method, binding.toString());
+                        }
+
+                        // Never reached
+                        return null;
+                    }
+                }
+
+                errorDuringTransaction(t.getCause().getCause(), method);
+            } else if (t.getCause() != null) {
+                // t is likely not interesting (java.lang.reflect.InvocationTargetException)
+                errorDuringTransaction(t.getCause(), method);
+            } else {
+                errorDuringTransaction(t, method);
+            }
+        }
+
+        // Never reached
+        return null;
+    }
+
+    // Nice method name to ease debugging while looking at log files
+    private void errorDuringTransaction(final Throwable t, final Method method, final String extraErrorMessage) throws Throwable {
+        final StringBuilder errorMessageBuilder = new StringBuilder("Error during transaction for sql entity {} and method {}");
+        if (t instanceof SQLException) {
+            final SQLException sqlException = (SQLException) t;
+            errorMessageBuilder.append(" [SQL State: ")
+                               .append(sqlException.getSQLState())
+                               .append(", Vendor Error Code: ")
+                               .append(sqlException.getErrorCode())
+                               .append("]");
+        }
+        if (extraErrorMessage != null) {
+            // This is usually the SQL statement
+            errorMessageBuilder.append("\n").append(extraErrorMessage);
+        }
+        logger.warn(errorMessageBuilder.toString(), sqlDaoClass, method.getName());
+
+        // This is to avoid throwing an exception wrapped in an UndeclaredThrowableException
+        if (!(t instanceof RuntimeException)) {
+            throw new RuntimeException(t);
+        } else {
+            throw t;
+        }
+    }
+
+    private void errorDuringTransaction(final Throwable t, final Method method) throws Throwable {
+        errorDuringTransaction(t, method, null);
+    }
+
+    private Object invokeSafely(final Object proxy, final Method method, final Object[] args) throws Throwable {
+        final Audited annotation = method.getAnnotation(Audited.class);
+
+        InternalCallContext context = null;
+        List<String> entityIds = null;
+        final Map<String, M> entities = new HashMap<String, M>();
+        final Map<String, Long> entityRecordIds = new HashMap<String, Long>();
+        if (annotation != null) {
+            // There will be some work required after the statement is executed,
+            // get the id before in case the change is a delete
+            context = retrieveContextFromArguments(args);
+            entityIds = retrieveEntityIdsFromArguments(method, args);
+            for (final String entityId : entityIds) {
+                entities.put(entityId, sqlDao.getById(entityId, context));
+                entityRecordIds.put(entityId, sqlDao.getRecordId(entityId, context));
+            }
+        }
+
+        // Real jdbc call
+        final Object obj = method.invoke(sqlDao, args);
+
+        // Update audit and history if needed
+        if (annotation != null) {
+            final ChangeType changeType = annotation.value();
+
+            for (final String entityId : entityIds) {
+                updateHistoryAndAudit(entityId, entities, entityRecordIds, changeType, context);
+            }
+        }
+
+        return obj;
+    }
+
+    private void updateHistoryAndAudit(final String entityId, final Map<String, M> entities, final Map<String, Long> entityRecordIds,
+                                       final ChangeType changeType, final InternalCallContext context) {
+        // Make sure to re-hydrate the object (especially needed for create calls)
+        final M reHydratedEntity = sqlDao.getById(entityId, context);
+        final Long reHydratedEntityRecordId = sqlDao.getRecordId(entityId, context);
+        final M entity = Objects.firstNonNull(reHydratedEntity, entities.get(entityId));
+        final Long entityRecordId = Objects.firstNonNull(reHydratedEntityRecordId, entityRecordIds.get(entityId));
+        final TableName tableName = entity.getTableName();
+
+        // Note: audit entries point to the history record id
+        final Long historyRecordId;
+        if (tableName.getHistoryTableName() != null) {
+            historyRecordId = insertHistory(entityRecordId, entity, changeType, context);
+        } else {
+            historyRecordId = entityRecordId;
+        }
+
+        insertAudits(tableName, historyRecordId, changeType, context);
+    }
+
+    private List<String> retrieveEntityIdsFromArguments(final Method method, final Object[] args) {
+        final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
+        int i = -1;
+        for (final Object arg : args) {
+            i++;
+
+            // Assume the first argument of type Entity is our type of Entity (type U here)
+            // This is true for e.g. create calls
+            if (arg instanceof Entity) {
+                return ImmutableList.<String>of(((Entity) arg).getId().toString());
+            }
+
+            // For Batch calls, the first argument will be of type List<Entity>
+            if (arg instanceof Iterable) {
+                final Builder<String> entityIds = extractEntityIdsFromBatchArgument((Iterable) arg);
+                if (entityIds != null) {
+                    return entityIds.build();
+                }
+            }
+
+            // Otherwise, use the first String argument, annotated with @Bind("id")
+            // This is true for e.g. update calls
+            if (!(arg instanceof String)) {
+                continue;
+            }
+
+            for (final Annotation annotation : parameterAnnotations[i]) {
+                if (Bind.class.equals(annotation.annotationType()) && ("id").equals(((Bind) annotation).value())) {
+                    return ImmutableList.<String>of((String) arg);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private Builder<String> extractEntityIdsFromBatchArgument(final Iterable arg) {
+        final Iterator iterator = arg.iterator();
+        final Builder<String> entityIds = new Builder<String>();
+        while (iterator.hasNext()) {
+            final Object object = iterator.next();
+            if (!(object instanceof Entity)) {
+                // No good - ignore
+                return null;
+            } else {
+                entityIds.add(((Entity) object).getId().toString());
+            }
+        }
+
+        return entityIds;
+    }
+
+
+    private InternalCallContext retrieveContextFromArguments(final Object[] args) {
+        for (final Object arg : args) {
+            if (!(arg instanceof InternalCallContext)) {
+                continue;
+            }
+            return (InternalCallContext) arg;
+        }
+        return null;
+    }
+
+    private Long insertHistory(final Long entityRecordId, final M entityModelDao, final ChangeType changeType, final InternalCallContext context) {
+        // TODO use clock
+        final EntityHistoryModelDao<M, E> history = new EntityHistoryModelDao<M, E>(entityModelDao, entityRecordId, changeType, context.getCreatedDate());
+        sqlDao.addHistoryFromTransaction(history, context);
+        return sqlDao.getHistoryRecordId(entityRecordId, context);
+    }
+
+    private void insertAudits(final TableName tableName, final Long historyRecordId, final ChangeType changeType, final InternalCallContext context) {
+        // TODO use clock
+        final TableName destinationTableName = Objects.firstNonNull(tableName.getHistoryTableName(), tableName);
+        final EntityAudit audit = new EntityAudit(destinationTableName, historyRecordId, changeType, context.getCreatedDate());
+        sqlDao.insertAuditFromTransaction(audit, context);
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/EntityBase.java b/util/src/main/java/com/ning/billing/util/entity/EntityBase.java
index 4b8810c..628a078 100644
--- a/util/src/main/java/com/ning/billing/util/entity/EntityBase.java
+++ b/util/src/main/java/com/ning/billing/util/entity/EntityBase.java
@@ -42,6 +42,12 @@ public abstract class EntityBase implements Entity {
         this.updatedDate = updatedDate;
     }
 
+    public EntityBase(final EntityBase target) {
+        this.id = UUID.randomUUID();
+        this.createdDate = target.getCreatedDate();
+        this.updatedDate = target.getUpdatedDate();
+    }
+
     @Override
     public UUID getId() {
         return id;
@@ -56,4 +62,36 @@ public abstract class EntityBase implements Entity {
     public DateTime getUpdatedDate() {
         return updatedDate;
     }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final EntityBase that = (EntityBase) o;
+
+        if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
+            return false;
+        }
+        if (id != null ? !id.equals(that.id) : that.id != null) {
+            return false;
+        }
+        if (updatedDate != null ? !updatedDate.equals(that.updatedDate) : that.updatedDate != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = id != null ? id.hashCode() : 0;
+        result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
+        result = 31 * result + (updatedDate != null ? updatedDate.hashCode() : 0);
+        return result;
+    }
 }
diff --git a/util/src/main/java/com/ning/billing/util/glue/CustomFieldModule.java b/util/src/main/java/com/ning/billing/util/glue/CustomFieldModule.java
index 97fbec0..8969805 100644
--- a/util/src/main/java/com/ning/billing/util/glue/CustomFieldModule.java
+++ b/util/src/main/java/com/ning/billing/util/glue/CustomFieldModule.java
@@ -18,7 +18,7 @@ package com.ning.billing.util.glue;
 
 import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.customfield.api.DefaultCustomFieldUserApi;
-import com.ning.billing.util.customfield.dao.AuditedCustomFieldDao;
+import com.ning.billing.util.customfield.dao.DefaultCustomFieldDao;
 import com.ning.billing.util.customfield.dao.CustomFieldDao;
 
 import com.google.inject.AbstractModule;
@@ -35,7 +35,7 @@ public class CustomFieldModule extends AbstractModule {
     }
 
     protected void installCustomFieldDao() {
-        bind(CustomFieldDao.class).to(AuditedCustomFieldDao.class).asEagerSingleton();
+        bind(CustomFieldDao.class).to(DefaultCustomFieldDao.class).asEagerSingleton();
     }
 
 }
diff --git a/util/src/main/java/com/ning/billing/util/glue/TagStoreModule.java b/util/src/main/java/com/ning/billing/util/glue/TagStoreModule.java
index e2b974b..2bf9f5f 100644
--- a/util/src/main/java/com/ning/billing/util/glue/TagStoreModule.java
+++ b/util/src/main/java/com/ning/billing/util/glue/TagStoreModule.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010-2011 Ning, Inc.
+ * Copyright 2010-2012 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
@@ -20,7 +20,7 @@ import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.svcapi.tag.DefaultTagInternalApi;
 import com.ning.billing.util.svcapi.tag.TagInternalApi;
 import com.ning.billing.util.tag.api.DefaultTagUserApi;
-import com.ning.billing.util.tag.dao.AuditedTagDao;
+import com.ning.billing.util.tag.dao.DefaultTagDao;
 import com.ning.billing.util.tag.dao.DefaultTagDefinitionDao;
 import com.ning.billing.util.tag.dao.TagDao;
 import com.ning.billing.util.tag.dao.TagDefinitionDao;
@@ -30,7 +30,7 @@ import com.google.inject.AbstractModule;
 public class TagStoreModule extends AbstractModule {
     protected void installDaos() {
         bind(TagDefinitionDao.class).to(DefaultTagDefinitionDao.class).asEagerSingleton();
-        bind(TagDao.class).to(AuditedTagDao.class).asEagerSingleton();
+        bind(TagDao.class).to(DefaultTagDao.class).asEagerSingleton();
     }
 
     @Override
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java b/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java
index b20c1a7..6f58a2e 100644
--- a/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java
+++ b/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java
@@ -36,6 +36,8 @@ 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.clock.Clock;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
 import com.ning.billing.util.notificationq.dao.NotificationSqlDao;
 
@@ -89,12 +91,12 @@ public class DefaultNotificationQueue extends NotificationQueueBase {
     }
 
     @Override
-    public void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao,
+    public void recordFutureNotificationFromTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> transactionalDao,
                                                         final DateTime futureNotificationTime,
                                                         final UUID accountId,
                                                         final NotificationKey notificationKey,
                                                         final InternalCallContext context) throws IOException {
-        final NotificationSqlDao transactionalNotificationDao = transactionalDao.become(NotificationSqlDao.class);
+        final NotificationSqlDao transactionalNotificationDao = transactionalDao.transmogrify(NotificationSqlDao.class);
         recordFutureNotificationInternal(futureNotificationTime, accountId, notificationKey, transactionalNotificationDao, context);
     }
 
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java
index 1444425..e592674 100644
--- a/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java
+++ b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java
@@ -24,6 +24,9 @@ import org.joda.time.DateTime;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
 import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.entity.dao.EntityDao;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.queue.QueueLifecycle;
 
 public interface NotificationQueue extends QueueLifecycle {
@@ -47,7 +50,7 @@ public interface NotificationQueue extends QueueLifecycle {
      * @param futureNotificationTime the time at which the notification is ready
      * @param notificationKey        the key for that notification
      */
-    public void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao,
+    public void recordFutureNotificationFromTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> transactionalDao,
                                                         final DateTime futureNotificationTime,
                                                         final UUID accountId,
                                                         final NotificationKey notificationKey,
diff --git a/util/src/main/java/com/ning/billing/util/svcapi/junction/DefaultBlockingState.java b/util/src/main/java/com/ning/billing/util/svcapi/junction/DefaultBlockingState.java
index d1c9853..21e3150 100644
--- a/util/src/main/java/com/ning/billing/util/svcapi/junction/DefaultBlockingState.java
+++ b/util/src/main/java/com/ning/billing/util/svcapi/junction/DefaultBlockingState.java
@@ -89,7 +89,7 @@ public class DefaultBlockingState extends EntityBase implements BlockingState {
              null);
     }
 
-
+    @Override
     public UUID getBlockedId() {
         return blockingId;
     }
@@ -115,6 +115,7 @@ public class DefaultBlockingState extends EntityBase implements BlockingState {
         return timestamp;
     }
 
+    @Override
     public String getService() {
         return service;
     }
diff --git a/util/src/main/java/com/ning/billing/util/svcapi/tag/DefaultTagInternalApi.java b/util/src/main/java/com/ning/billing/util/svcapi/tag/DefaultTagInternalApi.java
index 3c7f8d4..774e20c 100644
--- a/util/src/main/java/com/ning/billing/util/svcapi/tag/DefaultTagInternalApi.java
+++ b/util/src/main/java/com/ning/billing/util/svcapi/tag/DefaultTagInternalApi.java
@@ -13,10 +13,10 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
+
 package com.ning.billing.util.svcapi.tag;
 
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
 import javax.inject.Inject;
@@ -25,10 +25,21 @@ import com.ning.billing.ObjectType;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.tag.ControlTagType;
+import com.ning.billing.util.tag.DefaultControlTag;
+import com.ning.billing.util.tag.DefaultTagDefinition;
+import com.ning.billing.util.tag.DescriptiveTag;
 import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.TagDefinition;
 import com.ning.billing.util.tag.dao.TagDao;
 import com.ning.billing.util.tag.dao.TagDefinitionDao;
+import com.ning.billing.util.tag.dao.TagDefinitionModelDao;
+import com.ning.billing.util.tag.dao.TagModelDao;
+import com.ning.billing.util.tag.dao.TagModelDaoHelper;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 
 public class DefaultTagInternalApi implements TagInternalApi {
 
@@ -44,26 +55,38 @@ public class DefaultTagInternalApi implements TagInternalApi {
 
     @Override
     public List<TagDefinition> getTagDefinitions(final InternalTenantContext context) {
-        return tagDefinitionDao.getTagDefinitions(context);
+        return ImmutableList.<TagDefinition>copyOf(Collections2.transform(tagDefinitionDao.getTagDefinitions(context),
+                                                                          new Function<TagDefinitionModelDao, TagDefinition>() {
+                                                                              @Override
+                                                                              public TagDefinition apply(final TagDefinitionModelDao input) {
+                                                                                  return new DefaultTagDefinition(input, TagModelDaoHelper.isControlTag(input.getName()));
+                                                                              }
+                                                                          }));
     }
 
     @Override
-    public Map<String, Tag> getTags(UUID objectId, ObjectType objectType,
-            InternalTenantContext context) {
-        return tagDao.loadEntities(objectId, objectType, context);
+    public List<Tag> getTags(final UUID objectId, final ObjectType objectType, final InternalTenantContext context) {
+        return ImmutableList.<Tag>copyOf(Collections2.transform(tagDao.getTags(objectId, objectType, context),
+                                                                new Function<TagModelDao, Tag>() {
+                                                                    @Override
+                                                                    public Tag apply(final TagModelDao input) {
+                                                                        return TagModelDaoHelper.isControlTag(input.getTagDefinitionId()) ?
+                                                                               new DefaultControlTag(ControlTagType.getTypeFromId(input.getTagDefinitionId()), objectType, objectId, input.getCreatedDate()) :
+                                                                               new DescriptiveTag(input.getTagDefinitionId(), objectType, objectId, input.getCreatedDate());
+                                                                    }
+                                                                }));
     }
 
     @Override
-    public void addTag(UUID objectId, ObjectType objectType,
-            UUID tagDefinitionId, InternalCallContext context)
+    public void addTag(final UUID objectId, final ObjectType objectType, final UUID tagDefinitionId, final InternalCallContext context)
             throws TagApiException {
-        tagDao.insertTag(objectId, objectType, tagDefinitionId, context);
+        final TagModelDao tag = new TagModelDao(context.getCreatedDate(), tagDefinitionId, objectId, objectType);
+        tagDao.create(tag, context);
 
     }
 
     @Override
-    public void removeTag(UUID objectId, ObjectType objectType,
-            UUID tagDefinitionId, InternalCallContext context)
+    public void removeTag(final UUID objectId, final ObjectType objectType, final UUID tagDefinitionId, final InternalCallContext context)
             throws TagApiException {
         tagDao.deleteTag(objectId, objectType, tagDefinitionId, context);
     }
diff --git a/util/src/main/java/com/ning/billing/util/svcapi/tag/TagInternalApi.java b/util/src/main/java/com/ning/billing/util/svcapi/tag/TagInternalApi.java
index 3d6a7c2..bc3ae14 100644
--- a/util/src/main/java/com/ning/billing/util/svcapi/tag/TagInternalApi.java
+++ b/util/src/main/java/com/ning/billing/util/svcapi/tag/TagInternalApi.java
@@ -39,9 +39,9 @@ public interface TagInternalApi {
      * @param context    call context
      * @return mapping tag id -> tag
      */
-    public Map<String, Tag> getTags(UUID objectId, ObjectType objectType, InternalTenantContext context);
+    public List<Tag> getTags(UUID objectId, ObjectType objectType, InternalTenantContext context);
 
-    public void addTag(UUID objectId, ObjectType objectType, UUID tagDefinitionId, InternalCallContext context) throws TagApiException;
+    public void addTag(final UUID objectId, final ObjectType objectType, UUID tagDefinitionId, InternalCallContext context) throws TagApiException;
 
-    public void removeTag(UUID objectId, ObjectType objectType, UUID tagDefinitionId, InternalCallContext context) throws TagApiException;
+    public void removeTag(final UUID objectId, final ObjectType objectType, final UUID tagDefinitionId, InternalCallContext context) throws TagApiException;
 }
diff --git a/util/src/main/java/com/ning/billing/util/svcsapi/bus/InternalBus.java b/util/src/main/java/com/ning/billing/util/svcsapi/bus/InternalBus.java
index c445c8d..e952fa0 100644
--- a/util/src/main/java/com/ning/billing/util/svcsapi/bus/InternalBus.java
+++ b/util/src/main/java/com/ning/billing/util/svcsapi/bus/InternalBus.java
@@ -19,6 +19,8 @@ package com.ning.billing.util.svcsapi.bus;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
 import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.events.BusInternalEvent;
 
 import com.google.common.eventbus.Subscribe;
@@ -91,9 +93,11 @@ public interface InternalBus {
      * Guarantees that the event is persisted on disk from within the same transaction
      *
      * @param event   to be posted
-     * @param dao     a valid DAO object obtained through the DBI.onDemand() API.
+     * @param transactional     a valid transactional object
      * @param context call context. account record id and tenant id are expected to be populated
      * @throws EventBusException if bus not been started yet
      */
-    public void postFromTransaction(BusInternalEvent event, Transmogrifier dao, InternalCallContext context) throws EventBusException;
+    public void postFromTransaction(BusInternalEvent event, EntitySqlDaoWrapperFactory<EntitySqlDao> transactional, InternalCallContext context) throws EventBusException;
+
+
 }
diff --git a/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java b/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
index e927d84..ae12ebc 100644
--- a/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
+++ b/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
@@ -18,7 +18,6 @@ package com.ning.billing.util.tag.api;
 
 import java.util.Collection;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
 import com.ning.billing.ObjectType;
@@ -26,13 +25,24 @@ import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.tag.ControlTagType;
+import com.ning.billing.util.tag.DefaultControlTag;
+import com.ning.billing.util.tag.DefaultTagDefinition;
+import com.ning.billing.util.tag.DescriptiveTag;
 import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.TagDefinition;
 import com.ning.billing.util.tag.dao.TagDao;
 import com.ning.billing.util.tag.dao.TagDefinitionDao;
+import com.ning.billing.util.tag.dao.TagDefinitionModelDao;
+import com.ning.billing.util.tag.dao.TagModelDao;
+import com.ning.billing.util.tag.dao.TagModelDaoHelper;
 
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
 public class DefaultTagUserApi implements TagUserApi {
@@ -50,12 +60,19 @@ public class DefaultTagUserApi implements TagUserApi {
 
     @Override
     public List<TagDefinition> getTagDefinitions(final TenantContext context) {
-        return tagDefinitionDao.getTagDefinitions(internalCallContextFactory.createInternalTenantContext(context));
+        return ImmutableList.<TagDefinition>copyOf(Collections2.transform(tagDefinitionDao.getTagDefinitions(internalCallContextFactory.createInternalTenantContext(context)),
+                                                                          new Function<TagDefinitionModelDao, TagDefinition>() {
+                                                                              @Override
+                                                                              public TagDefinition apply(final TagDefinitionModelDao input) {
+                                                                                  return new DefaultTagDefinition(input, TagModelDaoHelper.isControlTag(input.getName()));
+                                                                              }
+                                                                          }));
     }
 
     @Override
     public TagDefinition create(final String definitionName, final String description, final CallContext context) throws TagDefinitionApiException {
-        return tagDefinitionDao.create(definitionName, description, internalCallContextFactory.createInternalCallContext(context));
+        final TagDefinitionModelDao tagDefinitionModelDao = tagDefinitionDao.create(definitionName, description, internalCallContextFactory.createInternalCallContext(context));
+        return new DefaultTagDefinition(tagDefinitionModelDao, TagModelDaoHelper.isControlTag(tagDefinitionModelDao.getName()));
     }
 
     @Override
@@ -66,26 +83,34 @@ public class DefaultTagUserApi implements TagUserApi {
     @Override
     public TagDefinition getTagDefinition(final UUID tagDefinitionId, final TenantContext context)
             throws TagDefinitionApiException {
-        return tagDefinitionDao.getById(tagDefinitionId, internalCallContextFactory.createInternalTenantContext(context));
+        final TagDefinitionModelDao tagDefinitionModelDao = tagDefinitionDao.getById(tagDefinitionId, internalCallContextFactory.createInternalTenantContext(context));
+        return new DefaultTagDefinition(tagDefinitionModelDao, TagModelDaoHelper.isControlTag(tagDefinitionModelDao.getName()));
     }
 
     @Override
     public List<TagDefinition> getTagDefinitions(final Collection<UUID> tagDefinitionIds, final TenantContext context)
             throws TagDefinitionApiException {
-        return tagDefinitionDao.getByIds(tagDefinitionIds, internalCallContextFactory.createInternalTenantContext(context));
+        return ImmutableList.<TagDefinition>copyOf(Collections2.transform(tagDefinitionDao.getByIds(tagDefinitionIds, internalCallContextFactory.createInternalTenantContext(context)),
+                                                                          new Function<TagDefinitionModelDao, TagDefinition>() {
+                                                                              @Override
+                                                                              public TagDefinition apply(final TagDefinitionModelDao input) {
+                                                                                  return new DefaultTagDefinition(input, TagModelDaoHelper.isControlTag(input.getName()));
+                                                                              }
+                                                                          }));
     }
 
     @Override
     public void addTags(final UUID objectId, final ObjectType objectType, final Collection<UUID> tagDefinitionIds, final CallContext context) throws TagApiException {
-        // TODO: consider making this batch
         for (final UUID tagDefinitionId : tagDefinitionIds) {
-            tagDao.insertTag(objectId, objectType, tagDefinitionId, internalCallContextFactory.createInternalCallContext(objectId, objectType, context));
+            addTag(objectId, objectType, tagDefinitionId, context);
         }
     }
 
     @Override
     public void addTag(final UUID objectId, final ObjectType objectType, final UUID tagDefinitionId, final CallContext context) throws TagApiException {
-        tagDao.insertTag(objectId, objectType, tagDefinitionId, internalCallContextFactory.createInternalCallContext(objectId, objectType, context));
+        final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(objectId, objectType, context);
+        final TagModelDao tag = new TagModelDao(context.getCreatedDate(), tagDefinitionId, objectId, objectType);
+        tagDao.create(tag, internalContext);
     }
 
     @Override
@@ -102,13 +127,22 @@ public class DefaultTagUserApi implements TagUserApi {
     }
 
     @Override
-    public Map<String, Tag> getTags(final UUID objectId, final ObjectType objectType, final TenantContext context) {
-        return tagDao.loadEntities(objectId, objectType, internalCallContextFactory.createInternalTenantContext(context));
+    public List<Tag> getTags(final UUID objectId, final ObjectType objectType, final TenantContext context) {
+        return ImmutableList.<Tag>copyOf(Collections2.transform(tagDao.getTags(objectId, objectType, internalCallContextFactory.createInternalTenantContext(context)),
+                                                                new Function<TagModelDao, Tag>() {
+                                                                    @Override
+                                                                    public Tag apply(final TagModelDao input) {
+                                                                        return TagModelDaoHelper.isControlTag(input.getTagDefinitionId()) ?
+                                                                               new DefaultControlTag(ControlTagType.getTypeFromId(input.getTagDefinitionId()), objectType, objectId, input.getCreatedDate()) :
+                                                                               new DescriptiveTag(input.getTagDefinitionId(), objectType, objectId, input.getCreatedDate());
+                                                                    }
+                                                                }));
     }
 
     @Override
     public TagDefinition getTagDefinitionForName(final String tagDefinitionName, final TenantContext context)
             throws TagDefinitionApiException {
-        return tagDefinitionDao.getByName(tagDefinitionName, internalCallContextFactory.createInternalTenantContext(context));
+        return new DefaultTagDefinition(tagDefinitionDao.getByName(tagDefinitionName, internalCallContextFactory.createInternalTenantContext(context)),
+                                        TagModelDaoHelper.isControlTag(tagDefinitionName));
     }
 }
diff --git a/util/src/main/java/com/ning/billing/util/tag/api/user/TagEventBuilder.java b/util/src/main/java/com/ning/billing/util/tag/api/user/TagEventBuilder.java
index 4db25cb..e8275ea 100644
--- a/util/src/main/java/com/ning/billing/util/tag/api/user/TagEventBuilder.java
+++ b/util/src/main/java/com/ning/billing/util/tag/api/user/TagEventBuilder.java
@@ -28,38 +28,40 @@ 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.tag.TagDefinition;
+import com.ning.billing.util.tag.DefaultTagDefinition;
+import com.ning.billing.util.tag.dao.TagDefinitionModelDao;
 
 public class TagEventBuilder {
-    public UserTagDefinitionCreationInternalEvent newUserTagDefinitionCreationEvent(final UUID tagDefinitionId, final TagDefinition tagDefinition, final InternalCallContext context) {
-        return new DefaultUserTagDefinitionCreationEvent(tagDefinitionId, tagDefinition, context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
+
+    public UserTagDefinitionCreationInternalEvent newUserTagDefinitionCreationEvent(final UUID tagDefinitionId, final TagDefinitionModelDao tagDefinition, final InternalCallContext context) {
+        return new DefaultUserTagDefinitionCreationEvent(tagDefinitionId, new DefaultTagDefinition(tagDefinition, false), context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
     }
 
-    public UserTagDefinitionDeletionInternalEvent newUserTagDefinitionDeletionEvent(final UUID tagDefinitionId, final TagDefinition tagDefinition, final InternalCallContext context) {
-        return new DefaultUserTagDefinitionDeletionEvent(tagDefinitionId, tagDefinition, context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
+    public UserTagDefinitionDeletionInternalEvent newUserTagDefinitionDeletionEvent(final UUID tagDefinitionId, final TagDefinitionModelDao tagDefinition, final InternalCallContext context) {
+        return new DefaultUserTagDefinitionDeletionEvent(tagDefinitionId, new DefaultTagDefinition(tagDefinition, false), context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
     }
 
-    public ControlTagDefinitionCreationInternalEvent newControlTagDefinitionCreationEvent(final UUID tagDefinitionId, final TagDefinition tagDefinition, final InternalCallContext context) {
-        return new DefaultControlTagDefinitionCreationEvent(tagDefinitionId, tagDefinition, context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
+    public ControlTagDefinitionCreationInternalEvent newControlTagDefinitionCreationEvent(final UUID tagDefinitionId, final TagDefinitionModelDao tagDefinition, final InternalCallContext context) {
+        return new DefaultControlTagDefinitionCreationEvent(tagDefinitionId, new DefaultTagDefinition(tagDefinition, true), context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
     }
 
-    public ControlTagDefinitionDeletionInternalEvent newControlTagDefinitionDeletionEvent(final UUID tagDefinitionId, final TagDefinition tagDefinition, final InternalCallContext context) {
-        return new DefaultControlTagDefinitionDeletionEvent(tagDefinitionId, tagDefinition, context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
+    public ControlTagDefinitionDeletionInternalEvent newControlTagDefinitionDeletionEvent(final UUID tagDefinitionId, final TagDefinitionModelDao tagDefinition, final InternalCallContext context) {
+        return new DefaultControlTagDefinitionDeletionEvent(tagDefinitionId, new DefaultTagDefinition(tagDefinition, true), context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
     }
 
-    public UserTagCreationInternalEvent newUserTagCreationEvent(final UUID tagId, final UUID objectId, final ObjectType objectType, final TagDefinition tagDefinition, final InternalCallContext context) {
-        return new DefaultUserTagCreationEvent(tagId, objectId, objectType, tagDefinition, context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
+    public UserTagCreationInternalEvent newUserTagCreationEvent(final UUID tagId, final UUID objectId, final ObjectType objectType, final TagDefinitionModelDao tagDefinition, final InternalCallContext context) {
+        return new DefaultUserTagCreationEvent(tagId, objectId, objectType, new DefaultTagDefinition(tagDefinition, false), context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
     }
 
-    public UserTagDeletionInternalEvent newUserTagDeletionEvent(final UUID tagId, final UUID objectId, final ObjectType objectType, final TagDefinition tagDefinition, final InternalCallContext context) {
-        return new DefaultUserTagDeletionEvent(tagId, objectId, objectType, tagDefinition, context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
+    public UserTagDeletionInternalEvent newUserTagDeletionEvent(final UUID tagId, final UUID objectId, final ObjectType objectType, final TagDefinitionModelDao tagDefinition, final InternalCallContext context) {
+        return new DefaultUserTagDeletionEvent(tagId, objectId, objectType, new DefaultTagDefinition(tagDefinition, false), context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
     }
 
-    public ControlTagCreationInternalEvent newControlTagCreationEvent(final UUID tagId, final UUID objectId, final ObjectType objectType, final TagDefinition tagDefinition, final InternalCallContext context) {
-        return new DefaultControlTagCreationEvent(tagId, objectId, objectType, tagDefinition, context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
+    public ControlTagCreationInternalEvent newControlTagCreationEvent(final UUID tagId, final UUID objectId, final ObjectType objectType, final TagDefinitionModelDao tagDefinition, final InternalCallContext context) {
+        return new DefaultControlTagCreationEvent(tagId, objectId, objectType, new DefaultTagDefinition(tagDefinition, true), context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
     }
 
-    public ControlTagDeletionInternalEvent newControlTagDeletionEvent(final UUID tagId, final UUID objectId, final ObjectType objectType, final TagDefinition tagDefinition, final InternalCallContext context) {
-        return new DefaultControlTagDeletionEvent(tagId, objectId, objectType, tagDefinition, context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId() );
+    public ControlTagDeletionInternalEvent newControlTagDeletionEvent(final UUID tagId, final UUID objectId, final ObjectType objectType, final TagDefinitionModelDao tagDefinition, final InternalCallContext context) {
+        return new DefaultControlTagDeletionEvent(tagId, objectId, objectType, new DefaultTagDefinition(tagDefinition, true), context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
     }
 }
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDao.java
new file mode 100644
index 0000000..d40fbf4
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDao.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.tag.dao;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.skife.jdbi.v2.IDBI;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.ObjectType;
+import com.ning.billing.util.api.TagApiException;
+import com.ning.billing.util.audit.ChangeType;
+import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.entity.dao.EntityDaoBase;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+import com.ning.billing.util.events.TagInternalEvent;
+import com.ning.billing.util.svcsapi.bus.InternalBus;
+import com.ning.billing.util.tag.ControlTagType;
+import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.api.user.TagEventBuilder;
+
+import com.google.inject.Inject;
+
+public class DefaultTagDao extends EntityDaoBase<TagModelDao, Tag, TagApiException> implements TagDao {
+
+    private static final Logger log = LoggerFactory.getLogger(DefaultTagDao.class);
+
+    private final TagEventBuilder tagEventBuilder;
+    private final InternalBus bus;
+
+    @Inject
+    public DefaultTagDao(final IDBI dbi, final TagEventBuilder tagEventBuilder, final InternalBus bus) {
+        super(new EntitySqlDaoTransactionalJdbiWrapper(dbi), TagSqlDao.class);
+        this.tagEventBuilder = tagEventBuilder;
+        this.bus = bus;
+    }
+
+    @Override
+    public List<TagModelDao> getTags(final UUID objectId, final ObjectType objectType, final InternalTenantContext internalTenantContext) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<TagModelDao>>() {
+            @Override
+            public List<TagModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(TagSqlDao.class).getTagsForObject(objectId, objectType, internalTenantContext);
+            }
+        });
+    }
+
+    @Override
+    protected void postBusEventFromTransaction(final TagModelDao tag, final TagModelDao savedTag, final ChangeType changeType,
+                                               final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalCallContext context)
+            throws BillingExceptionBase {
+
+        final TagInternalEvent tagEvent;
+        final TagDefinitionModelDao tagDefinition = getTagDefinitionFromTransaction(tag.getTagDefinitionId(), entitySqlDaoWrapperFactory, context);
+        final boolean isControlTag = ControlTagType.getTypeFromId(tagDefinition.getId()) != null;
+        switch (changeType) {
+            case INSERT:
+                tagEvent = (isControlTag) ?
+                           tagEventBuilder.newControlTagCreationEvent(tag.getId(), tag.getObjectId(), tag.getObjectType(), tagDefinition, context) :
+                           tagEventBuilder.newUserTagCreationEvent(tag.getId(), tag.getObjectId(), tag.getObjectType(), tagDefinition, context);
+                break;
+            case DELETE:
+                tagEvent = (isControlTag) ?
+                           tagEventBuilder.newControlTagDeletionEvent(tag.getId(), tag.getObjectId(), tag.getObjectType(), tagDefinition, context) :
+                           tagEventBuilder.newUserTagDeletionEvent(tag.getId(), tag.getObjectId(), tag.getObjectType(), tagDefinition, context);
+                break;
+            default:
+                return;
+        }
+
+        try {
+            bus.postFromTransaction(tagEvent, entitySqlDaoWrapperFactory, context);
+        } catch (InternalBus.EventBusException e) {
+            log.warn("Failed to post tag event for tag " + tag.getId().toString(), e);
+        }
+    }
+
+    @Override
+    protected TagApiException generateAlreadyExistsException(final TagModelDao entity, final InternalCallContext context) {
+        return new TagApiException(ErrorCode.TAG_ALREADY_EXISTS, entity.getId());
+    }
+
+    private TagDefinitionModelDao getTagDefinitionFromTransaction(final UUID tagDefinitionId, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) throws TagApiException {
+        TagDefinitionModelDao tagDefintion = null;
+        for (final ControlTagType t : ControlTagType.values()) {
+            if (t.getId().equals(tagDefinitionId)) {
+                tagDefintion = new TagDefinitionModelDao(t);
+                break;
+            }
+        }
+        if (tagDefintion == null) {
+            final TagDefinitionSqlDao transTagDefintionSqlDao = entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class);
+            tagDefintion = transTagDefintionSqlDao.getById(tagDefinitionId.toString(), context);
+        }
+
+        if (tagDefintion == null) {
+            throw new TagApiException(ErrorCode.TAG_DEFINITION_DOES_NOT_EXIST, tagDefinitionId);
+        }
+        return tagDefintion;
+    }
+
+    @Override
+    public void deleteTag(final UUID objectId, final ObjectType objectType, final UUID tagDefinitionId, final InternalCallContext context) throws TagApiException {
+
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+
+                final TagDefinitionModelDao tagDefinition = getTagDefinitionFromTransaction(tagDefinitionId, entitySqlDaoWrapperFactory, context);
+                final TagSqlDao transactional = entitySqlDaoWrapperFactory.become(TagSqlDao.class);
+                final List<TagModelDao> tags = transactional.getTagsForObject(objectId, objectType, context);
+                TagModelDao tag = null;
+                for (final TagModelDao cur : tags) {
+                    if (cur.getTagDefinitionId().equals(tagDefinitionId)) {
+                        tag = cur;
+                        break;
+                    }
+                }
+                if (tag == null) {
+                    throw new TagApiException(ErrorCode.TAG_DOES_NOT_EXIST, tagDefinition.getName());
+                }
+                // Delete the tag
+                transactional.markTagAsDeleted(tag.getId().toString(), context);
+
+                postBusEventFromTransaction(tag, tag, ChangeType.DELETE, entitySqlDaoWrapperFactory, context);
+                return null;
+            }
+        });
+
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
index ad50c0e..968c957 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
@@ -22,20 +22,24 @@ import java.util.List;
 import java.util.UUID;
 
 import org.skife.jdbi.v2.IDBI;
-import org.skife.jdbi.v2.Transaction;
-import org.skife.jdbi.v2.TransactionStatus;
 import org.skife.jdbi.v2.exceptions.TransactionFailedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.ning.billing.BillingExceptionBase;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.util.api.TagDefinitionApiException;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.entity.dao.EntityDaoBase;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.events.TagDefinitionInternalEvent;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
 import com.ning.billing.util.tag.ControlTagType;
-import com.ning.billing.util.tag.DefaultTagDefinition;
 import com.ning.billing.util.tag.TagDefinition;
 import com.ning.billing.util.tag.api.user.TagEventBuilder;
 
@@ -43,108 +47,130 @@ import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
 import com.google.inject.Inject;
 
-public class DefaultTagDefinitionDao implements TagDefinitionDao {
+public class DefaultTagDefinitionDao extends EntityDaoBase<TagDefinitionModelDao, TagDefinition, TagDefinitionApiException> implements TagDefinitionDao {
 
     private static final Logger log = LoggerFactory.getLogger(DefaultTagDefinitionDao.class);
 
-    private final TagDefinitionSqlDao tagDefinitionSqlDao;
     private final TagEventBuilder tagEventBuilder;
     private final InternalBus bus;
 
     @Inject
     public DefaultTagDefinitionDao(final IDBI dbi, final TagEventBuilder tagEventBuilder, final InternalBus bus) {
+        super(new EntitySqlDaoTransactionalJdbiWrapper(dbi), TagDefinitionSqlDao.class);
         this.tagEventBuilder = tagEventBuilder;
         this.bus = bus;
-        this.tagDefinitionSqlDao = dbi.onDemand(TagDefinitionSqlDao.class);
     }
 
     @Override
-    public List<TagDefinition> getTagDefinitions(final InternalTenantContext context) {
-        // Get user definitions from the database
-        final List<TagDefinition> definitionList = new LinkedList<TagDefinition>();
-        definitionList.addAll(tagDefinitionSqlDao.get(context));
-
-        // Add control tag definitions
-        for (final ControlTagType controlTag : ControlTagType.values()) {
-            definitionList.add(new DefaultTagDefinition(controlTag));
-        }
-        return definitionList;
-    }
+    public List<TagDefinitionModelDao> getTagDefinitions(final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<TagDefinitionModelDao>>() {
+            @Override
+            public List<TagDefinitionModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                // Get user definitions from the database
+                final List<TagDefinitionModelDao> definitionList = new LinkedList<TagDefinitionModelDao>();
+                definitionList.addAll(entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class).get(context));
 
-    @Override
-    public TagDefinition getByName(final String definitionName, final InternalTenantContext context) {
-        for (final ControlTagType controlTag : ControlTagType.values()) {
-            if (controlTag.name().equals(definitionName)) {
-                return new DefaultTagDefinition(controlTag);
+                // Add control tag definitions
+                for (final ControlTagType controlTag : ControlTagType.values()) {
+                    definitionList.add(new TagDefinitionModelDao(controlTag));
+                }
+                return definitionList;
             }
-        }
-        return tagDefinitionSqlDao.getByName(definitionName, context);
+        });
     }
 
     @Override
-    public TagDefinition getById(final UUID definitionId, final InternalTenantContext context) {
-        for (final ControlTagType controlTag : ControlTagType.values()) {
-            if (controlTag.getId().equals(definitionId)) {
-                return new DefaultTagDefinition(controlTag);
+    public TagDefinitionModelDao getByName(final String definitionName, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<TagDefinitionModelDao>() {
+            @Override
+            public TagDefinitionModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                for (final ControlTagType controlTag : ControlTagType.values()) {
+                    if (controlTag.name().equals(definitionName)) {
+                        return new TagDefinitionModelDao(controlTag);
+                    }
+                }
+                return entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class).getByName(definitionName, context);
             }
-        }
-        return tagDefinitionSqlDao.getById(definitionId.toString(), context);
+        });
     }
 
     @Override
-    public List<TagDefinition> getByIds(final Collection<UUID> definitionIds, final InternalTenantContext context) {
-        final List<TagDefinition> result = new LinkedList<TagDefinition>();
-        for (final UUID cur : definitionIds) {
-            for (final ControlTagType controlTag : ControlTagType.values()) {
-                if (controlTag.getId().equals(cur)) {
-                    result.add(new DefaultTagDefinition(controlTag));
-                    break;
+    public TagDefinitionModelDao getById(final UUID definitionId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<TagDefinitionModelDao>() {
+            @Override
+            public TagDefinitionModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                for (final ControlTagType controlTag : ControlTagType.values()) {
+                    if (controlTag.getId().equals(definitionId)) {
+                        return new TagDefinitionModelDao(controlTag);
+                    }
                 }
+                return entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class).getById(definitionId.toString(), context);
             }
-        }
-        if (definitionIds.size() > 0) {
-            result.addAll(tagDefinitionSqlDao.getByIds(Collections2.transform(definitionIds, new Function<UUID, String>() {
-                @Override
-                public String apply(final UUID input) {
-                    return input.toString();
+        });
+    }
+
+    @Override
+    public List<TagDefinitionModelDao> getByIds(final Collection<UUID> definitionIds, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<TagDefinitionModelDao>>() {
+            @Override
+            public List<TagDefinitionModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final List<TagDefinitionModelDao> result = new LinkedList<TagDefinitionModelDao>();
+                for (final UUID cur : definitionIds) {
+                    for (final ControlTagType controlTag : ControlTagType.values()) {
+                        if (controlTag.getId().equals(cur)) {
+                            result.add(new TagDefinitionModelDao(controlTag));
+                            break;
+                        }
+                    }
                 }
+                if (definitionIds.size() > 0) {
+                    result.addAll(entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class).getByIds(Collections2.transform(definitionIds, new Function<UUID, String>() {
+                        @Override
+                        public String apply(final UUID input) {
+                            return input.toString();
+                        }
 
-            }), context));
-        }
-        return result;
+                    }), context));
+                }
+                return result;
+            }
+        });
     }
 
     @Override
-    public TagDefinition create(final String definitionName, final String description,
-                                final InternalCallContext context) throws TagDefinitionApiException {
+    public TagDefinitionModelDao create(final String definitionName, final String description,
+                                        final InternalCallContext context) throws TagDefinitionApiException {
         // Make sure a control tag with this name don't already exist
-        if (isControlTagName(definitionName)) {
+        if (TagModelDaoHelper.isControlTag(definitionName)) {
             throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_CONFLICTS_WITH_CONTROL_TAG, definitionName);
         }
 
         try {
-            return tagDefinitionSqlDao.inTransaction(new Transaction<TagDefinition, TagDefinitionSqlDao>() {
+            return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<TagDefinitionModelDao>() {
                 @Override
-                public TagDefinition inTransaction(final TagDefinitionSqlDao tagDefinitionSqlDao, final TransactionStatus status) throws Exception {
+                public TagDefinitionModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                    final TagDefinitionSqlDao tagDefinitionSqlDao = entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class);
+
                     // Make sure the tag definition doesn't exist already
-                    final TagDefinition existingDefinition = tagDefinitionSqlDao.getByName(definitionName, context);
+                    final TagDefinitionModelDao existingDefinition = tagDefinitionSqlDao.getByName(definitionName, context);
                     if (existingDefinition != null) {
                         throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_ALREADY_EXISTS, definitionName);
                     }
 
                     // Create it
-                    final TagDefinition tagDefinition = new DefaultTagDefinition(definitionName, description, false);
+                    final TagDefinitionModelDao tagDefinition = new TagDefinitionModelDao(context.getCreatedDate(), definitionName, description);
                     tagDefinitionSqlDao.create(tagDefinition, context);
 
                     // Post an event to the bus
+                    final boolean isControlTag = TagModelDaoHelper.isControlTag(tagDefinition.getName());
                     final TagDefinitionInternalEvent tagDefinitionEvent;
-                    if (tagDefinition.isControlTag()) {
+                    if (isControlTag) {
                         tagDefinitionEvent = tagEventBuilder.newControlTagDefinitionCreationEvent(tagDefinition.getId(), tagDefinition, context);
                     } else {
                         tagDefinitionEvent = tagEventBuilder.newUserTagDefinitionCreationEvent(tagDefinition.getId(), tagDefinition, context);
                     }
                     try {
-                        bus.postFromTransaction(tagDefinitionEvent, tagDefinitionSqlDao, context);
+                        bus.postFromTransaction(tagDefinitionEvent, entitySqlDaoWrapperFactory, context);
                     } catch (InternalBus.EventBusException e) {
                         log.warn("Failed to post tag definition creation event for tag " + tagDefinition.getId(), e);
                     }
@@ -161,24 +187,16 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
         }
     }
 
-    private boolean isControlTagName(final String definitionName) {
-        for (final ControlTagType controlTagName : ControlTagType.values()) {
-            if (controlTagName.toString().equals(definitionName)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
     @Override
     public void deleteById(final UUID definitionId, final InternalCallContext context) throws TagDefinitionApiException {
         try {
-            tagDefinitionSqlDao.inTransaction(new Transaction<Void, TagDefinitionSqlDao>() {
+            transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
                 @Override
-                public Void inTransaction(final TagDefinitionSqlDao tagDefinitionSqlDao, final TransactionStatus status) throws Exception {
+                public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                    final TagDefinitionSqlDao tagDefinitionSqlDao = entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class);
+
                     // Make sure the tag definition exists
-                    final TagDefinition tagDefinition = tagDefinitionSqlDao.getById(definitionId.toString(), context);
+                    final TagDefinitionModelDao tagDefinition = tagDefinitionSqlDao.getById(definitionId.toString(), context);
                     if (tagDefinition == null) {
                         throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_DOES_NOT_EXIST, definitionId);
                     }
@@ -189,21 +207,9 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
                     }
 
                     // Delete it
-                    tagDefinitionSqlDao.deleteTagDefinition(definitionId.toString(), context);
-
-                    // Post an event to the Bus
-                    final TagDefinitionInternalEvent tagDefinitionEvent;
-                    if (tagDefinition.isControlTag()) {
-                        tagDefinitionEvent = tagEventBuilder.newControlTagDefinitionDeletionEvent(tagDefinition.getId(), tagDefinition, context);
-                    } else {
-                        tagDefinitionEvent = tagEventBuilder.newUserTagDefinitionDeletionEvent(tagDefinition.getId(), tagDefinition, context);
-                    }
-                    try {
-                        bus.postFromTransaction(tagDefinitionEvent, tagDefinitionSqlDao, context);
-                    } catch (InternalBus.EventBusException e) {
-                        log.warn("Failed to post tag definition deletion event for tag " + tagDefinition.getId(), e);
-                    }
+                    tagDefinitionSqlDao.markTagDefinitionAsDeleted(definitionId.toString(), context);
 
+                    postBusEventFromTransaction(tagDefinition, tagDefinition, ChangeType.DELETE, entitySqlDaoWrapperFactory, context);
                     return null;
                 }
             });
@@ -215,4 +221,38 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
             }
         }
     }
+
+    protected void postBusEventFromTransaction(final TagDefinitionModelDao tagDefinition, final TagDefinitionModelDao savedTagDefinition,
+                                               final ChangeType changeType, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalCallContext context)
+            throws BillingExceptionBase {
+
+        final TagDefinitionInternalEvent tagDefinitionEvent;
+        final boolean isControlTag = TagModelDaoHelper.isControlTag(tagDefinition.getName());
+        switch (changeType) {
+            case INSERT:
+                tagDefinitionEvent = (isControlTag) ?
+                                     tagEventBuilder.newControlTagDefinitionCreationEvent(tagDefinition.getId(), tagDefinition, context) :
+                                     tagEventBuilder.newUserTagDefinitionCreationEvent(tagDefinition.getId(), tagDefinition, context);
+
+                break;
+            case DELETE:
+                tagDefinitionEvent = (isControlTag) ?
+                                     tagEventBuilder.newControlTagDefinitionDeletionEvent(tagDefinition.getId(), tagDefinition, context) :
+                                     tagEventBuilder.newUserTagDefinitionDeletionEvent(tagDefinition.getId(), tagDefinition, context);
+                break;
+            default:
+                return;
+        }
+
+        try {
+            bus.postFromTransaction(tagDefinitionEvent, entitySqlDaoWrapperFactory, context);
+        } catch (InternalBus.EventBusException e) {
+            log.warn("Failed to post tag definition event for tag " + tagDefinition.getId().toString(), e);
+        }
+    }
+
+    @Override
+    protected TagDefinitionApiException generateAlreadyExistsException(final TagDefinitionModelDao entity, final InternalCallContext context) {
+        return new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_ALREADY_EXISTS, entity.getId());
+    }
 }
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
index ccca5ce..152dfbf 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
@@ -16,17 +16,21 @@
 
 package com.ning.billing.util.tag.dao;
 
+import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.ObjectType;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.callcontext.InternalCallContext;
-import com.ning.billing.util.dao.AuditedCollectionDao;
-import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.callcontext.InternalTenantContext;
 
-public interface TagDao extends AuditedCollectionDao<Tag> {
+public interface TagDao {
 
-    void insertTag(UUID objectId, ObjectType objectType, UUID tagDefinition, InternalCallContext context) throws TagApiException;
+    void create(TagModelDao tag, InternalCallContext context) throws TagApiException;
 
     void deleteTag(UUID objectId, ObjectType objectType, UUID tagDefinition, InternalCallContext context) throws TagApiException;
+
+    TagModelDao getById(UUID tagId, InternalTenantContext context);
+
+    List<TagModelDao> getTags(UUID objectId, ObjectType objectType, InternalTenantContext internalTenantContext);
 }
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionDao.java
index b2cc4f0..74b33f6 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionDao.java
@@ -23,19 +23,18 @@ import java.util.UUID;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.tag.TagDefinition;
 
 public interface TagDefinitionDao {
 
-    public List<TagDefinition> getTagDefinitions(InternalTenantContext context);
+    public List<TagDefinitionModelDao> getTagDefinitions(InternalTenantContext context);
 
-    public TagDefinition getById(UUID definitionId, InternalTenantContext context);
+    public TagDefinitionModelDao getById(UUID definitionId, InternalTenantContext context);
 
-    public TagDefinition getByName(String definitionName, InternalTenantContext context);
+    public TagDefinitionModelDao getByName(String definitionName, InternalTenantContext context);
 
-    public List<TagDefinition> getByIds(Collection<UUID> definitionIds, InternalTenantContext context);
+    public List<TagDefinitionModelDao> getByIds(Collection<UUID> definitionIds, InternalTenantContext context);
 
-    public TagDefinition create(String definitionName, String description, InternalCallContext context) throws TagDefinitionApiException;
+    public TagDefinitionModelDao create(String definitionName, String description, InternalCallContext context) throws TagDefinitionApiException;
 
     public void deleteById(UUID definitionId, InternalCallContext context) throws TagDefinitionApiException;
 }
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionModelDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionModelDao.java
new file mode 100644
index 0000000..5c0aaf9
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionModelDao.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.tag.dao;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.dao.EntityModelDao;
+import com.ning.billing.util.tag.ControlTagType;
+import com.ning.billing.util.tag.TagDefinition;
+
+public class TagDefinitionModelDao extends EntityBase implements EntityModelDao<TagDefinition> {
+
+    private String name;
+    private String description;
+    private Boolean isActive;
+
+    public TagDefinitionModelDao() { /* For the DAO mapper */ }
+
+    public TagDefinitionModelDao(final UUID id, final DateTime createdDate, final DateTime updatedDate, final String name, final String description) {
+        super(id, createdDate, updatedDate);
+        this.name = name;
+        this.description = description;
+        this.isActive = true;
+    }
+
+    public TagDefinitionModelDao(final ControlTagType tag) {
+        this(tag.getId(), null, null, tag.name(), tag.getDescription());
+    }
+
+    public TagDefinitionModelDao(final DateTime createdDate, final String name, final String description) {
+        this(UUID.randomUUID(), createdDate, createdDate, name, description);
+    }
+
+    public TagDefinitionModelDao(final TagDefinition tagDefinition) {
+        this(tagDefinition.getId(), tagDefinition.getCreatedDate(), tagDefinition.getUpdatedDate(), tagDefinition.getName(),
+             tagDefinition.getDescription());
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public Boolean getIsActive() {
+        return isActive;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("TagDefinitionModelDao");
+        sb.append("{name='").append(name).append('\'');
+        sb.append(", description='").append(description).append('\'');
+        sb.append(", isActive=").append(isActive);
+        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 TagDefinitionModelDao that = (TagDefinitionModelDao) o;
+
+        if (description != null ? !description.equals(that.description) : that.description != null) {
+            return false;
+        }
+        if (isActive != null ? !isActive.equals(that.isActive) : that.isActive != null) {
+            return false;
+        }
+        if (name != null ? !name.equals(that.name) : that.name != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        result = 31 * result + (description != null ? description.hashCode() : 0);
+        result = 31 * result + (isActive != null ? isActive.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.TAG_DEFINITIONS;
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.java
index 3df4c49..8603f3d 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.java
@@ -16,92 +16,39 @@
 
 package com.ning.billing.util.tag.dao;
 
-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;
-import java.sql.ResultSet;
-import java.sql.SQLException;
 import java.util.Collection;
 import java.util.List;
-import java.util.UUID;
 
-import org.skife.jdbi.v2.SQLStatement;
-import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.Binder;
-import org.skife.jdbi.v2.sqlobject.BinderFactory;
-import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 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.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
-import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
+import com.ning.billing.util.entity.dao.Audited;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
-import com.ning.billing.util.tag.DefaultTagDefinition;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 import com.ning.billing.util.tag.TagDefinition;
 
-@ExternalizedSqlViaStringTemplate3
-@RegisterMapper(TagDefinitionSqlDao.TagDefinitionMapper.class)
-public interface TagDefinitionSqlDao extends EntitySqlDao<TagDefinition>, Transactional<TagDefinitionSqlDao>, Transmogrifier {
-
-    @Override
-    @SqlUpdate
-    public void create(@TagDefinitionBinder final TagDefinition entity,
-                       @InternalTenantContextBinder final InternalCallContext context);
+@EntitySqlDaoStringTemplate
+public interface TagDefinitionSqlDao extends EntitySqlDao<TagDefinitionModelDao, TagDefinition> {
 
     @SqlQuery
-    public TagDefinition getByName(@Bind("name") final String definitionName,
-                                   @InternalTenantContextBinder final InternalTenantContext context);
+    public TagDefinitionModelDao getByName(@Bind("name") final String definitionName,
+                                           @BindBean final InternalTenantContext context);
 
     @SqlUpdate
-    public void deleteTagDefinition(@Bind("id") final String definitionId,
-                                    @InternalTenantContextBinder final InternalCallContext context);
+    @Audited(ChangeType.DELETE)
+    public void markTagDefinitionAsDeleted(@Bind("id") final String definitionId,
+                                           @BindBean final InternalCallContext context);
 
     @SqlQuery
     public int tagDefinitionUsageCount(@Bind("id") final String definitionId,
-                                       @InternalTenantContextBinder final InternalTenantContext context);
+                                       @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    public List<TagDefinition> getByIds(@UUIDCollectionBinder final Collection<String> definitionIds,
-                                        @InternalTenantContextBinder final InternalTenantContext context);
-
-    public class TagDefinitionMapper implements ResultSetMapper<TagDefinition> {
-
-        @Override
-        public TagDefinition map(final int index, final ResultSet result, final StatementContext context) throws SQLException {
-            final UUID id = UUID.fromString(result.getString("id"));
-            final String name = result.getString("name");
-            final String description = result.getString("description");
-            return new DefaultTagDefinition(id, name, description, false);
-        }
-    }
-
-    @BindingAnnotation(TagDefinitionBinder.TagDefinitionBinderFactory.class)
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target({ElementType.PARAMETER})
-    public @interface TagDefinitionBinder {
-
-        public static class TagDefinitionBinderFactory implements BinderFactory {
-
-            @Override
-            public Binder build(final Annotation annotation) {
-                return new Binder<TagDefinitionBinder, TagDefinition>() {
-                    @Override
-                    public void bind(final SQLStatement q, final TagDefinitionBinder bind, final TagDefinition tagDefinition) {
-                        q.bind("id", tagDefinition.getId().toString());
-                        q.bind("name", tagDefinition.getName());
-                        q.bind("description", tagDefinition.getDescription());
-                    }
-                };
-            }
-        }
-    }
+    public List<TagDefinitionModelDao> getByIds(@UUIDCollectionBinder final Collection<String> definitionIds,
+                                                @BindBean final InternalTenantContext context);
 }
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagModelDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagModelDao.java
new file mode 100644
index 0000000..8d381df
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagModelDao.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.tag.dao;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.ObjectType;
+import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.dao.EntityModelDao;
+import com.ning.billing.util.tag.Tag;
+
+public class TagModelDao extends EntityBase implements EntityModelDao<Tag> {
+
+    private UUID tagDefinitionId;
+    private UUID objectId;
+    private ObjectType objectType;
+    private Boolean isActive;
+
+    public TagModelDao() { /* For the DAO mapper */ }
+
+    public TagModelDao(final DateTime createdDate, final UUID tagDefinitionId,
+                       final UUID objectId, final ObjectType objectType) {
+        this(UUID.randomUUID(), createdDate, createdDate, tagDefinitionId, objectId, objectType);
+    }
+
+    public TagModelDao(final UUID id, final DateTime createdDate, final DateTime updatedDate, final UUID tagDefinitionId,
+                       final UUID objectId, final ObjectType objectType) {
+        super(id, createdDate, updatedDate);
+        this.tagDefinitionId = tagDefinitionId;
+        this.objectId = objectId;
+        this.objectType = objectType;
+        this.isActive = true;
+    }
+
+    public TagModelDao(final Tag tag) {
+        this(tag.getId(), tag.getCreatedDate(), tag.getUpdatedDate(), tag.getTagDefinitionId(), tag.getObjectId(), tag.getObjectType());
+    }
+
+    public UUID getTagDefinitionId() {
+        return tagDefinitionId;
+    }
+
+    public UUID getObjectId() {
+        return objectId;
+    }
+
+    public ObjectType getObjectType() {
+        return objectType;
+    }
+
+    public Boolean getIsActive() {
+        return isActive;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("TagModelDao");
+        sb.append("{tagDefinitionId=").append(tagDefinitionId);
+        sb.append(", objectId=").append(objectId);
+        sb.append(", objectType=").append(objectType);
+        sb.append(", isActive=").append(isActive);
+        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 TagModelDao that = (TagModelDao) o;
+
+        if (isActive != null ? !isActive.equals(that.isActive) : that.isActive != null) {
+            return false;
+        }
+        if (objectId != null ? !objectId.equals(that.objectId) : that.objectId != null) {
+            return false;
+        }
+        if (objectType != that.objectType) {
+            return false;
+        }
+        if (tagDefinitionId != null ? !tagDefinitionId.equals(that.tagDefinitionId) : that.tagDefinitionId != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (tagDefinitionId != null ? tagDefinitionId.hashCode() : 0);
+        result = 31 * result + (objectId != null ? objectId.hashCode() : 0);
+        result = 31 * result + (objectType != null ? objectType.hashCode() : 0);
+        result = 31 * result + (isActive != null ? isActive.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.TAG;
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagSqlDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagSqlDao.java
index a619b79..2455417 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagSqlDao.java
@@ -16,75 +16,33 @@
 
 package com.ning.billing.util.tag.dao;
 
-import java.util.Collection;
 import java.util.List;
+import java.util.UUID;
 
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.SqlBatch;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 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.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 
 import com.ning.billing.ObjectType;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.EntityHistory;
-import com.ning.billing.util.dao.ObjectTypeBinder;
-import com.ning.billing.util.entity.collection.dao.UpdatableEntityCollectionSqlDao;
+import com.ning.billing.util.entity.dao.Audited;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 import com.ning.billing.util.tag.Tag;
 
-@ExternalizedSqlViaStringTemplate3
-@RegisterMapper(TagMapper.class)
-public interface TagSqlDao extends UpdatableEntityCollectionSqlDao<Tag>, Transactional<TagSqlDao>, Transmogrifier {
-
-    @Override
-    @SqlBatch(transactional = false)
-    public void insertFromTransaction(@Bind("objectId") final String objectId,
-                                      @ObjectTypeBinder final ObjectType objectType,
-                                      @TagBinder final Collection<Tag> tags,
-                                      @InternalTenantContextBinder final InternalCallContext context);
-
-    @Override
-    @SqlBatch(transactional = false)
-    public void updateFromTransaction(@Bind("objectId") final String objectId,
-                                      @ObjectTypeBinder final ObjectType objectType,
-                                      @TagBinder final Collection<Tag> tags,
-                                      @InternalTenantContextBinder final InternalCallContext context);
-
-    @Override
-    @SqlBatch(transactional = false)
-    public void deleteFromTransaction(@Bind("objectId") final String objectId,
-                                      @ObjectTypeBinder final ObjectType objectType,
-                                      @TagBinder final Collection<Tag> tags,
-                                      @InternalTenantContextBinder final InternalCallContext context);
-
-    @Override
-    @SqlBatch(transactional = false)
-    public void addHistoryFromTransaction(@Bind("objectId") final String objectId,
-                                          @ObjectTypeBinder final ObjectType objectType,
-                                          @TagHistoryBinder final List<EntityHistory<Tag>> histories,
-                                          @InternalTenantContextBinder final InternalCallContext context);
-
-    @SqlUpdate
-    public void addTagFromTransaction(@Bind("id") final String tagId,
-                                      @Bind("tagDefinitionId") final String tagDefinitionId,
-                                      @Bind("objectId") final String objectId,
-                                      @ObjectTypeBinder final ObjectType objectType,
-                                      @InternalTenantContextBinder final InternalCallContext context);
+@EntitySqlDaoStringTemplate
+public interface TagSqlDao extends EntitySqlDao<TagModelDao, Tag> {
 
     @SqlUpdate
-    public void removeTagFromTransaction(@Bind("tagDefinitionId") final String tagDefinitionId,
-                                         @Bind("objectId") final String objectId,
-                                         @ObjectTypeBinder final ObjectType objectType,
-                                         @InternalTenantContextBinder final InternalCallContext context);
+    @Audited(ChangeType.DELETE)
+    void markTagAsDeleted(@Bind("id") String tagId,
+                          @BindBean InternalCallContext context);
 
     @SqlQuery
-    public Tag findTag(@Bind("tagDefinitionId") final String tagDefinitionId,
-                       @Bind("objectId") final String objectId,
-                       @ObjectTypeBinder final ObjectType objectType,
-                       @InternalTenantContextBinder final InternalTenantContext context);
+    List<TagModelDao> getTagsForObject(@Bind("objectId") UUID objectId,
+                                       @Bind("objectType") ObjectType objectType,
+                                       @BindBean InternalTenantContext internalTenantContext);
 }
diff --git a/util/src/main/java/com/ning/billing/util/tag/DefaultControlTag.java b/util/src/main/java/com/ning/billing/util/tag/DefaultControlTag.java
index f18844f..83e1152 100644
--- a/util/src/main/java/com/ning/billing/util/tag/DefaultControlTag.java
+++ b/util/src/main/java/com/ning/billing/util/tag/DefaultControlTag.java
@@ -18,23 +18,22 @@ package com.ning.billing.util.tag;
 
 import java.util.UUID;
 
-import javax.annotation.Nullable;
-
 import org.joda.time.DateTime;
 
+import com.ning.billing.ObjectType;
+
 public class DefaultControlTag extends DescriptiveTag implements ControlTag {
 
     private final ControlTagType controlTagType;
 
     // use to create new objects
-    public DefaultControlTag(final ControlTagType controlTagType) {
-        super(controlTagType.getId());
-        this.controlTagType = controlTagType;
+    public DefaultControlTag(final ControlTagType controlTagType, final ObjectType objectType, final UUID objectId, final DateTime createdDate) {
+        this(UUID.randomUUID(), controlTagType, objectType, objectId, createdDate);
     }
 
     // use to hydrate objects when loaded from the persistence layer
-    public DefaultControlTag(final UUID id, @Nullable final DateTime createdDate, final ControlTagType controlTagType) {
-        super(id, createdDate, controlTagType.getId());
+    public DefaultControlTag(final UUID id, final ControlTagType controlTagType, final ObjectType objectType, final UUID objectId, final DateTime createdDate) {
+        super(id, controlTagType.getId(), objectType, objectId, createdDate);
         this.controlTagType = controlTagType;
     }
 
diff --git a/util/src/main/java/com/ning/billing/util/tag/DefaultTagDefinition.java b/util/src/main/java/com/ning/billing/util/tag/DefaultTagDefinition.java
index 7732daa..b823b86 100644
--- a/util/src/main/java/com/ning/billing/util/tag/DefaultTagDefinition.java
+++ b/util/src/main/java/com/ning/billing/util/tag/DefaultTagDefinition.java
@@ -21,6 +21,7 @@ import java.util.UUID;
 
 import com.ning.billing.ObjectType;
 import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.tag.dao.TagDefinitionModelDao;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -33,6 +34,10 @@ public class DefaultTagDefinition extends EntityBase implements TagDefinition {
     private final Boolean controlTag;
     private final List<ObjectType> applicableObjectTypes;
 
+    public DefaultTagDefinition(final TagDefinitionModelDao tagDefinitionModelDao, final boolean isControlTag) {
+        this(tagDefinitionModelDao.getId(), tagDefinitionModelDao.getName(), tagDefinitionModelDao.getDescription(), isControlTag);
+    }
+
     public DefaultTagDefinition(final String name, final String description, final Boolean isControlTag) {
         this(UUID.randomUUID(), name, description, isControlTag);
     }
diff --git a/util/src/main/java/com/ning/billing/util/tag/DescriptiveTag.java b/util/src/main/java/com/ning/billing/util/tag/DescriptiveTag.java
index e916d3e..acf4fbb 100644
--- a/util/src/main/java/com/ning/billing/util/tag/DescriptiveTag.java
+++ b/util/src/main/java/com/ning/billing/util/tag/DescriptiveTag.java
@@ -22,22 +22,29 @@ import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
 
+import com.ning.billing.ObjectType;
 import com.ning.billing.util.entity.EntityBase;
 
 public class DescriptiveTag extends EntityBase implements Tag {
 
     private final UUID tagDefinitionId;
+    private final UUID objectId;
+    private final ObjectType objectType;
 
     // use to hydrate objects from the persistence layer
-    public DescriptiveTag(final UUID id, @Nullable final DateTime createdDate, final UUID tagDefinitionId) {
+    public DescriptiveTag(final UUID id, final UUID tagDefinitionId, final ObjectType objectType, final UUID objectId, final DateTime createdDate) {
         super(id, createdDate, createdDate);
         this.tagDefinitionId = tagDefinitionId;
+        this.objectType = objectType;
+        this.objectId = objectId;
     }
 
     // use to create new objects
-    public DescriptiveTag(final UUID tagDefinitionId) {
-        super();
+    public DescriptiveTag(final UUID tagDefinitionId, final ObjectType objectType, final UUID objectId, final DateTime createdDate) {
+        super(UUID.randomUUID(), createdDate, createdDate);
         this.tagDefinitionId = tagDefinitionId;
+        this.objectType = objectType;
+        this.objectId = objectId;
     }
 
     @Override
@@ -46,6 +53,16 @@ public class DescriptiveTag extends EntityBase implements Tag {
     }
 
     @Override
+    public ObjectType getObjectType() {
+        return objectType;
+    }
+
+    @Override
+    public UUID getObjectId() {
+        return objectId;
+    }
+
+    @Override
     public String toString() {
         return "DescriptiveTag [tagDefinitionId=" + tagDefinitionId + ", id=" + id + "]";
     }
diff --git a/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.sql.stg
index 9c6ff53..2f8b857 100644
--- a/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.sql.stg
@@ -1,98 +1,40 @@
-group CustomFieldSqlDao;
-
-CHECK_TENANT() ::= "tenant_record_id = :tenantRecordId"
-AND_CHECK_TENANT() ::= "AND <CHECK_TENANT()>"
-
-insertFromTransaction() ::= <<
-    INSERT INTO custom_fields(id, object_id, object_type, field_name, field_value, created_by, created_date, updated_by, updated_date, account_record_id, tenant_record_id)
-    VALUES (:id, :objectId, :objectType, :fieldName, :fieldValue, :userName, :createdDate, :userName, :updatedDate, :accountRecordId, :tenantRecordId);
->>
-
-updateFromTransaction() ::= <<
-    UPDATE custom_fields
-    SET field_value = :fieldValue, updated_by = :userName, updated_date = :updatedDate
-    WHERE object_id = :objectId AND object_type = :objectType AND field_name = :fieldName
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-deleteFromTransaction() ::= <<
-    DELETE FROM custom_fields
-    WHERE object_id = :objectId AND object_type = :objectType AND field_name = :fieldName
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-load() ::= <<
-    SELECT id, object_id, object_type, field_name, field_value, created_by, created_date, updated_by, updated_date
-    FROM custom_fields
-    WHERE object_id = :objectId AND object_type = :objectType
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-getRecordIds() ::= <<
-    SELECT record_id, id
-    FROM custom_fields
-    WHERE object_id = :objectId AND object_type = :objectType
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-historyFields(prefix) ::= <<
-  <prefix>record_id,
-  <prefix>id,
-  <prefix>object_id,
-  <prefix>object_type,
-  <prefix>field_name,
-  <prefix>field_value,
-  <prefix>updated_by,
-  <prefix>date,
-  <prefix>change_type,
-  <prefix>account_record_id,
-  <prefix>tenant_record_id
->>
-
-addHistoryFromTransaction() ::= <<
-    INSERT INTO custom_field_history(<historyFields()>)
-    VALUES(:recordId, :id, :objectId, :objectType, :fieldName, :fieldValue, :userName, :updatedDate, :changeType, :accountRecordId, :tenantRecordId);
->>
-
-getMaxHistoryRecordId() ::= <<
-    SELECT MAX(history_record_id)
-    FROM custom_field_history
-    WHERE <CHECK_TENANT()>
-    ;
->>
-
-getHistoryRecordIds() ::= <<
-    SELECT history_record_id, record_id
-    FROM custom_field_history
-    WHERE history_record_id > :maxHistoryRecordId
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-auditFields(prefix) ::= <<
-    <prefix>table_name,
-    <prefix>record_id,
-    <prefix>change_type,
-    <prefix>change_date,
-    <prefix>changed_by,
-    <prefix>reason_code,
-    <prefix>comments,
-    <prefix>user_token,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
-
-insertAuditFromTransaction() ::= <<
-    INSERT INTO audit_log(<auditFields()>)
-    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken, :accountRecordId, :tenantRecordId);
->>
-
-test() ::= <<
-    SELECT 1 FROM custom_fields WHERE <CHECK_TENANT()>;
->>
+group CustomFieldSqlDao: EntitySqlDao;
+
+tableName() ::= "custom_fields"
+
+tableFields(prefix) ::= <<
+  <prefix>object_id
+, <prefix>object_type
+, <prefix>field_name
+, <prefix>field_value
+, <prefix>created_by
+, <prefix>created_date
+, <prefix>updated_by
+, <prefix>updated_date
+>>
+
+tableValues() ::= <<
+  :objectId
+, :objectType
+, :fieldName
+, :fieldValue
+, :createdBy
+, :createdDate
+, :updatedBy
+, :updatedDate
+>>
+
+historyTableName() ::= "custom_field_history"
+
+
+getCustomFieldsForObject() ::= <<
+select
+<allTableFields()>
+from <tableName()>
+where
+object_id = :objectId
+and object_type = :objectType
+<AND_CHECK_TENANT()>
 ;
+>>
 
diff --git a/util/src/main/resources/com/ning/billing/util/ddl.sql b/util/src/main/resources/com/ning/billing/util/ddl.sql
index 980d9c9..404ba91 100644
--- a/util/src/main/resources/com/ning/billing/util/ddl.sql
+++ b/util/src/main/resources/com/ning/billing/util/ddl.sql
@@ -21,21 +21,23 @@ CREATE INDEX custom_fields_tenant_account_record_id ON custom_fields(tenant_reco
 
 DROP TABLE IF EXISTS custom_field_history;
 CREATE TABLE custom_field_history (
-    history_record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
-    record_id int(11) unsigned NOT NULL,
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
     id char(36) NOT NULL,
+    target_record_id int(11) unsigned NOT NULL,
     object_id char(36) NOT NULL,
     object_type varchar(30) NOT NULL,
     field_name varchar(30),
     field_value varchar(255),
-    updated_by varchar(50) NOT NULL,
-    date datetime NOT NULL,
     change_type char(6) NOT NULL,
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
+    updated_by varchar(50) NOT NULL,
+    updated_date datetime NOT NULL,
     account_record_id int(11) unsigned default null,
     tenant_record_id int(11) unsigned default null,
-    PRIMARY KEY(history_record_id)
+    PRIMARY KEY(record_id)
 ) ENGINE=innodb;
-CREATE INDEX custom_field_history_record_id ON custom_field_history(record_id);
+CREATE INDEX custom_field_history_target_record_id ON custom_field_history(target_record_id);
 CREATE INDEX custom_field_history_object_id_object_type ON custom_fields(object_id, object_type);
 CREATE INDEX custom_field_history_tenant_account_record_id ON custom_field_history(tenant_record_id, account_record_id);
 
@@ -45,6 +47,7 @@ CREATE TABLE tag_definitions (
     id char(36) NOT NULL,
     name varchar(20) NOT NULL,
     description varchar(200) NOT NULL,
+    is_active bool DEFAULT true,
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
     updated_by varchar(50) NOT NULL,
@@ -53,25 +56,27 @@ CREATE TABLE tag_definitions (
     PRIMARY KEY(record_id)
 ) ENGINE=innodb;
 CREATE UNIQUE INDEX tag_definitions_id ON tag_definitions(id);
-CREATE UNIQUE INDEX tag_definitions_name ON tag_definitions(name);
 CREATE INDEX tag_definitions_tenant_record_id ON tag_definitions(tenant_record_id);
 
 DROP TABLE IF EXISTS tag_definition_history;
 CREATE TABLE tag_definition_history (
-    history_record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
-    record_id int(11) unsigned NOT NULL,
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
     id char(36) NOT NULL,
+    target_record_id int(11) unsigned NOT NULL,
     name varchar(30) NOT NULL,
-    created_by varchar(50),
     description varchar(200),
+    is_active bool DEFAULT true,
     change_type char(6) NOT NULL,
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
     updated_by varchar(50) NOT NULL,
-    date datetime NOT NULL,
+    updated_date datetime NOT NULL,
+    account_record_id int(11) unsigned default null,
     tenant_record_id int(11) unsigned default null,
-    PRIMARY KEY(history_record_id)
+    PRIMARY KEY(record_id)
 ) ENGINE=innodb;
 CREATE INDEX tag_definition_history_id ON tag_definition_history(id);
-CREATE INDEX tag_definition_history_record_id ON tag_definition_history(record_id);
+CREATE INDEX tag_definition_history_target_record_id ON tag_definition_history(target_record_id);
 CREATE INDEX tag_definition_history_name ON tag_definition_history(name);
 CREATE INDEX tag_definition_history_tenant_record_id ON tag_definition_history(tenant_record_id);
 
@@ -82,33 +87,38 @@ CREATE TABLE tags (
     tag_definition_id char(36) NOT NULL,
     object_id char(36) NOT NULL,
     object_type varchar(30) NOT NULL,
+    is_active bool DEFAULT true,
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
+    updated_by varchar(50) NOT NULL,
+    updated_date datetime NOT NULL,
     account_record_id int(11) unsigned default null,
     tenant_record_id int(11) unsigned default null,
     PRIMARY KEY(record_id)
 ) ENGINE = innodb;
 CREATE UNIQUE INDEX tags_id ON tags(id);
 CREATE INDEX tags_by_object ON tags(object_id);
-CREATE UNIQUE INDEX tags_unique ON tags(tag_definition_id, object_id);
 CREATE INDEX tags_tenant_account_record_id ON tags(tenant_record_id, account_record_id);
 
 DROP TABLE IF EXISTS tag_history;
 CREATE TABLE tag_history (
-    history_record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
-    record_id int(11) unsigned NOT NULL,
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
     id char(36) NOT NULL,
+    target_record_id int(11) unsigned NOT NULL,
     object_id char(36) NOT NULL,
     object_type varchar(30) NOT NULL,
     tag_definition_id char(36) NOT NULL,
-    updated_by varchar(50) NOT NULL,
-    date datetime NOT NULL,
+    is_active bool DEFAULT true,
     change_type char(6) NOT NULL,
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
+    updated_by varchar(50) NOT NULL,
+    updated_date datetime NOT NULL,
     account_record_id int(11) unsigned default null,
     tenant_record_id int(11) unsigned default null,
-    PRIMARY KEY(history_record_id)
+    PRIMARY KEY(record_id)
 ) ENGINE = innodb;
-CREATE INDEX tag_history_record_id ON tag_history(record_id);
+CREATE INDEX tag_history_target_record_id ON tag_history(target_record_id);
 CREATE INDEX tag_history_by_object ON tags(object_id);
 CREATE INDEX tag_history_tenant_account_record_id ON tag_history(tenant_record_id, account_record_id);
 
@@ -150,21 +160,22 @@ CREATE INDEX claimed_notifications_tenant_account_record_id ON claimed_notificat
 
 DROP TABLE IF EXISTS audit_log;
 CREATE TABLE audit_log (
-    id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    id char(36) NOT NULL,
     table_name varchar(50) NOT NULL,
-    record_id int(11) NOT NULL,
+    target_record_id int(11) NOT NULL,
     change_type char(6) NOT NULL,
-    change_date datetime NOT NULL,
-    changed_by varchar(50) NOT NULL,
+    created_by varchar(50) NOT NULL,
     reason_code varchar(255) DEFAULT NULL,
     comments varchar(255) DEFAULT NULL,
     user_token char(36),
+    created_date datetime NOT NULL,
     account_record_id int(11) unsigned default null,
     tenant_record_id int(11) unsigned default null,
-    PRIMARY KEY(id)
+    PRIMARY KEY(record_id)
 ) ENGINE=innodb;
-CREATE INDEX audit_log_fetch_record ON audit_log(table_name, record_id);
-CREATE INDEX audit_log_user_name ON audit_log(changed_by);
+CREATE INDEX audit_log_fetch_target_record_id ON audit_log(table_name, target_record_id);
+CREATE INDEX audit_log_user_name ON audit_log(created_by);
 CREATE INDEX audit_log_tenant_account_record_id ON audit_log(tenant_record_id, account_record_id);
 
 DROP TABLE IF EXISTS bus_events;
diff --git a/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg
new file mode 100644
index 0000000..6610aa5
--- /dev/null
+++ b/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg
@@ -0,0 +1,306 @@
+group EntitySqlDao;
+
+
+/******************   To override in each EntitySqlDao template file *****************************/
+
+tableName() ::= ""
+
+/** Leave out id, account_record_id and tenant_record_id **/
+tableFields(prefix) ::= ""
+
+tableValues() ::= ""
+
+historyTableName() ::= ""
+
+historyTableFields(prefix) ::= <<
+  <targetRecordIdField(prefix)>
+, <changeTypeField(prefix)>
+, <tableFields(prefix)>
+>>
+
+historyTableValues() ::= <<
+  <targetRecordIdValue()>
+, <changeTypeValue()>
+, <tableValues()>
+>>
+
+/** Used for entities that can be soft deleted to make we exclude those entries in base calls getById(), get() **/
+andCheckSoftDeletionWithComma(prefix) ::= ""
+
+
+/** Add extra fields for SELECT queries **/
+extraTableFieldsWithComma(prefix) ::= ""
+
+/******************   To override in each EntitySqlDao template file <end>  *****************************/
+
+
+idField(prefix) ::= <<
+<prefix>id
+>>
+
+idValue() ::= ":id"
+
+recordIdField(prefix) ::= <<
+<prefix>record_id
+>>
+
+recordIdValue() ::= ":recordId"
+
+changeTypeField(prefix) ::= <<
+<prefix>change_type
+>>
+
+changeTypeValue() ::= ":changeType"
+
+targetRecordIdField(prefix) ::= <<
+<prefix>target_record_id
+>>
+
+targetRecordIdValue() ::= ":targetRecordId"
+
+/** Override this if the Entity isn't tied to an account **/
+accountRecordIdField(prefix) ::= <<
+<prefix>account_record_id
+>>
+
+
+accountRecordIdFieldWithComma(prefix) ::= <<
+, <accountRecordIdField(prefix)>
+>>
+
+accountRecordIdValue() ::= ":accountRecordId"
+
+accountRecordIdValueWithComma() ::= <<
+, <accountRecordIdValue()>
+>>
+
+tenantRecordIdField(prefix) ::= <<
+<prefix>tenant_record_id
+>>
+
+tenantRecordIdFieldWithComma(prefix) ::= <<
+, <tenantRecordIdField(prefix)>
+>>
+
+
+tenantRecordIdValue() ::= ":tenantRecordId"
+
+tenantRecordIdValueWithComma() ::= <<
+, <tenantRecordIdValue()>
+>>
+
+
+allTableFields(prefix) ::= <<
+  <recordIdField(prefix)>
+, <idField(prefix)>
+, <tableFields(prefix)>
+<extraTableFieldsWithComma(prefix)>
+<accountRecordIdFieldWithComma(prefix)>
+<tenantRecordIdFieldWithComma(prefix)>
+>>
+
+
+
+allTableValues() ::= <<
+  <recordIdValue()>
+, <idValue()>
+, <tableValues()>
+<accountRecordIdValueWithComma()>
+<tenantRecordIdValueWithComma()>
+>>
+
+
+allHistoryTableFields(prefix) ::= <<
+  <recordIdField(prefix)>
+, <idField(prefix)>
+, <targetRecordIdField(prefix)>
+, <historyTableFields(prefix)>
+<accountRecordIdFieldWithComma(prefix)>
+<tenantRecordIdFieldWithComma(prefix)>
+>>
+
+allHistoryTableValues() ::= <<
+  <recordIdValue()>
+, <idValue()>
+,  <targetRecordIdValue()>
+, <historyTableValues()>
+<accountRecordIdValueWithComma()>
+<tenantRecordIdValueWithComma()>
+>>
+
+
+/** Macros used for multi-tenancy (almost any query should use them!) */
+CHECK_TENANT(prefix) ::= "<prefix>tenant_record_id = :tenantRecordId"
+AND_CHECK_TENANT(prefix) ::= "and <CHECK_TENANT(prefix)>"
+
+get(limit) ::= <<
+select
+<allTableFields("t.")>
+from <tableName()> t
+where <CHECK_TENANT("t.")>
+<andCheckSoftDeletionWithComma("t.")>
+<if(limit)>limit :limit<endif>
+;
+>>
+
+getById(id) ::= <<
+select
+<allTableFields("t.")>
+from <tableName()> t
+where <idField("t.")> = :id
+<andCheckSoftDeletionWithComma("t.")>
+<AND_CHECK_TENANT("t.")>
+;
+>>
+
+getByRecordId(recordId) ::= <<
+select
+<allTableFields("t.")>
+from <tableName()> t
+where <recordIdField("t.")> = :recordId
+<AND_CHECK_TENANT("t.")>
+;
+>>
+
+getHistoryTargetRecordId(recordId) ::= <<
+select
+<targetRecordIdField("t.")>
+from <historyTableName()> t
+where <recordIdField("t.")> = :recordId
+<AND_CHECK_TENANT("t.")>
+;
+>>
+
+
+getRecordId(id) ::= <<
+select
+  <recordIdField("t.")>
+from <tableName()> t
+where <idField("t.")> = :id
+<AND_CHECK_TENANT("t.")>
+;
+>>
+
+getRecordIdForTable(tableName) ::= <<
+select
+  <recordIdField("t.")>
+from <tableName> t
+where <idField("t.")> = :id
+<AND_CHECK_TENANT("t.")>
+;
+>>
+
+getHistoryRecordId(targetRecordId) ::= <<
+select
+  max(<recordIdField("t.")>)
+from <historyTableName()> t
+where <targetRecordIdField("t.")> = :targetRecordId
+<AND_CHECK_TENANT("t.")>
+;
+>>
+
+
+getHistoryRecordIdsForTable(historyTableName) ::= <<
+select
+  <recordIdField("t.")>
+from <historyTableName> t
+where <targetRecordIdField("t.")> = :targetRecordId
+<AND_CHECK_TENANT("t.")>
+;
+>>
+
+
+create() ::= <<
+insert into <tableName()> (
+  <idField()>
+, <tableFields()>
+<accountRecordIdFieldWithComma()>
+<tenantRecordIdFieldWithComma()>
+)
+values (
+  <idValue()>
+, <tableValues()>
+<accountRecordIdValueWithComma()>
+<tenantRecordIdValueWithComma()>
+)
+;
+>>
+
+/** Audits, History **/
+auditTableName() ::= "audit_log"
+
+auditTableFields(prefix) ::= <<
+  <prefix>id
+, <prefix>table_name
+, <prefix>target_record_id
+, <prefix>change_type
+, <prefix>created_by
+, <prefix>reason_code
+, <prefix>comments
+, <prefix>user_token
+, <prefix>created_date
+<if(accountRecordIdField(prefix))>, <accountRecordIdField(prefix)><endif>
+<if(tenantRecordIdField(prefix))>, <tenantRecordIdField(prefix)><endif>
+>>
+
+auditTableValues() ::= <<
+  :id
+, :tableName
+, :targetRecordId
+, :changeType
+, :createdBy
+, :reasonCode
+, :comments
+, :userToken
+, :createdDate
+<if(accountRecordIdField(""))>, <accountRecordIdValue()><endif>
+<if(tenantRecordIdField(""))>, <tenantRecordIdValue()><endif>
+>>
+
+
+addHistoryFromTransaction() ::= <<
+insert into <historyTableName()> (
+  <idField()>
+, <historyTableFields()>
+<accountRecordIdFieldWithComma()>
+<tenantRecordIdFieldWithComma()>
+)
+values (
+  <idValue()>
+, <historyTableValues()>
+<accountRecordIdValueWithComma()>
+<tenantRecordIdValueWithComma()>
+)
+;
+>>
+
+
+insertAuditFromTransaction() ::= <<
+insert into <auditTableName()> (
+<auditTableFields()>
+)
+values (
+<auditTableValues()>
+)
+;
+>>
+
+getAuditLogsForTargetRecordId() ::= <<
+select
+  <auditTableFields("t.")>
+from <auditTableName()> t
+where t.target_record_id = :targetRecordId
+and t.table_name = :tableName
+<AND_CHECK_TENANT("t.")>
+order by <recordIdField("t.")> ASC
+;
+>>
+
+test() ::= <<
+select
+<allTableFields("t.")>
+from <tableName()> t
+where <CHECK_TENANT("t.")>
+limit 1
+;
+>>
diff --git a/util/src/main/resources/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.sql.stg
index 9fb5bf2..08e0800 100644
--- a/util/src/main/resources/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.sql.stg
@@ -1,74 +1,70 @@
-group TagDefinitionDao;
+group TagDefinitionDao: EntitySqlDao;
 
-CHECK_TENANT() ::= "tenant_record_id = :tenantRecordId"
-AND_CHECK_TENANT() ::= "AND <CHECK_TENANT()>"
+tableName() ::= "tag_definitions"
 
-fields(prefix) ::= <<
-    <prefix>id,
-    <prefix>name,
-    <prefix>description,
-    <prefix>created_by,
-    <prefix>created_date ,
-    <prefix>updated_by,
-    <prefix>updated_date,
-    <prefix>tenant_record_id
->>
+andCheckSoftDeletionWithComma(prefix) ::= "and <prefix>is_active"
 
-get() ::= <<
-  SELECT <fields()>
-  FROM tag_definitions
-  WHERE <CHECK_TENANT()>
-  ;
+tableFields(prefix) ::= <<
+  <prefix>name
+, <prefix>description
+, <prefix>is_active
+, <prefix>created_by
+, <prefix>created_date
+, <prefix>updated_by
+, <prefix>updated_date
 >>
 
-create() ::= <<
-  INSERT INTO tag_definitions(<fields()>)
-  VALUES(:id, :name, :description, :userName, :createdDate, :userName, :updatedDate, :tenantRecordId);
+tableValues() ::= <<
+  :name
+, :description
+, :isActive
+, :createdBy
+, :createdDate
+, :updatedBy
+, :updatedDate
 >>
 
-load() ::= <<
-  SELECT <fields()>
-  FROM tag_definitions
-  WHERE id = :id
-  <AND_CHECK_TENANT()>
-  ;
->>
+accountRecordIdFieldWithComma(prefix) ::= ""
+
+accountRecordIdValueWithComma() ::= ""
 
+historyTableName() ::= "tag_definition_history"
 
-deleteTagDefinition() ::= <<
-  DELETE FROM tag_definitions
-  WHERE id = :id
-  <AND_CHECK_TENANT()>
-  ;
+markTagDefinitionAsDeleted() ::= <<
+update <tableName()> t
+set t.is_active = 0
+where <idField("t.")> = :id
+<AND_CHECK_TENANT("t.")>
+;
 >>
 
 tagDefinitionUsageCount() ::= <<
-  SELECT COUNT(id)
-  FROM tags
-  WHERE tag_definition_id = :id
-  <AND_CHECK_TENANT()>
+select
+  count(<idField("t.")>)
+from tags t
+where t.is_active
+and t.tag_definition_id = :id
+<AND_CHECK_TENANT("t.")>
+;
 >>
 
 getByName() ::= <<
-  SELECT <fields()>
-   FROM tag_definitions
-  WHERE name = :name
-  <AND_CHECK_TENANT()>
-  ;
->>
-
-getById() ::= <<
-  SELECT <fields()>
-  FROM tag_definitions
-  WHERE id = :id
-  <AND_CHECK_TENANT()>
-  ;
+select
+  <allTableFields("t.")>
+from <tableName()> t
+where t.name = :name
+and t.is_active
+<AND_CHECK_TENANT("t.")>
+;
 >>
 
 getByIds(tag_definition_ids) ::= <<
-  SELECT <fields()>
-  FROM tag_definitions
-  WHERE id IN (<tag_definition_ids: {id | :id_<i0>}; separator="," >)
-  <AND_CHECK_TENANT()>
->>
+select
+  <allTableFields("t.")>
+from <tableName()> t
+where t.is_active
+and <idField("t.")> in (<tag_definition_ids: {id | :id_<i0>}; separator="," >)
+<AND_CHECK_TENANT("t.")>
 ;
+>>
+
diff --git a/util/src/main/resources/com/ning/billing/util/tag/dao/TagSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/tag/dao/TagSqlDao.sql.stg
index a1a674d..4d709a9 100644
--- a/util/src/main/resources/com/ning/billing/util/tag/dao/TagSqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/tag/dao/TagSqlDao.sql.stg
@@ -1,120 +1,48 @@
-group TagDao;
+group TagDao: EntitySqlDao;
 
-CHECK_TENANT() ::= "tenant_record_id = :tenantRecordId"
-AND_CHECK_TENANT() ::= "AND <CHECK_TENANT()>"
+tableName() ::= "tags"
 
-fields(prefix) ::= <<
-    <prefix>id,
-    <prefix>tag_definition_id,
-    <prefix>object_id,
-    <prefix>object_type,
-    <prefix>created_by,
-    <prefix>created_date,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
-
-insertFromTransaction() ::= <<
-  INSERT INTO tags(<fields()>)
-  VALUES (:id, :tagDefinitionId, :objectId, :objectType, :userName, :createdDate, :accountRecordId, :tenantRecordId);
->>
-
-deleteFromTransaction() ::= <<
-    DELETE FROM tags
-    WHERE tag_definition_id = :tagDefinitionId
-        AND object_id = :objectId AND object_type = :objectType
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-addTagFromTransaction() ::= <<
-    INSERT INTO tags(<fields()>)
-    VALUES (:id, :tagDefinitionId, :objectId, :objectType, :userName, :createdDate, :accountRecordId, :tenantRecordId);
->>
-
-removeTagFromTransaction() ::= <<
-    DELETE FROM tags
-    WHERE tag_definition_id = :tagDefinitionId
-    AND object_id = :objectId AND object_type = :objectType
-    <AND_CHECK_TENANT()>
-    ;
->>
+andCheckSoftDeletionWithComma(prefix) ::= "and <prefix>is_active"
 
-findTag() ::= <<
-    SELECT <fields()> FROM tags
-    WHERE tag_definition_id = :tagDefinitionId
-    AND object_id = :objectId AND object_type = :objectType
-    <AND_CHECK_TENANT()>
-    ;
+tableFields(prefix) ::= <<
+  <prefix>tag_definition_id
+, <prefix>object_id
+, <prefix>object_type
+, <prefix>is_active
+, <prefix>created_by
+, <prefix>created_date
+, <prefix>updated_by
+, <prefix>updated_date
 >>
 
-getRecordIds() ::= <<
-    SELECT record_id, id
-    FROM tags
-    WHERE object_id = :objectId AND object_type = :objectType
-    <AND_CHECK_TENANT()>
-    ;
+tableValues() ::= <<
+  :tagDefinitionId
+, :objectId
+, :objectType
+, :isActive
+, :createdBy
+, :createdDate
+, :updatedBy
+, :updatedDate
 >>
 
-historyFields(prefix) ::= <<
-  <prefix>record_id,
-  <prefix>id,
-  <prefix>object_id,
-  <prefix>object_type,
-  <prefix>tag_definition_id,
-  <prefix>updated_by,
-  <prefix>date,
-  <prefix>change_type,
-  <prefix>account_record_id,
-  <prefix>tenant_record_id
->>
-
-addHistoryFromTransaction() ::= <<
-    INSERT INTO tag_history(<historyFields()>)
-    VALUES(:recordId, :id, :objectId, :objectType, :tagDefinitionId, :userName, :updatedDate, :changeType, :accountRecordId, :tenantRecordId);
->>
+historyTableName() ::= "tag_history"
 
-getMaxHistoryRecordId() ::= <<
-    SELECT MAX(history_record_id)
-    FROM tag_history
-    WHERE <CHECK_TENANT()>
-    ;
->>
-
-getHistoryRecordIds() ::= <<
-    SELECT history_record_id, record_id
-    FROM tag_history
-    WHERE history_record_id > :maxHistoryRecordId
-    <AND_CHECK_TENANT()>
-    ;
->>
-
-auditFields(prefix) ::= <<
-    <prefix>table_name,
-    <prefix>record_id,
-    <prefix>change_type,
-    <prefix>change_date,
-    <prefix>changed_by,
-    <prefix>reason_code,
-    <prefix>comments,
-    <prefix>user_token,
-    <prefix>account_record_id,
-    <prefix>tenant_record_id
->>
-
-load() ::= <<
-  SELECT <fields()>
-    FROM tags
-  WHERE object_id = :objectId AND object_type = :objectType
-  <AND_CHECK_TENANT()>;
->>
-
-insertAuditFromTransaction() ::= <<
-    INSERT INTO audit_log(<auditFields()>)
-    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken, :accountRecordId, :tenantRecordId);
+markTagAsDeleted() ::= <<
+update <tableName()> t
+set t.is_active = 0
+where <idField("t.")> = :id
+<AND_CHECK_TENANT("t.")>
+;
 >>
 
-test() ::= <<
-  SELECT 1 FROM tags WHERE <CHECK_TENANT()>;
->>
+getTagsForObject() ::= <<
+select
+  <allTableFields("t.")>
+from <tableName()> t
+where t.is_active
+and t.object_id = :objectId
+and t.object_type = :objectType
+<AND_CHECK_TENANT("t.")>
 ;
+>>
diff --git a/util/src/test/java/com/ning/billing/dbi/DBIProvider.java b/util/src/test/java/com/ning/billing/dbi/DBIProvider.java
index 4855d78..b66f87d 100644
--- a/util/src/test/java/com/ning/billing/dbi/DBIProvider.java
+++ b/util/src/test/java/com/ning/billing/dbi/DBIProvider.java
@@ -16,10 +16,19 @@
 
 package com.ning.billing.dbi;
 
+import java.util.concurrent.TimeUnit;
+
 import org.skife.jdbi.v2.DBI;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.tweak.transactions.SerializableTransactionRunner;
 
+import com.ning.billing.util.dao.DateTimeArgumentFactory;
+import com.ning.billing.util.dao.DateTimeZoneArgumentFactory;
+import com.ning.billing.util.dao.EnumArgumentFactory;
+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.google.inject.Provider;
 import com.jolbox.bonecp.BoneCPConfig;
@@ -27,29 +36,43 @@ import com.jolbox.bonecp.BoneCPDataSource;
 
 public class DBIProvider implements Provider<IDBI> {
 
-    private final DbiConfig config;
+    private final BoneCPConfig dbConfig;
 
     @Inject
     public DBIProvider(final DbiConfig config) {
-        this.config = config;
+        this(config.getJdbcUrl(), config.getUsername(), config.getPassword());
     }
 
-    @Override
-    public IDBI get() {
+    public DBIProvider(final String dbiString, final String userName, final String pwd) {
+        this.dbConfig = createConfig(dbiString, userName, pwd);
+    }
+
+    BoneCPConfig createConfig(final String dbiString, final String userName, final String pwd) {
         final BoneCPConfig dbConfig = new BoneCPConfig();
-        dbConfig.setJdbcUrl(config.getJdbcUrl());
-        dbConfig.setUsername(config.getUsername());
-        dbConfig.setPassword(config.getPassword());
-        dbConfig.setMinConnectionsPerPartition(config.getMinIdle());
-        dbConfig.setMaxConnectionsPerPartition(config.getMaxActive());
-        dbConfig.setConnectionTimeout(config.getConnectionTimeout().getPeriod(), config.getConnectionTimeout().getUnit());
+        dbConfig.setJdbcUrl(dbiString);
+        dbConfig.setUsername(userName);
+        dbConfig.setPassword(pwd);
+        dbConfig.setMinConnectionsPerPartition(1);
+        dbConfig.setMaxConnectionsPerPartition(30);
+        dbConfig.setConnectionTimeout(10, TimeUnit.SECONDS);
         dbConfig.setPartitionCount(1);
         dbConfig.setDefaultTransactionIsolation("REPEATABLE_READ");
         dbConfig.setDisableJMX(false);
         dbConfig.setLazyInit(true);
+        return dbConfig;
+    }
 
+    @Override
+    public IDBI get() {
         final BoneCPDataSource ds = new BoneCPDataSource(dbConfig);
         final DBI dbi = new DBI(ds);
+        dbi.registerArgumentFactory(new UUIDArgumentFactory());
+        dbi.registerArgumentFactory(new DateTimeZoneArgumentFactory());
+        dbi.registerArgumentFactory(new DateTimeArgumentFactory());
+        dbi.registerArgumentFactory(new LocalDateArgumentFactory());
+        dbi.registerArgumentFactory(new EnumArgumentFactory());
+        dbi.registerMapper(new UuidMapper());
+
         // Restart transactions in case of deadlocks
         dbi.setTransactionHandler(new SerializableTransactionRunner());
         //final SQLLog log = new Log4JLog();
diff --git a/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java b/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
index bcf65bc..b5105a9 100644
--- a/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
+++ b/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
@@ -76,7 +76,6 @@ public class MysqlTestingHelper {
         }
     }
 
-
     public boolean isUsingLocalInstance() {
         return (System.getProperty(USE_LOCAL_DB_PROP) != null);
     }
@@ -163,7 +162,6 @@ public class MysqlTestingHelper {
         return allTables;
     }
 
-
     public void stopMysql() {
         try {
             if (mysqldResource != null) {
@@ -177,9 +175,14 @@ public class MysqlTestingHelper {
         }
     }
 
-    public IDBI getDBI() {
-        final String dbiString = getJdbcConnectionString() + "?createDatabaseIfNotExist=true&allowMultiQueries=true";
-        return new DBI(dbiString, USERNAME, PASSWORD);
+    private IDBI dbiInstance = null;
+
+    public synchronized IDBI getDBI() {
+        if (dbiInstance == null) {
+            final String dbiString = getJdbcConnectionString() + "?createDatabaseIfNotExist=true&allowMultiQueries=true";
+            dbiInstance = new DBIProvider(dbiString, USERNAME, PASSWORD).get();
+        }
+        return dbiInstance;
     }
 
     public String getJdbcConnectionString() {
@@ -228,14 +231,16 @@ public class MysqlTestingHelper {
                "account_record_id int(11) unsigned not null, tenant_record_id int(11) unsigned default 0, primary key(record_id)) engine=innodb;");
 
         for (final String pack : new String[]{"account", "analytics", "beatrix", "entitlement", "util", "payment", "invoice", "junction", "tenant"}) {
-            final String ddl;
-            try {
-                ddl = IOUtils.toString(Resources.getResource("com/ning/billing/" + pack + "/ddl.sql").openStream());
-            } catch (final IllegalArgumentException ignored) {
-                // The test doesn't have this module ddl in the classpath - that's fine
-                continue;
+            for (final String ddlFile : new String[]{"ddl.sql", "ddl_test.sql"}) {
+                final String ddl;
+                try {
+                    ddl = IOUtils.toString(Resources.getResource("com/ning/billing/" + pack + "/" + ddlFile).openStream());
+                } catch (final IllegalArgumentException ignored) {
+                    // The test doesn't have this module ddl in the classpath - that's fine
+                    continue;
+                }
+                initDb(ddl);
             }
-            initDb(ddl);
         }
     }
 
@@ -247,7 +252,7 @@ public class MysqlTestingHelper {
         dbi.withHandle(new HandleCallback<Void>() {
             @Override
             public Void withHandle(final Handle handle) throws Exception {
-                log.info("Executing DDL script: " + ddl);
+                log.debug("Executing DDL script: " + ddl);
                 handle.createScript(ddl).execute();
                 return null;
             }
diff --git a/util/src/test/java/com/ning/billing/KillbillTestSuite.java b/util/src/test/java/com/ning/billing/KillbillTestSuite.java
index f5d9882..b0d009d 100644
--- a/util/src/test/java/com/ning/billing/KillbillTestSuite.java
+++ b/util/src/test/java/com/ning/billing/KillbillTestSuite.java
@@ -32,6 +32,8 @@ 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.clock.Clock;
+import com.ning.billing.util.clock.ClockMock;
 
 public class KillbillTestSuite {
 
@@ -40,10 +42,12 @@ public class KillbillTestSuite {
 
     private boolean hasFailed = false;
 
+    private Clock clock = new ClockMock();
+
     protected final InternalCallContext internalCallContext = new InternalCallContext(InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID, 1687L, UUID.randomUUID(),
                                                                                       UUID.randomUUID().toString(), CallOrigin.TEST,
                                                                                       UserType.TEST, "Testing", "This is a test",
-                                                                                      new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC));
+                                                                                      clock.getUTCNow(), clock.getUTCNow());
     protected final CallContext callContext = internalCallContext.toCallContext();
 
     @BeforeMethod(alwaysRun = true)
diff --git a/util/src/test/java/com/ning/billing/mock/api/MockEntitlementUserApi.java b/util/src/test/java/com/ning/billing/mock/api/MockEntitlementUserApi.java
index 8a9a6dc..e95dfaa 100644
--- a/util/src/test/java/com/ning/billing/mock/api/MockEntitlementUserApi.java
+++ b/util/src/test/java/com/ning/billing/mock/api/MockEntitlementUserApi.java
@@ -74,7 +74,7 @@ public class MockEntitlementUserApi implements EntitlementUserApi {
             }
 
             @Override
-            public String getKey() {
+            public String getExternalKey() {
                 return key;
             }
 
diff --git a/util/src/test/java/com/ning/billing/util/audit/api/TestDefaultAuditUserApi.java b/util/src/test/java/com/ning/billing/util/audit/api/TestDefaultAuditUserApi.java
index d1fef2c..32912cf 100644
--- a/util/src/test/java/com/ning/billing/util/audit/api/TestDefaultAuditUserApi.java
+++ b/util/src/test/java/com/ning/billing/util/audit/api/TestDefaultAuditUserApi.java
@@ -67,7 +67,7 @@ public class TestDefaultAuditUserApi extends AuditLogsTestBase {
             }
         }
 
-        auditUserApi = new DefaultAuditUserApi(auditDao, Mockito.mock(InternalCallContextFactory.class));
+        auditUserApi = new DefaultAuditUserApi(auditDao, null, Mockito.mock(InternalCallContextFactory.class));
     }
 
     @Test(groups = "fast")
diff --git a/util/src/test/java/com/ning/billing/util/audit/dao/TestDefaultAuditDao.java b/util/src/test/java/com/ning/billing/util/audit/dao/TestDefaultAuditDao.java
index d0fe9c4..d24059a 100644
--- a/util/src/test/java/com/ning/billing/util/audit/dao/TestDefaultAuditDao.java
+++ b/util/src/test/java/com/ning/billing/util/audit/dao/TestDefaultAuditDao.java
@@ -18,7 +18,6 @@ package com.ning.billing.util.audit.dao;
 
 import java.io.IOException;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
 import org.skife.jdbi.v2.Handle;
@@ -30,32 +29,38 @@ import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.ning.billing.ObjectType;
+import com.ning.billing.mock.glue.MockDbHelperModule;
+import com.ning.billing.mock.glue.MockEntitlementModule;
 import com.ning.billing.util.UtilTestSuiteWithEmbeddedDB;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.audit.AuditLog;
 import com.ning.billing.util.audit.ChangeType;
-import com.ning.billing.util.svcsapi.bus.InternalBus;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.dao.TableName;
 import com.ning.billing.util.glue.AuditModule;
-import com.ning.billing.util.tag.MockTagStoreModuleSql;
+import com.ning.billing.util.glue.BusModule;
+import com.ning.billing.util.glue.ClockModule;
+import com.ning.billing.util.glue.TagStoreModule;
+import com.ning.billing.util.svcsapi.bus.InternalBus;
+import com.ning.billing.util.tag.DescriptiveTag;
 import com.ning.billing.util.tag.Tag;
-import com.ning.billing.util.tag.TagDefinition;
-import com.ning.billing.util.tag.dao.AuditedTagDao;
+import com.ning.billing.util.tag.dao.DefaultTagDao;
 import com.ning.billing.util.tag.dao.TagDefinitionDao;
+import com.ning.billing.util.tag.dao.TagDefinitionModelDao;
+import com.ning.billing.util.tag.dao.TagModelDao;
 
 import com.google.inject.Inject;
 
-@Guice(modules = {MockTagStoreModuleSql.class, AuditModule.class})
+@Guice(modules = {TagStoreModule.class, AuditModule.class, MockEntitlementModule.class, ClockModule.class, BusModule.class, MockDbHelperModule.class})
 public class TestDefaultAuditDao extends UtilTestSuiteWithEmbeddedDB {
 
     @Inject
     private TagDefinitionDao tagDefinitionDao;
 
     @Inject
-    private AuditedTagDao tagDao;
+    private DefaultTagDao tagDao;
 
     @Inject
     private AuditDao auditDao;
@@ -108,19 +113,22 @@ public class TestDefaultAuditDao extends UtilTestSuiteWithEmbeddedDB {
 
     private void addTag() throws TagDefinitionApiException, TagApiException {
         // Create a tag definition
-        final TagDefinition tagDefinition = tagDefinitionDao.create(UUID.randomUUID().toString().substring(0, 5),
-                                                                    UUID.randomUUID().toString().substring(0, 5),
-                                                                    internalCallContext);
+        final TagDefinitionModelDao tagDefinition = tagDefinitionDao.create(UUID.randomUUID().toString().substring(0, 5),
+                                                                            UUID.randomUUID().toString().substring(0, 5),
+                                                                            internalCallContext);
         Assert.assertEquals(tagDefinitionDao.getById(tagDefinition.getId(), internalCallContext), tagDefinition);
 
         // Create a tag
         final UUID objectId = UUID.randomUUID();
-        tagDao.insertTag(objectId, ObjectType.ACCOUNT, tagDefinition.getId(), internalCallContext);
-        final Map<String, Tag> tags = tagDao.loadEntities(objectId, ObjectType.ACCOUNT, internalCallContext);
+
+        final Tag tag = new DescriptiveTag(tagDefinition.getId(), ObjectType.ACCOUNT, objectId, clock.getUTCNow());
+
+        tagDao.create(new TagModelDao(tag), internalCallContext);
+        final List<TagModelDao> tags = tagDao.getTags(objectId, ObjectType.ACCOUNT, internalCallContext);
         Assert.assertEquals(tags.size(), 1);
-        final Tag tag = tags.values().iterator().next();
-        Assert.assertEquals(tag.getTagDefinitionId(), tagDefinition.getId());
-        tagId = tag.getId();
+        final TagModelDao savedTag = tags.get(0);
+        Assert.assertEquals(savedTag.getTagDefinitionId(), tagDefinition.getId());
+        tagId = savedTag.getId();
     }
 
     private void verifyAuditLogsForTag(final List<AuditLog> auditLogs, final AuditLevel level) {
@@ -132,9 +140,9 @@ public class TestDefaultAuditDao extends UtilTestSuiteWithEmbeddedDB {
         Assert.assertEquals(auditLogs.size(), 1);
         Assert.assertEquals(auditLogs.get(0).getUserToken(), internalCallContext.getUserToken().toString());
         Assert.assertEquals(auditLogs.get(0).getChangeType(), ChangeType.INSERT);
-        Assert.assertEquals(auditLogs.get(0).getComment(), internalCallContext.getComment());
+        Assert.assertEquals(auditLogs.get(0).getComment(), internalCallContext.getComments());
         Assert.assertEquals(auditLogs.get(0).getReasonCode(), internalCallContext.getReasonCode());
-        Assert.assertEquals(auditLogs.get(0).getUserName(), internalCallContext.getUserName());
+        Assert.assertEquals(auditLogs.get(0).getUserName(), internalCallContext.getCreatedBy());
         Assert.assertNotNull(auditLogs.get(0).getCreatedDate());
     }
 }
diff --git a/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLog.java b/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLog.java
index a6fa217..db78585 100644
--- a/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLog.java
+++ b/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLog.java
@@ -37,7 +37,7 @@ public class TestDefaultAuditLog extends UtilTestSuite {
         final TableName tableName = TableName.ACCOUNT_EMAIL_HISTORY;
         final long recordId = Long.MAX_VALUE;
         final ChangeType changeType = ChangeType.DELETE;
-        final EntityAudit entityAudit = new EntityAudit(tableName, recordId, changeType);
+        final EntityAudit entityAudit = new EntityAudit(tableName, recordId, changeType, null);
 
         final UUID tenantId = UUID.randomUUID();
         final String userName = UUID.randomUUID().toString();
@@ -61,7 +61,7 @@ public class TestDefaultAuditLog extends UtilTestSuite {
         final TableName tableName = TableName.ACCOUNT_EMAIL_HISTORY;
         final long recordId = Long.MAX_VALUE;
         final ChangeType changeType = ChangeType.DELETE;
-        final EntityAudit entityAudit = new EntityAudit(tableName, recordId, changeType);
+        final EntityAudit entityAudit = new EntityAudit(tableName, recordId, changeType, null);
 
         final UUID tenantId = UUID.randomUUID();
         final String userName = UUID.randomUUID().toString();
diff --git a/util/src/test/java/com/ning/billing/util/callcontext/TestCallContext.java b/util/src/test/java/com/ning/billing/util/callcontext/TestCallContext.java
index 99d1870..d094414 100644
--- a/util/src/test/java/com/ning/billing/util/callcontext/TestCallContext.java
+++ b/util/src/test/java/com/ning/billing/util/callcontext/TestCallContext.java
@@ -63,7 +63,7 @@ public class TestCallContext implements CallContext {
     }
 
     @Override
-    public String getComment() {
+    public String getComments() {
         return null;
     }
 
diff --git a/util/src/test/java/com/ning/billing/util/callcontext/TestDefaultCallContext.java b/util/src/test/java/com/ning/billing/util/callcontext/TestDefaultCallContext.java
index 8d79422..860d537 100644
--- a/util/src/test/java/com/ning/billing/util/callcontext/TestDefaultCallContext.java
+++ b/util/src/test/java/com/ning/billing/util/callcontext/TestDefaultCallContext.java
@@ -43,7 +43,7 @@ public class TestDefaultCallContext extends UtilTestSuite {
         Assert.assertEquals(callContext.getTenantId(), tenantId);
         Assert.assertEquals(callContext.getCreatedDate(), createdDate);
         Assert.assertNull(callContext.getCallOrigin());
-        Assert.assertEquals(callContext.getComment(), comment);
+        Assert.assertEquals(callContext.getComments(), comment);
         Assert.assertEquals(callContext.getReasonCode(), reasonCode);
         Assert.assertEquals(callContext.getUserName(), userName);
         Assert.assertEquals(callContext.getUpdatedDate(), createdDate);
diff --git a/util/src/test/java/com/ning/billing/util/callcontext/TestInternalCallContextFactory.java b/util/src/test/java/com/ning/billing/util/callcontext/TestInternalCallContextFactory.java
index 5564b47..20e5d3e 100644
--- a/util/src/test/java/com/ning/billing/util/callcontext/TestInternalCallContextFactory.java
+++ b/util/src/test/java/com/ning/billing/util/callcontext/TestInternalCallContextFactory.java
@@ -96,13 +96,13 @@ public class TestInternalCallContextFactory extends UtilTestSuiteWithEmbeddedDB 
 
     private void verifyInternalCallContext(final InternalCallContext context) {
         Assert.assertEquals(context.getCallOrigin(), callContext.getCallOrigin());
-        Assert.assertEquals(context.getComment(), callContext.getComment());
+        Assert.assertEquals(context.getComments(), callContext.getComments());
         Assert.assertEquals(context.getCreatedDate(), callContext.getCreatedDate());
         Assert.assertEquals(context.getReasonCode(), callContext.getReasonCode());
         Assert.assertEquals(context.getUpdatedDate(), callContext.getUpdatedDate());
-        Assert.assertEquals(context.getUserName(), callContext.getUserName());
+        Assert.assertEquals(context.getCreatedBy(), callContext.getUserName());
         Assert.assertEquals(context.getUserToken(), callContext.getUserToken());
-        Assert.assertEquals(context.getUserType(), callContext.getUserType());
+        Assert.assertEquals(context.getContextUserType(), callContext.getUserType());
         // Our test callContext doesn't have a tenant id
         Assert.assertEquals(context.getTenantRecordId(), (Long) InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID);
     }
diff --git a/util/src/test/java/com/ning/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java b/util/src/test/java/com/ning/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java
index 79afb13..ac498c4 100644
--- a/util/src/test/java/com/ning/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java
+++ b/util/src/test/java/com/ning/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java
@@ -33,7 +33,7 @@ import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.customfield.CustomField;
 import com.ning.billing.util.customfield.StringCustomField;
-import com.ning.billing.util.customfield.dao.AuditedCustomFieldDao;
+import com.ning.billing.util.customfield.dao.DefaultCustomFieldDao;
 import com.ning.billing.util.customfield.dao.CustomFieldDao;
 
 import com.google.common.collect.ImmutableList;
@@ -45,7 +45,7 @@ public class TestDefaultCustomFieldUserApi extends UtilTestSuiteWithEmbeddedDB {
     @BeforeMethod(groups = "slow")
     public void setUp() throws Exception {
         final InternalCallContextFactory internalCallContextFactory = new InternalCallContextFactory(getMysqlTestingHelper().getDBI(), new ClockMock());
-        final CustomFieldDao customFieldDao = new AuditedCustomFieldDao(getMysqlTestingHelper().getDBI());
+        final CustomFieldDao customFieldDao = new DefaultCustomFieldDao(getMysqlTestingHelper().getDBI());
         customFieldUserApi = new DefaultCustomFieldUserApi(internalCallContextFactory, customFieldDao);
     }
 
@@ -65,13 +65,13 @@ public class TestDefaultCustomFieldUserApi extends UtilTestSuiteWithEmbeddedDB {
             }
         });
 
-        final CustomField customField = new StringCustomField(UUID.randomUUID().toString().substring(1, 4), UUID.randomUUID().toString().substring(1, 4));
-        customFieldUserApi.saveCustomFields(accountId, ObjectType.ACCOUNT, ImmutableList.<CustomField>of(customField), callContext);
+        final CustomField customField = new StringCustomField(UUID.randomUUID().toString().substring(1, 4), UUID.randomUUID().toString().substring(1, 4), ObjectType.ACCOUNT, accountId, callContext.getCreatedDate());
+        customFieldUserApi.addCustomFields(ImmutableList.<CustomField>of(customField), callContext);
 
         // Verify the field was saved
-        final Map<String, CustomField> customFields = customFieldUserApi.getCustomFields(accountId, ObjectType.ACCOUNT, callContext);
-        Assert.assertEquals(customFields.keySet().size(), 1);
-        Assert.assertEquals(customFields.get(customField.getName()), customField);
+        final List<CustomField> customFields = customFieldUserApi.getCustomFields(accountId, ObjectType.ACCOUNT, callContext);
+        Assert.assertEquals(customFields.size(), 1);
+        Assert.assertEquals(customFields.get(0), customField);
         // Verify the account_record_id was populated
         getMysqlTestingHelper().getDBI().withHandle(new HandleCallback<Void>() {
             @Override
diff --git a/util/src/test/java/com/ning/billing/util/customfield/dao/MockCustomFieldDao.java b/util/src/test/java/com/ning/billing/util/customfield/dao/MockCustomFieldDao.java
index b1e703f..354da71 100644
--- a/util/src/test/java/com/ning/billing/util/customfield/dao/MockCustomFieldDao.java
+++ b/util/src/test/java/com/ning/billing/util/customfield/dao/MockCustomFieldDao.java
@@ -16,48 +16,27 @@
 
 package com.ning.billing.util.customfield.dao;
 
-import java.util.HashMap;
+import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-
 import com.ning.billing.ObjectType;
-import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.entity.dao.MockEntityDaoBase;
 
-public class MockCustomFieldDao implements CustomFieldDao {
-
-    private final Map<UUID, List<CustomField>> fields = new HashMap<UUID, List<CustomField>>();
+public class MockCustomFieldDao extends MockEntityDaoBase<CustomFieldModelDao, CustomField, CustomFieldApiException> implements CustomFieldDao {
 
     @Override
-    public void saveEntitiesFromTransaction(final Transmogrifier transactionalDao, final UUID objectId, final ObjectType objectType,
-                                            final List<CustomField> entities, final InternalCallContext context) {
-        fields.put(objectId, entities);
-    }
-
-    @Override
-    public void saveEntities(final UUID objectId, final ObjectType objectType, final List<CustomField> entities, final InternalCallContext context) {
-        fields.put(objectId, entities);
-    }
-
-    @Override
-    public Map<String, CustomField> loadEntities(final UUID objectId, final ObjectType objectType, final InternalTenantContext context) {
-        return getMap(fields.get(objectId));
-    }
-
-    @Override
-    public Map<String, CustomField> loadEntitiesFromTransaction(final Transmogrifier dao, final UUID objectId, final ObjectType objectType, final InternalTenantContext context) {
-        return getMap(fields.get(objectId));
-    }
-
-    private Map<String, CustomField> getMap(final List<CustomField> customFields) {
-        final Map<String, CustomField> map = new HashMap<String, CustomField>();
-        for (final CustomField customField : customFields) {
-            map.put(customField.getName(), customField);
+    public List<CustomFieldModelDao> getCustomFields(final UUID objectId, final ObjectType objectType, final InternalTenantContext context) {
+        final List<CustomFieldModelDao> result = new ArrayList<CustomFieldModelDao>();
+        final List<CustomFieldModelDao> all = get(context);
+        for (final CustomFieldModelDao cur : all) {
+            if (cur.getObjectId().equals(objectId) && cur.getObjectType() == objectType) {
+                result.add(cur);
+            }
         }
-        return map;
+        return result;
     }
 }
diff --git a/util/src/test/java/com/ning/billing/util/customfield/TestFieldStore.java b/util/src/test/java/com/ning/billing/util/customfield/TestFieldStore.java
index 24fcde1..f401ca4 100644
--- a/util/src/test/java/com/ning/billing/util/customfield/TestFieldStore.java
+++ b/util/src/test/java/com/ning/billing/util/customfield/TestFieldStore.java
@@ -29,24 +29,24 @@ import com.ning.billing.KillbillTestSuiteWithEmbeddedDB;
 import com.ning.billing.ObjectType;
 import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.util.UtilTestSuiteWithEmbeddedDB;
-import com.ning.billing.util.customfield.dao.AuditedCustomFieldDao;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.customfield.dao.CustomFieldDao;
-import com.ning.billing.util.customfield.dao.CustomFieldSqlDao;
+import com.ning.billing.util.customfield.dao.CustomFieldModelDao;
+import com.ning.billing.util.customfield.dao.DefaultCustomFieldDao;
 
-import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.fail;
 
 public class TestFieldStore extends UtilTestSuiteWithEmbeddedDB {
+
     private final Logger log = LoggerFactory.getLogger(TestFieldStore.class);
     private final MysqlTestingHelper helper = KillbillTestSuiteWithEmbeddedDB.getMysqlTestingHelper();
-    private IDBI dbi;
     private CustomFieldDao customFieldDao;
 
     @BeforeClass(groups = "slow")
     protected void setup() throws IOException {
         try {
-            dbi = helper.getDBI();
-            customFieldDao = new AuditedCustomFieldDao(dbi);
+            final IDBI dbi = helper.getDBI();
+            customFieldDao = new DefaultCustomFieldDao(dbi);
         } catch (Throwable t) {
             log.error("Setup failed", t);
             fail(t.toString());
@@ -54,33 +54,19 @@ public class TestFieldStore extends UtilTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow")
-    public void testFieldStore() {
+    public void testCreateCustomField() throws CustomFieldApiException {
         final UUID id = UUID.randomUUID();
         final ObjectType objectType = ObjectType.ACCOUNT;
 
-        final FieldStore fieldStore1 = new DefaultFieldStore(id, objectType);
-
-        final String fieldName = "TestField1";
+        String fieldName = "TestField1";
         String fieldValue = "Kitty Hawk";
-        fieldStore1.setValue(fieldName, fieldValue);
-
-        final CustomFieldSqlDao customFieldSqlDao = dbi.onDemand(CustomFieldSqlDao.class);
-        customFieldDao.saveEntitiesFromTransaction(customFieldSqlDao, id, objectType, fieldStore1.getEntityList(), internalCallContext);
 
-        final FieldStore fieldStore2 = DefaultFieldStore.create(id, objectType);
-        fieldStore2.add(customFieldSqlDao.load(id.toString(), objectType, internalCallContext));
-
-        assertEquals(fieldStore2.getValue(fieldName), fieldValue);
+        final CustomField field = new StringCustomField(fieldName, fieldValue, objectType, id, internalCallContext.getCreatedDate());
+        customFieldDao.create(new CustomFieldModelDao(field), internalCallContext);
 
+        fieldName = "TestField2";
         fieldValue = "Cape Canaveral";
-        fieldStore2.setValue(fieldName, fieldValue);
-        assertEquals(fieldStore2.getValue(fieldName), fieldValue);
-        customFieldDao.saveEntitiesFromTransaction(customFieldSqlDao, id, objectType, fieldStore2.getEntityList(), internalCallContext);
-
-        final FieldStore fieldStore3 = DefaultFieldStore.create(id, objectType);
-        assertEquals(fieldStore3.getValue(fieldName), null);
-        fieldStore3.add(customFieldSqlDao.load(id.toString(), objectType, internalCallContext));
-
-        assertEquals(fieldStore3.getValue(fieldName), fieldValue);
+        final CustomField field2 = new StringCustomField(fieldName, fieldValue, objectType, id, internalCallContext.getCreatedDate());
+        customFieldDao.create(new CustomFieldModelDao(field2), internalCallContext);
     }
 }
diff --git a/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java b/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java
new file mode 100644
index 0000000..5513d73
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.dao;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import org.antlr.stringtemplate.StringTemplateGroup;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.ning.billing.util.UtilTestSuite;
+
+import com.google.common.collect.ImmutableMap;
+
+public class TestStringTemplateInheritance extends UtilTestSuite {
+
+    InputStream entityStream;
+    InputStream kombuchaStream;
+
+    @BeforeMethod(groups = "fast")
+    public void setUp() throws Exception {
+        entityStream = this.getClass().getResourceAsStream("/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg");
+        kombuchaStream = this.getClass().getResourceAsStream("/com/ning/billing/util/dao/Kombucha.sql.stg");
+    }
+
+    @AfterMethod(groups = "fast")
+    public void tearDown() throws Exception {
+        if (entityStream != null) {
+            entityStream.close();
+        }
+        if (kombuchaStream != null) {
+            kombuchaStream.close();
+        }
+    }
+
+    @Test(groups = "fast")
+    public void testCheckQueries() throws Exception {
+        // From http://www.antlr.org/wiki/display/ST/ST+condensed+--+Templates+and+groups#STcondensed--Templatesandgroups-Withsupergroupfile:
+        //     there is no mechanism for automatically loading a mentioned super-group file
+        new StringTemplateGroup(new InputStreamReader(entityStream));
+
+        final StringTemplateGroup kombucha = new StringTemplateGroup(new InputStreamReader(kombuchaStream));
+
+        // Verify non inherited template
+        Assert.assertEquals(kombucha.getInstanceOf("isIsTimeForKombucha").toString(), "select hour(current_timestamp()) = 17 as is_time;");
+
+        // Verify inherited templates
+        Assert.assertEquals(kombucha.getInstanceOf("getById").toString(), "select\n" +
+                                                                          "  t.record_id\n" +
+                                                                          ", t.id\n" +
+                                                                          ", t.tea\n" +
+                                                                          ", t.mushroom\n" +
+                                                                          ", t.sugar\n" +
+                                                                          ", t.account_record_id\n" +
+                                                                          ", t.tenant_record_id\n" +
+                                                                          "from kombucha t\n" +
+                                                                          "where t.id = :id\n" +
+                                                                          "and t.tenant_record_id = :tenantRecordId\n" +
+                                                                          ";");
+        Assert.assertEquals(kombucha.getInstanceOf("getByRecordId").toString(), "select\n" +
+                                                                                "  t.record_id\n" +
+                                                                                ", t.id\n" +
+                                                                                ", t.tea\n" +
+                                                                                ", t.mushroom\n" +
+                                                                                ", t.sugar\n" +
+                                                                                ", t.account_record_id\n" +
+                                                                                ", t.tenant_record_id\n" +
+                                                                                "from kombucha t\n" +
+                                                                                "where t.record_id = :recordId\n" +
+                                                                                "and t.tenant_record_id = :tenantRecordId\n" +
+                                                                                ";");
+        Assert.assertEquals(kombucha.getInstanceOf("getRecordId").toString(), "select\n" +
+                                                                              "  t.record_id\n" +
+                                                                              "from kombucha t\n" +
+                                                                              "where t.id = :id\n" +
+                                                                              "and t.tenant_record_id = :tenantRecordId\n" +
+                                                                              ";");
+        Assert.assertEquals(kombucha.getInstanceOf("getHistoryRecordId").toString(), "select\n" +
+                                                                                     "  max(t.record_id)\n" +
+                                                                                     "from kombucha_history t\n" +
+                                                                                     "where t.target_record_id = :targetRecordId\n" +
+                                                                                     "and t.tenant_record_id = :tenantRecordId\n" +
+                                                                                     ";");
+        Assert.assertEquals(kombucha.getInstanceOf("get").toString(), "select\n" +
+                                                                      "  t.record_id\n" +
+                                                                      ", t.id\n" +
+                                                                      ", t.tea\n" +
+                                                                      ", t.mushroom\n" +
+                                                                      ", t.sugar\n" +
+                                                                      ", t.account_record_id\n" +
+                                                                      ", t.tenant_record_id\n" +
+                                                                      "from kombucha t\n" +
+                                                                      "where t.tenant_record_id = :tenantRecordId\n" +
+                                                                      ";");
+        Assert.assertEquals(kombucha.getInstanceOf("get", ImmutableMap.<String, String>of("limit", "12")).toString(), "select\n" +
+                                                                                                                      "  t.record_id\n" +
+                                                                                                                      ", t.id\n" +
+                                                                                                                      ", t.tea\n" +
+                                                                                                                      ", t.mushroom\n" +
+                                                                                                                      ", t.sugar\n" +
+                                                                                                                      ", t.account_record_id\n" +
+                                                                                                                      ", t.tenant_record_id\n" +
+                                                                                                                      "from kombucha t\n" +
+                                                                                                                      "where t.tenant_record_id = :tenantRecordId\n" +
+                                                                                                                      "limit :limit\n" +
+                                                                                                                      ";");
+        Assert.assertEquals(kombucha.getInstanceOf("test").toString(), "select\n" +
+                                                                       "  t.record_id\n" +
+                                                                       ", t.id\n" +
+                                                                       ", t.tea\n" +
+                                                                       ", t.mushroom\n" +
+                                                                       ", t.sugar\n" +
+                                                                       ", t.account_record_id\n" +
+                                                                       ", t.tenant_record_id\n" +
+                                                                       "from kombucha t\n" +
+                                                                       "where t.tenant_record_id = :tenantRecordId\n" +
+                                                                       "limit 1\n" +
+                                                                       ";");
+        Assert.assertEquals(kombucha.getInstanceOf("addHistoryFromTransaction").toString(), "insert into kombucha_history (\n" +
+                                                                                            "  id\n" +
+                                                                                            ", target_record_id\n" +
+                                                                                            ", change_type\n" +
+                                                                                            ", tea\n" +
+                                                                                            ", mushroom\n" +
+                                                                                            ", sugar\n" +
+                                                                                            ", account_record_id\n" +
+                                                                                            ", tenant_record_id\n" +
+                                                                                            ")\n" +
+                                                                                            "values (\n" +
+                                                                                            "  :id\n" +
+                                                                                            ", :targetRecordId\n" +
+                                                                                            ", :changeType\n" +
+                                                                                            ",   :tea\n" +
+                                                                                            ", :mushroom\n" +
+                                                                                            ", :sugar\n" +
+                                                                                            ", :accountRecordId\n" +
+                                                                                            ", :tenantRecordId\n" +
+                                                                                            ")\n" +
+                                                                                            ";");
+
+        Assert.assertEquals(kombucha.getInstanceOf("insertAuditFromTransaction").toString(), "insert into audit_log (\n" +
+                                                                                             "id\n" +
+                                                                                             ", table_name\n" +
+                                                                                             ", target_record_id\n" +
+                                                                                             ", change_type\n" +
+                                                                                             ", created_by\n" +
+                                                                                             ", reason_code\n" +
+                                                                                             ", comments\n" +
+                                                                                             ", user_token\n" +
+                                                                                             ", created_date\n" +
+                                                                                             ", account_record_id\n" +
+                                                                                             ", tenant_record_id\n" +
+                                                                                             ")\n" +
+                                                                                             "values (\n" +
+                                                                                             "  :id\n" +
+                                                                                             ", :tableName\n" +
+                                                                                             ", :targetRecordId\n" +
+                                                                                             ", :changeType\n" +
+                                                                                             ", :createdBy\n" +
+                                                                                             ", :reasonCode\n" +
+                                                                                             ", :comments\n" +
+                                                                                             ", :userToken\n" +
+                                                                                             ", :createdDate\n" +
+                                                                                             ", :accountRecordId\n" +
+                                                                                             ", :tenantRecordId\n" +
+                                                                                             ")\n" +
+                                                                                             ";");
+    }
+}
diff --git a/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritanceWithJdbi.java b/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritanceWithJdbi.java
new file mode 100644
index 0000000..ed26dd4
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritanceWithJdbi.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.dao;
+
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.KillbillTestSuiteWithEmbeddedDB;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.entity.Entity;
+import com.ning.billing.util.entity.dao.EntityModelDao;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
+
+public class TestStringTemplateInheritanceWithJdbi extends KillbillTestSuiteWithEmbeddedDB {
+
+    private final Clock clock = new ClockMock();
+
+    private static interface Kombucha extends Entity {}
+
+    private static interface KombuchaModelDao extends EntityModelDao<Kombucha> {}
+
+    @EntitySqlDaoStringTemplate("/com/ning/billing/util/dao/Kombucha.sql.stg")
+    private static interface KombuchaSqlDao extends EntitySqlDao<KombuchaModelDao, Kombucha> {
+
+        @SqlQuery
+        public boolean isIsTimeForKombucha();
+    }
+
+    @Test(groups = "slow")
+    public void testInheritQueries() throws Exception {
+        final KombuchaSqlDao dao = getMysqlTestingHelper().getDBI().onDemand(KombuchaSqlDao.class);
+
+        // Verify non inherited template
+        Assert.assertEquals(dao.isIsTimeForKombucha(), clock.getUTCNow().getHourOfDay() == 17);
+
+        // Verify inherited templates
+        Assert.assertEquals(dao.get(internalCallContext).size(), 0);
+    }
+}
diff --git a/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java b/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java
index 2a59bfd..ec917f2 100644
--- a/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java
+++ b/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java
@@ -29,6 +29,8 @@ import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import com.ning.billing.util.config.NotificationConfig;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
 import com.ning.billing.util.queue.PersistentQueueEntryLifecycle.PersistentQueueEntryLifecycleState;
 
@@ -65,7 +67,7 @@ public class MockNotificationQueue extends NotificationQueueBase implements Noti
     }
 
     @Override
-    public void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao, final DateTime futureNotificationTime,
+    public void recordFutureNotificationFromTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> transactionalDao, final DateTime futureNotificationTime,
                                                         final UUID accountId, final NotificationKey notificationKey, final InternalCallContext context) throws IOException {
         recordFutureNotification(futureNotificationTime, accountId, notificationKey, context);
     }
diff --git a/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java b/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java
index ee19da5..fd7da4e 100644
--- a/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java
+++ b/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java
@@ -44,6 +44,10 @@ import com.ning.billing.util.UtilTestSuiteWithEmbeddedDB;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.io.IOUtils;
 import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
 import com.ning.billing.util.notificationq.dao.NotificationSqlDao;
@@ -64,8 +68,11 @@ import static org.testng.Assert.assertEquals;
 public class TestNotificationQueue extends UtilTestSuiteWithEmbeddedDB {
     private final Logger log = LoggerFactory.getLogger(TestNotificationQueue.class);
 
+
     private static final UUID accountId = UUID.randomUUID();
 
+    private EntitySqlDaoTransactionalJdbiWrapper entitySqlDaoTransactionalJdbiWrapper;
+
     @Inject
     private IDBI dbi;
 
@@ -103,6 +110,7 @@ public class TestNotificationQueue extends UtilTestSuiteWithEmbeddedDB {
         final String testDdl = IOUtils.toString(NotificationSqlDao.class.getResourceAsStream("/com/ning/billing/util/ddl_test.sql"));
         helper.initDb(testDdl);
         dao = dbi.onDemand(DummySqlTest.class);
+        entitySqlDaoTransactionalJdbiWrapper = new EntitySqlDaoTransactionalJdbiWrapper(dbi);
     }
 
     @BeforeTest(groups = "slow")
@@ -159,13 +167,12 @@ public class TestNotificationQueue extends UtilTestSuiteWithEmbeddedDB {
         expectedNotifications.put(notificationKey, Boolean.FALSE);
 
         // Insert dummy to be processed in 2 sec'
-        dao.inTransaction(new Transaction<Void, DummySqlTest>() {
+        entitySqlDaoTransactionalJdbiWrapper.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final DummySqlTest transactional,
-                                      final TransactionStatus status) throws Exception {
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
 
-                transactional.insertDummy(obj);
-                queue.recordFutureNotificationFromTransaction(transactional, readyTime, accountId, notificationKey, internalCallContext);
+                entitySqlDaoWrapperFactory.transmogrify(DummySqlTest.class).insertDummy(obj);
+                queue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, readyTime, accountId, notificationKey, internalCallContext);
                 log.info("Posted key: " + notificationKey);
 
                 return null;
@@ -219,18 +226,18 @@ public class TestNotificationQueue extends UtilTestSuiteWithEmbeddedDB {
             final NotificationKey notificationKey = new TestNotificationKey(key.toString());
             expectedNotifications.put(notificationKey, Boolean.FALSE);
 
-            dao.inTransaction(new Transaction<Void, DummySqlTest>() {
+            entitySqlDaoTransactionalJdbiWrapper.execute(new EntitySqlDaoTransactionWrapper<Void>() {
                 @Override
-                public Void inTransaction(final DummySqlTest transactional,
-                                          final TransactionStatus status) throws Exception {
+                public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
 
-                    transactional.insertDummy(obj);
-                    queue.recordFutureNotificationFromTransaction(transactional, now.plus((currentIteration + 1) * nextReadyTimeIncrementMs),
+                    entitySqlDaoWrapperFactory.transmogrify(DummySqlTest.class).insertDummy(obj);
+                    queue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, now.plus((currentIteration + 1) * nextReadyTimeIncrementMs),
                                                                   accountId, notificationKey, internalCallContext);
                     return null;
                 }
             });
 
+
             // Move time in the future after the notification effectiveDate
             if (i == 0) {
                 ((ClockMock) clock).setDeltaFromReality(nextReadyTimeIncrementMs);
@@ -330,17 +337,15 @@ public class TestNotificationQueue extends UtilTestSuiteWithEmbeddedDB {
         expectedNotificationsFred.put(notificationKeyBarney, Boolean.FALSE);
 
         // Insert dummy to be processed in 2 sec'
-        dao.inTransaction(new Transaction<Void, DummySqlTest>() {
+        entitySqlDaoTransactionalJdbiWrapper.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final DummySqlTest transactional,
-                                      final TransactionStatus status) throws Exception {
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                entitySqlDaoWrapperFactory.transmogrify(DummySqlTest.class).insertDummy(obj);
 
-                transactional.insertDummy(obj);
-                queueFred.recordFutureNotificationFromTransaction(transactional, readyTime, accountId, notificationKeyFred, internalCallContext);
+                queueFred.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, readyTime, accountId, notificationKeyFred, internalCallContext);
                 log.info("posted key: " + notificationKeyFred.toString());
-                queueBarney.recordFutureNotificationFromTransaction(transactional, readyTime, accountId, notificationKeyBarney, internalCallContext);
+                queueBarney.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, readyTime, accountId, notificationKeyBarney, internalCallContext);
                 log.info("posted key: " + notificationKeyBarney.toString());
-
                 return null;
             }
         });
@@ -408,14 +413,12 @@ public class TestNotificationQueue extends UtilTestSuiteWithEmbeddedDB {
 
         // add 3 events
 
-        dao.inTransaction(new Transaction<Void, DummySqlTest>() {
+        entitySqlDaoTransactionalJdbiWrapper.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final DummySqlTest transactional,
-                                      final TransactionStatus status) throws Exception {
-
-                queue.recordFutureNotificationFromTransaction(transactional, start.plus(nextReadyTimeIncrementMs), accountId, notificationKey, internalCallContext);
-                queue.recordFutureNotificationFromTransaction(transactional, start.plus(2 * nextReadyTimeIncrementMs), accountId, notificationKey, internalCallContext);
-                queue.recordFutureNotificationFromTransaction(transactional, start.plus(3 * nextReadyTimeIncrementMs), accountId, notificationKey2, internalCallContext);
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                queue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, start.plus(nextReadyTimeIncrementMs), accountId, notificationKey, internalCallContext);
+                queue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, start.plus(2 * nextReadyTimeIncrementMs), accountId, notificationKey, internalCallContext);
+                queue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, start.plus(3 * nextReadyTimeIncrementMs), accountId, notificationKey2, internalCallContext);
                 return null;
             }
         });
diff --git a/util/src/test/java/com/ning/billing/util/tag/api/user/TestTagEventBuilder.java b/util/src/test/java/com/ning/billing/util/tag/api/user/TestTagEventBuilder.java
index 7543b4c..f619b3a 100644
--- a/util/src/test/java/com/ning/billing/util/tag/api/user/TestTagEventBuilder.java
+++ b/util/src/test/java/com/ning/billing/util/tag/api/user/TestTagEventBuilder.java
@@ -35,19 +35,21 @@ import com.ning.billing.util.events.UserTagDefinitionDeletionInternalEvent;
 import com.ning.billing.util.events.UserTagDeletionInternalEvent;
 import com.ning.billing.util.tag.DefaultTagDefinition;
 import com.ning.billing.util.tag.TagDefinition;
+import com.ning.billing.util.tag.dao.TagDefinitionModelDao;
 
 public class TestTagEventBuilder extends UtilTestSuite {
+
     @Test(groups = "fast")
     public void testNewUserTagDefinitionCreationEvent() throws Exception {
         final UUID tagDefinitionId = UUID.randomUUID();
         final String tagDefinitionName = UUID.randomUUID().toString();
         final String tagDefinitionDescription = UUID.randomUUID().toString();
-        final boolean controlTag = true;
+        final boolean controlTag = false;
         final TagDefinition tagDefinition = new DefaultTagDefinition(tagDefinitionId, tagDefinitionName, tagDefinitionDescription, controlTag);
         final UUID userToken = internalCallContext.getUserToken();
 
         final TagEventBuilder tagEventBuilder = new TagEventBuilder();
-        final TagDefinitionInternalEvent event = tagEventBuilder.newUserTagDefinitionCreationEvent(tagDefinitionId, tagDefinition, internalCallContext);
+        final TagDefinitionInternalEvent event = tagEventBuilder.newUserTagDefinitionCreationEvent(tagDefinitionId, new TagDefinitionModelDao(tagDefinition), internalCallContext);
         Assert.assertTrue(event instanceof UserTagDefinitionCreationInternalEvent);
 
         Assert.assertEquals(event, new DefaultUserTagDefinitionCreationEvent(tagDefinitionId, tagDefinition, userToken, 1L, 1L));
@@ -61,12 +63,12 @@ public class TestTagEventBuilder extends UtilTestSuite {
         final UUID tagDefinitionId = UUID.randomUUID();
         final String tagDefinitionName = UUID.randomUUID().toString();
         final String tagDefinitionDescription = UUID.randomUUID().toString();
-        final boolean controlTag = true;
+        final boolean controlTag = false;
         final TagDefinition tagDefinition = new DefaultTagDefinition(tagDefinitionId, tagDefinitionName, tagDefinitionDescription, controlTag);
         final UUID userToken = internalCallContext.getUserToken();
 
         final TagEventBuilder tagEventBuilder = new TagEventBuilder();
-        final TagDefinitionInternalEvent event = tagEventBuilder.newUserTagDefinitionDeletionEvent(tagDefinitionId, tagDefinition, internalCallContext);
+        final TagDefinitionInternalEvent event = tagEventBuilder.newUserTagDefinitionDeletionEvent(tagDefinitionId, new TagDefinitionModelDao(tagDefinition), internalCallContext);
         Assert.assertTrue(event instanceof UserTagDefinitionDeletionInternalEvent);
 
         Assert.assertEquals(event, new DefaultUserTagDefinitionDeletionEvent(tagDefinitionId, tagDefinition, userToken, 1L, 1L));
@@ -85,7 +87,7 @@ public class TestTagEventBuilder extends UtilTestSuite {
         final UUID userToken = internalCallContext.getUserToken();
 
         final TagEventBuilder tagEventBuilder = new TagEventBuilder();
-        final TagDefinitionInternalEvent event = tagEventBuilder.newControlTagDefinitionCreationEvent(tagDefinitionId, tagDefinition, internalCallContext);
+        final TagDefinitionInternalEvent event = tagEventBuilder.newControlTagDefinitionCreationEvent(tagDefinitionId, new TagDefinitionModelDao(tagDefinition), internalCallContext);
         Assert.assertTrue(event instanceof ControlTagDefinitionCreationInternalEvent);
 
         Assert.assertEquals(event, new DefaultControlTagDefinitionCreationEvent(tagDefinitionId, tagDefinition, userToken, 1L, 1L));
@@ -104,7 +106,7 @@ public class TestTagEventBuilder extends UtilTestSuite {
         final UUID userToken = internalCallContext.getUserToken();
 
         final TagEventBuilder tagEventBuilder = new TagEventBuilder();
-        final TagDefinitionInternalEvent event = tagEventBuilder.newControlTagDefinitionDeletionEvent(tagDefinitionId, tagDefinition, internalCallContext);
+        final TagDefinitionInternalEvent event = tagEventBuilder.newControlTagDefinitionDeletionEvent(tagDefinitionId, new TagDefinitionModelDao(tagDefinition), internalCallContext);
         Assert.assertTrue(event instanceof ControlTagDefinitionDeletionInternalEvent);
 
         Assert.assertEquals(event, new DefaultControlTagDefinitionDeletionEvent(tagDefinitionId, tagDefinition, userToken, 1L, 1L));
@@ -121,12 +123,12 @@ public class TestTagEventBuilder extends UtilTestSuite {
         final UUID tagDefinitionId = UUID.randomUUID();
         final String tagDefinitionName = UUID.randomUUID().toString();
         final String tagDefinitionDescription = UUID.randomUUID().toString();
-        final boolean controlTag = true;
+        final boolean controlTag = false;
         final TagDefinition tagDefinition = new DefaultTagDefinition(tagDefinitionId, tagDefinitionName, tagDefinitionDescription, controlTag);
         final UUID userToken = internalCallContext.getUserToken();
 
         final TagEventBuilder tagEventBuilder = new TagEventBuilder();
-        final TagInternalEvent event = tagEventBuilder.newUserTagCreationEvent(tagId, objectId, objectType, tagDefinition, internalCallContext);
+        final TagInternalEvent event = tagEventBuilder.newUserTagCreationEvent(tagId, objectId, objectType, new TagDefinitionModelDao(tagDefinition), internalCallContext);
         Assert.assertTrue(event instanceof UserTagCreationInternalEvent);
 
         Assert.assertEquals(event, new DefaultUserTagCreationEvent(tagId, objectId, objectType, tagDefinition, userToken, 1L, 1L));
@@ -143,12 +145,12 @@ public class TestTagEventBuilder extends UtilTestSuite {
         final UUID tagDefinitionId = UUID.randomUUID();
         final String tagDefinitionName = UUID.randomUUID().toString();
         final String tagDefinitionDescription = UUID.randomUUID().toString();
-        final boolean controlTag = true;
+        final boolean controlTag = false;
         final TagDefinition tagDefinition = new DefaultTagDefinition(tagDefinitionId, tagDefinitionName, tagDefinitionDescription, controlTag);
         final UUID userToken = internalCallContext.getUserToken();
 
         final TagEventBuilder tagEventBuilder = new TagEventBuilder();
-        final TagInternalEvent event = tagEventBuilder.newUserTagDeletionEvent(tagId, objectId, objectType, tagDefinition, internalCallContext);
+        final TagInternalEvent event = tagEventBuilder.newUserTagDeletionEvent(tagId, objectId, objectType, new TagDefinitionModelDao(tagDefinition), internalCallContext);
         Assert.assertTrue(event instanceof UserTagDeletionInternalEvent);
 
         Assert.assertEquals(event, new DefaultUserTagDeletionEvent(tagId, objectId, objectType, tagDefinition, userToken, 1L, 1L));
@@ -170,7 +172,7 @@ public class TestTagEventBuilder extends UtilTestSuite {
         final UUID userToken = internalCallContext.getUserToken();
 
         final TagEventBuilder tagEventBuilder = new TagEventBuilder();
-        final TagInternalEvent event = tagEventBuilder.newControlTagCreationEvent(tagId, objectId, objectType, tagDefinition, internalCallContext);
+        final TagInternalEvent event = tagEventBuilder.newControlTagCreationEvent(tagId, objectId, objectType, new TagDefinitionModelDao(tagDefinition), internalCallContext);
         Assert.assertTrue(event instanceof ControlTagCreationInternalEvent);
 
         Assert.assertEquals(event, new DefaultControlTagCreationEvent(tagId, objectId, objectType, tagDefinition, userToken, 1L, 1L));
@@ -192,7 +194,7 @@ public class TestTagEventBuilder extends UtilTestSuite {
         final UUID userToken = internalCallContext.getUserToken();
 
         final TagEventBuilder tagEventBuilder = new TagEventBuilder();
-        final TagInternalEvent event = tagEventBuilder.newControlTagDeletionEvent(tagId, objectId, objectType, tagDefinition, internalCallContext);
+        final TagInternalEvent event = tagEventBuilder.newControlTagDeletionEvent(tagId, objectId, objectType, new TagDefinitionModelDao(tagDefinition), internalCallContext);
         Assert.assertTrue(event instanceof ControlTagDeletionInternalEvent);
 
         Assert.assertEquals(event, new DefaultControlTagDeletionEvent(tagId, objectId, objectType, tagDefinition, userToken, 1L, 1L));
diff --git a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
index db56f4b..309e20f 100644
--- a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
+++ b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
@@ -23,97 +23,58 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
-import javax.annotation.Nullable;
-
-import org.joda.time.DateTime;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-
 import com.ning.billing.ObjectType;
+import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.tag.Tag;
-
-public class MockTagDao implements TagDao {
 
-    private final Map<UUID, List<Tag>> tagStore = new HashMap<UUID, List<Tag>>();
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 
-    @Override
-    public void saveEntitiesFromTransaction(final Transmogrifier dao, final UUID objectId, final ObjectType objectType,
-                                            final List<Tag> tags, final InternalCallContext context) {
-        tagStore.put(objectId, tags);
-    }
+public class MockTagDao implements TagDao {
 
-    @Override
-    public void saveEntities(final UUID objectId, final ObjectType objectType, final List<Tag> tags, final InternalCallContext context) {
-        tagStore.put(objectId, tags);
-    }
+    private final Map<UUID, List<TagModelDao>> tagStore = new HashMap<UUID, List<TagModelDao>>();
 
     @Override
-    public Map<String, Tag> loadEntities(final UUID objectId, final ObjectType objectType, final InternalTenantContext context) {
-        return getMap(tagStore.get(objectId));
+    public void create(final TagModelDao tag, final InternalCallContext context) throws TagApiException {
+        if (tagStore.get(tag.getObjectId()) == null) {
+            tagStore.put(tag.getObjectId(), new ArrayList<TagModelDao>());
+        }
+        tagStore.get(tag.getObjectId()).add(tag);
     }
 
     @Override
-    public Map<String, Tag> loadEntitiesFromTransaction(final Transmogrifier dao, final UUID objectId, final ObjectType objectType, final InternalTenantContext context) {
-        return getMap(tagStore.get(objectId));
-    }
-
-    private Map<String, Tag> getMap(@Nullable final List<Tag> tags) {
-        final Map<String, Tag> map = new HashMap<String, Tag>();
+    public void deleteTag(final UUID objectId, final ObjectType objectType,
+                          final UUID tagDefinitionId, final InternalCallContext context) {
+        final List<TagModelDao> tags = tagStore.get(objectId);
         if (tags != null) {
-            for (final Tag tag : tags) {
-                map.put(tag.getTagDefinitionId().toString(), tag);
+            final Iterator<TagModelDao> tagIterator = tags.iterator();
+            while (tagIterator.hasNext()) {
+                final TagModelDao tag = tagIterator.next();
+                if (tag.getTagDefinitionId().equals(tagDefinitionId)) {
+                    tagIterator.remove();
+                }
             }
         }
-        return map;
     }
 
     @Override
-    public void insertTag(final UUID objectId, final ObjectType objectType,
-                          final UUID tagDefinitionId, final InternalCallContext context) {
-        final Tag tag = new Tag() {
-            private final UUID id = UUID.randomUUID();
-
-            @Override
-            public UUID getTagDefinitionId() {
-                return tagDefinitionId;
-            }
-
-            @Override
-            public UUID getId() {
-                return id;
-            }
-
-            @Override
-            public DateTime getCreatedDate() {
-                throw new UnsupportedOperationException();
-            }
-
-            @Override
-            public DateTime getUpdatedDate() {
-                throw new UnsupportedOperationException();
-            }
-        };
+    public TagModelDao getById(final UUID tagId, final InternalTenantContext context) {
+        throw new UnsupportedOperationException();
+    }
 
+    @Override
+    public List<TagModelDao> getTags(final UUID objectId, final ObjectType objectType, final InternalTenantContext internalTenantContext) {
         if (tagStore.get(objectId) == null) {
-            tagStore.put(objectId, new ArrayList<Tag>());
+            return ImmutableList.<TagModelDao>of();
         }
 
-        tagStore.get(objectId).add(tag);
-    }
-
-    @Override
-    public void deleteTag(final UUID objectId, final ObjectType objectType,
-                          final UUID tagDefinitionId, final InternalCallContext context) {
-        final List<Tag> tags = tagStore.get(objectId);
-        if (tags != null) {
-            final Iterator<Tag> tagIterator = tags.iterator();
-            while (tagIterator.hasNext()) {
-                final Tag tag = tagIterator.next();
-                if (tag.getTagDefinitionId().equals(tagDefinitionId)) {
-                    tagIterator.remove();
-                }
+        return ImmutableList.<TagModelDao>copyOf(Collections2.filter(tagStore.get(objectId), new Predicate<TagModelDao>() {
+            @Override
+            public boolean apply(final TagModelDao input) {
+                return objectType.equals(input.getObjectType());
             }
-        }
+        }));
     }
 }
diff --git a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDefinitionDao.java b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDefinitionDao.java
index 9c1c02e..f03a034 100644
--- a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDefinitionDao.java
+++ b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDefinitionDao.java
@@ -26,27 +26,25 @@ import java.util.concurrent.ConcurrentHashMap;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.tag.DefaultTagDefinition;
-import com.ning.billing.util.tag.TagDefinition;
 
 public class MockTagDefinitionDao implements TagDefinitionDao {
 
-    private final Map<String, TagDefinition> tags = new ConcurrentHashMap<String, TagDefinition>();
+    private final Map<String, TagDefinitionModelDao> tags = new ConcurrentHashMap<String, TagDefinitionModelDao>();
 
     @Override
-    public List<TagDefinition> getTagDefinitions(final InternalTenantContext context) {
-        return new ArrayList<TagDefinition>(tags.values());
+    public List<TagDefinitionModelDao> getTagDefinitions(final InternalTenantContext context) {
+        return new ArrayList<TagDefinitionModelDao>(tags.values());
     }
 
     @Override
-    public TagDefinition getByName(final String definitionName, final InternalTenantContext context) {
+    public TagDefinitionModelDao getByName(final String definitionName, final InternalTenantContext context) {
         return tags.get(definitionName);
     }
 
     @Override
-    public TagDefinition create(final String definitionName, final String description,
-                                final InternalCallContext context) throws TagDefinitionApiException {
-        final TagDefinition tag = new DefaultTagDefinition(definitionName, description, false);
+    public TagDefinitionModelDao create(final String definitionName, final String description,
+                                        final InternalCallContext context) throws TagDefinitionApiException {
+        final TagDefinitionModelDao tag = new TagDefinitionModelDao(null, definitionName, description);
 
         tags.put(tag.getId().toString(), tag);
         return tag;
@@ -58,12 +56,12 @@ public class MockTagDefinitionDao implements TagDefinitionDao {
     }
 
     @Override
-    public TagDefinition getById(final UUID definitionId, final InternalTenantContext context) {
+    public TagDefinitionModelDao getById(final UUID definitionId, final InternalTenantContext context) {
         return null;
     }
 
     @Override
-    public List<TagDefinition> getByIds(final Collection<UUID> definitionIds, final InternalTenantContext context) {
+    public List<TagDefinitionModelDao> getByIds(final Collection<UUID> definitionIds, final InternalTenantContext context) {
         return null;
     }
 }
diff --git a/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDefinitionDao.java b/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDefinitionDao.java
index d5c2f2e..b53ff95 100644
--- a/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDefinitionDao.java
+++ b/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDefinitionDao.java
@@ -29,18 +29,20 @@ import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.mock.glue.MockDbHelperModule;
 import com.ning.billing.util.UtilTestSuiteWithEmbeddedDB;
-import com.ning.billing.util.svcsapi.bus.InternalBus;
+import com.ning.billing.util.bus.InMemoryBusModule;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.events.BusInternalEvent;
 import com.ning.billing.util.events.TagDefinitionInternalEvent;
-import com.ning.billing.util.tag.MockTagStoreModuleSql;
-import com.ning.billing.util.tag.TagDefinition;
+import com.ning.billing.util.glue.ClockModule;
+import com.ning.billing.util.glue.TagStoreModule;
+import com.ning.billing.util.svcsapi.bus.InternalBus;
 
 import com.google.common.eventbus.Subscribe;
 import com.google.inject.Inject;
 
-@Guice(modules = MockTagStoreModuleSql.class)
+@Guice(modules = {TagStoreModule.class, ClockModule.class, InMemoryBusModule.class, MockDbHelperModule.class})
 public class TestDefaultTagDefinitionDao extends UtilTestSuiteWithEmbeddedDB {
 
     @Inject
@@ -83,12 +85,12 @@ public class TestDefaultTagDefinitionDao extends UtilTestSuiteWithEmbeddedDB {
         Assert.assertEquals(eventsListener.getTagDefinitionEvents().size(), 0);
 
         // Make sure we can create a tag definition
-        final TagDefinition createdTagDefinition = tagDefinitionDao.create(definitionName, description, internalCallContext);
+        final TagDefinitionModelDao createdTagDefinition = tagDefinitionDao.create(definitionName, description, internalCallContext);
         Assert.assertEquals(createdTagDefinition.getName(), definitionName);
         Assert.assertEquals(createdTagDefinition.getDescription(), description);
 
         // Make sure we can retrieve it via the DAO
-        final TagDefinition foundTagDefinition = tagDefinitionDao.getByName(definitionName, internalCallContext);
+        final TagDefinitionModelDao foundTagDefinition = tagDefinitionDao.getByName(definitionName, internalCallContext);
         Assert.assertEquals(foundTagDefinition, createdTagDefinition);
 
         // Verify we caught an event on the bus
@@ -97,7 +99,8 @@ public class TestDefaultTagDefinitionDao extends UtilTestSuiteWithEmbeddedDB {
         final TagDefinitionInternalEvent tagDefinitionFirstEventReceived = eventsListener.getTagDefinitionEvents().get(0);
         Assert.assertEquals(eventsListener.getEvents().get(0), tagDefinitionFirstEventReceived);
         Assert.assertEquals(tagDefinitionFirstEventReceived.getTagDefinitionId(), createdTagDefinition.getId());
-        Assert.assertEquals(tagDefinitionFirstEventReceived.getTagDefinition(), createdTagDefinition);
+        Assert.assertEquals(tagDefinitionFirstEventReceived.getTagDefinition().getName(), createdTagDefinition.getName());
+        Assert.assertEquals(tagDefinitionFirstEventReceived.getTagDefinition().getDescription(), createdTagDefinition.getDescription());
         Assert.assertEquals(tagDefinitionFirstEventReceived.getBusEventType(), BusInternalEvent.BusInternalEventType.USER_TAGDEFINITION_CREATION);
         Assert.assertEquals(tagDefinitionFirstEventReceived.getUserToken(), internalCallContext.getUserToken());
 
@@ -113,7 +116,8 @@ public class TestDefaultTagDefinitionDao extends UtilTestSuiteWithEmbeddedDB {
         final TagDefinitionInternalEvent tagDefinitionSecondEventReceived = eventsListener.getTagDefinitionEvents().get(1);
         Assert.assertEquals(eventsListener.getEvents().get(1), tagDefinitionSecondEventReceived);
         Assert.assertEquals(tagDefinitionSecondEventReceived.getTagDefinitionId(), createdTagDefinition.getId());
-        Assert.assertEquals(tagDefinitionSecondEventReceived.getTagDefinition(), createdTagDefinition);
+        Assert.assertEquals(tagDefinitionSecondEventReceived.getTagDefinition().getName(), createdTagDefinition.getName());
+        Assert.assertEquals(tagDefinitionSecondEventReceived.getTagDefinition().getDescription(), createdTagDefinition.getDescription());
         Assert.assertEquals(tagDefinitionSecondEventReceived.getBusEventType(), BusInternalEvent.BusInternalEventType.USER_TAGDEFINITION_DELETION);
         Assert.assertEquals(tagDefinitionSecondEventReceived.getUserToken(), internalCallContext.getUserToken());
     }
diff --git a/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java b/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java
index 00f9931..13691d1 100644
--- a/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java
+++ b/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java
@@ -17,31 +17,33 @@
 package com.ning.billing.util.tag;
 
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.joda.time.Seconds;
-import org.skife.jdbi.v2.Handle;
 import org.skife.jdbi.v2.IDBI;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.ning.billing.ObjectType;
 import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.mock.glue.MockDbHelperModule;
 import com.ning.billing.util.UtilTestSuiteWithEmbeddedDB;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
-import com.ning.billing.util.svcsapi.bus.InternalBus;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.glue.BusModule;
+import com.ning.billing.util.glue.ClockModule;
+import com.ning.billing.util.glue.TagStoreModule;
+import com.ning.billing.util.svcsapi.bus.InternalBus;
 import com.ning.billing.util.tag.dao.TagDao;
 import com.ning.billing.util.tag.dao.TagDefinitionDao;
+import com.ning.billing.util.tag.dao.TagDefinitionModelDao;
+import com.ning.billing.util.tag.dao.TagModelDao;
 
 import com.google.inject.Inject;
 
@@ -52,7 +54,7 @@ import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
 @Test(groups = {"slow"})
-@Guice(modules = MockTagStoreModuleSql.class)
+@Guice(modules = {TagStoreModule.class, ClockModule.class, BusModule.class, MockDbHelperModule.class})
 public class TestTagStore extends UtilTestSuiteWithEmbeddedDB {
 
     @Inject
@@ -73,7 +75,7 @@ public class TestTagStore extends UtilTestSuiteWithEmbeddedDB {
     @Inject
     private InternalBus bus;
 
-    private TagDefinition testTag;
+    private TagDefinitionModelDao testTagDefinition;
 
     private final Logger log = LoggerFactory.getLogger(TestTagStore.class);
 
@@ -82,142 +84,52 @@ public class TestTagStore extends UtilTestSuiteWithEmbeddedDB {
         try {
             bus.start();
 
-            tagDefinitionDao.create("tag1", "First tag", internalCallContext);
-            testTag = tagDefinitionDao.create("testTag", "Second tag", internalCallContext);
         } catch (Throwable t) {
             log.error("Failed to start tag store tests", t);
             fail(t.toString());
         }
     }
 
+    // We need tag definitions before we start the tests
+    @Override
+    @BeforeMethod(groups = "slow")
+    public void cleanupTablesBetweenMethods() {
+        super.cleanupTablesBetweenMethods();
+        try {
+            tagDefinitionDao.create("tag1", "First tag", internalCallContext);
+            testTagDefinition = tagDefinitionDao.create("testTagDefinition", "Second tag", internalCallContext);
+        } catch (TagDefinitionApiException e) {
+            fail(e.toString());
+        }
+    }
+
     @AfterClass(groups = "slow")
     public void tearDown() {
         bus.stop();
     }
 
     @Test(groups = "slow")
-    public void testTagCreationAndRetrieval() {
+    public void testTagCreationAndRetrieval() throws TagApiException {
         final UUID accountId = UUID.randomUUID();
+        final Tag tag = new DescriptiveTag(testTagDefinition.getId(), ObjectType.ACCOUNT, accountId, clock.getUTCNow());
 
-        final TagStore tagStore = new DefaultTagStore(accountId, ObjectType.ACCOUNT);
-        final Tag tag = new DescriptiveTag(testTag.getId());
-        tagStore.add(tag);
-
-        tagDao.saveEntities(accountId, ObjectType.ACCOUNT, tagStore.getEntityList(), internalCallContext);
-
-        final Map<String, Tag> savedTags = tagDao.loadEntities(accountId, ObjectType.ACCOUNT, internalCallContext);
-        assertEquals(savedTags.size(), 1);
+        tagDao.create(new TagModelDao(tag), internalCallContext);
 
-        final Tag savedTag = savedTags.get(tag.getId().toString());
+        final TagModelDao savedTag = tagDao.getById(tag.getId(), internalCallContext);
         assertEquals(savedTag.getTagDefinitionId(), tag.getTagDefinitionId());
         assertEquals(savedTag.getId(), tag.getId());
     }
 
     @Test(groups = "slow")
-    public void testControlTagCreation() {
+    public void testControlTagCreation() throws TagApiException {
         final UUID accountId = UUID.randomUUID();
-        final TagStore tagStore = new DefaultTagStore(accountId, ObjectType.ACCOUNT);
-
-        final ControlTag tag = new DefaultControlTag(ControlTagType.AUTO_INVOICING_OFF);
-        tagStore.add(tag);
-        assertEquals(tagStore.generateInvoice(), false);
-
-        final List<Tag> tagList = tagStore.getEntityList();
-        tagDao.saveEntities(accountId, ObjectType.ACCOUNT, tagList, internalCallContext);
-
-        tagStore.clear();
-        assertEquals(tagStore.getEntityList().size(), 0);
-
-        final Map<String, Tag> tagMap = tagDao.loadEntities(accountId, ObjectType.ACCOUNT, internalCallContext);
-        assertEquals(tagMap.size(), 1);
-
-        assertEquals(tagMap.values().iterator().next().getTagDefinitionId(), ControlTagType.AUTO_INVOICING_OFF.getId());
-    }
-
-    @Test(groups = "slow")
-    public void testDescriptiveTagCreation() {
-        final UUID accountId = UUID.randomUUID();
-        final TagStore tagStore = new DefaultTagStore(accountId, ObjectType.ACCOUNT);
-
-        final String definitionName = "SomeTestTag";
-        TagDefinition tagDefinition = null;
-        try {
-            tagDefinition = tagDefinitionDao.create(definitionName, "Test tag for some test purpose", internalCallContext);
-        } catch (TagDefinitionApiException e) {
-            fail("Tag definition creation failed.", e);
-        }
-
-        final DescriptiveTag tag = new DescriptiveTag(tagDefinition.getId());
-        tagStore.add(tag);
-        assertEquals(tagStore.generateInvoice(), true);
-
-        tagDao.saveEntities(accountId, ObjectType.ACCOUNT, tagStore.getEntityList(), internalCallContext);
-
-        tagStore.clear();
-        assertEquals(tagStore.getEntityList().size(), 0);
-
-        final Map<String, Tag> tagMap = tagDao.loadEntities(accountId, ObjectType.ACCOUNT, internalCallContext);
-        assertEquals(tagMap.size(), 1);
-
-        assertEquals(tagMap.values().iterator().next().getTagDefinitionId(), tagDefinition.getId());
-    }
-
-    @Test(groups = "slow")
-    public void testMixedTagCreation() {
-        final UUID accountId = UUID.randomUUID();
-        final TagStore tagStore = new DefaultTagStore(accountId, ObjectType.ACCOUNT);
-
-        final String definitionName = "MixedTagTest";
-        TagDefinition tagDefinition = null;
-        try {
-            tagDefinition = tagDefinitionDao.create(definitionName, "Test tag for some test purpose", internalCallContext);
-        } catch (TagDefinitionApiException e) {
-            fail("Tag definition creation failed.", e);
-        }
-
-        final DescriptiveTag descriptiveTag = new DescriptiveTag(tagDefinition.getId());
-        tagStore.add(descriptiveTag);
-        assertEquals(tagStore.generateInvoice(), true);
-
-        final ControlTag controlTag = new DefaultControlTag(ControlTagType.AUTO_INVOICING_OFF);
-        tagStore.add(controlTag);
-        assertEquals(tagStore.generateInvoice(), false);
-
-        tagDao.saveEntities(accountId, ObjectType.ACCOUNT, tagStore.getEntityList(), internalCallContext);
-
-        tagStore.clear();
-        assertEquals(tagStore.getEntityList().size(), 0);
-
-        final Map<String, Tag> tagMap = tagDao.loadEntities(accountId, ObjectType.ACCOUNT, internalCallContext);
-        assertEquals(tagMap.size(), 2);
-
-        boolean found_AUTO_INVOICING_OFF_tag = false;
-        for (Tag cur : tagMap.values()) {
-            if (cur.getTagDefinitionId().equals(ControlTagType.AUTO_INVOICING_OFF.getId())) {
-                found_AUTO_INVOICING_OFF_tag = true;
-                break;
-            }
-        }
-        assertEquals(found_AUTO_INVOICING_OFF_tag, true);
 
-    }
+        final ControlTag tag = new DefaultControlTag(ControlTagType.AUTO_INVOICING_OFF, ObjectType.ACCOUNT, accountId, clock.getUTCNow());
+        tagDao.create(new TagModelDao(tag), internalCallContext);
 
-    @Test(groups = "slow")
-    public void testControlTags() {
-        final UUID accountId = UUID.randomUUID();
-        final TagStore tagStore = new DefaultTagStore(accountId, ObjectType.ACCOUNT);
-        assertEquals(tagStore.generateInvoice(), true);
-        assertEquals(tagStore.processPayment(), true);
-
-        final ControlTag invoiceTag = new DefaultControlTag(ControlTagType.AUTO_INVOICING_OFF);
-        tagStore.add(invoiceTag);
-        assertEquals(tagStore.generateInvoice(), false);
-        assertEquals(tagStore.processPayment(), true);
-
-        final ControlTag paymentTag = new DefaultControlTag(ControlTagType.AUTO_PAY_OFF);
-        tagStore.add(paymentTag);
-        assertEquals(tagStore.generateInvoice(), false);
-        assertEquals(tagStore.processPayment(), false);
+        final TagModelDao savedTag = tagDao.getById(tag.getId(), internalCallContext);
+        assertEquals(savedTag.getTagDefinitionId(), tag.getTagDefinitionId());
+        assertEquals(savedTag.getId(), tag.getId());
     }
 
     @Test(groups = "slow", expectedExceptions = TagDefinitionApiException.class)
@@ -231,7 +143,7 @@ public class TestTagStore extends UtilTestSuiteWithEmbeddedDB {
         final String definitionName = "TestTag1234";
         tagDefinitionDao.create(definitionName, "Some test tag", internalCallContext);
 
-        TagDefinition tagDefinition = tagDefinitionDao.getByName(definitionName, internalCallContext);
+        TagDefinitionModelDao tagDefinition = tagDefinitionDao.getByName(definitionName, internalCallContext);
         assertNotNull(tagDefinition);
 
         tagDefinitionDao.deleteById(tagDefinition.getId(), internalCallContext);
@@ -240,22 +152,16 @@ public class TestTagStore extends UtilTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow", expectedExceptions = TagDefinitionApiException.class)
-    public void testTagDefinitionDeletionForDefinitionInUse() throws TagDefinitionApiException {
+    public void testTagDefinitionDeletionForDefinitionInUse() throws TagDefinitionApiException, TagApiException {
         final String definitionName = "TestTag12345";
         tagDefinitionDao.create(definitionName, "Some test tag", internalCallContext);
 
-        final TagDefinition tagDefinition = tagDefinitionDao.getByName(definitionName, internalCallContext);
+        final TagDefinitionModelDao tagDefinition = tagDefinitionDao.getByName(definitionName, internalCallContext);
         assertNotNull(tagDefinition);
 
         final UUID objectId = UUID.randomUUID();
-        final TagStore tagStore = new DefaultTagStore(objectId, ObjectType.ACCOUNT);
-        final Tag tag = new DescriptiveTag(tagDefinition.getId());
-        tagStore.add(tag);
-
-        tagDao.saveEntities(objectId, ObjectType.ACCOUNT, tagStore.getEntityList(), internalCallContext);
-
-        final Map<String, Tag> tagMap = tagDao.loadEntities(objectId, ObjectType.ACCOUNT, internalCallContext);
-        assertEquals(tagMap.size(), 1);
+        final Tag tag = new DescriptiveTag(tagDefinition.getId(), ObjectType.ACCOUNT, objectId, internalCallContext.getCreatedDate());
+        tagDao.create(new TagModelDao(tag), internalCallContext);
 
         tagDefinitionDao.deleteById(tagDefinition.getId(), internalCallContext);
     }
@@ -269,22 +175,14 @@ public class TestTagStore extends UtilTestSuiteWithEmbeddedDB {
             fail("Could not create tag definition", e);
         }
 
-        final TagDefinition tagDefinition = tagDefinitionDao.getByName(definitionName, internalCallContext);
+        final TagDefinitionModelDao tagDefinition = tagDefinitionDao.getByName(definitionName, internalCallContext);
         assertNotNull(tagDefinition);
 
         final UUID objectId = UUID.randomUUID();
-        final TagStore tagStore = new DefaultTagStore(objectId, ObjectType.ACCOUNT);
-        final Tag tag = new DescriptiveTag(tagDefinition.getId());
-        tagStore.add(tag);
-
-        tagDao.saveEntities(objectId, ObjectType.ACCOUNT, tagStore.getEntityList(), internalCallContext);
-
-        final Map<String, Tag> tagMap = tagDao.loadEntities(objectId, ObjectType.ACCOUNT, internalCallContext);
-        assertEquals(tagMap.size(), 1);
 
+        final Tag tag = new DescriptiveTag(tagDefinition.getId(), ObjectType.ACCOUNT, objectId, internalCallContext.getCreatedDate());
+        tagDao.create(new TagModelDao(tag), internalCallContext);
         tagDao.deleteTag(objectId, ObjectType.ACCOUNT, tagDefinition.getId(), internalCallContext);
-        final Map<String, Tag> tagMapAfterDeletion = tagDao.loadEntities(objectId, ObjectType.ACCOUNT, internalCallContext);
-        assertEquals(tagMapAfterDeletion.size(), 0);
 
         try {
             tagDefinitionDao.deleteById(tagDefinition.getId(), internalCallContext);
@@ -295,116 +193,7 @@ public class TestTagStore extends UtilTestSuiteWithEmbeddedDB {
 
     @Test(groups = "slow")
     public void testGetTagDefinitions() {
-        final List<TagDefinition> definitionList = tagDefinitionDao.getTagDefinitions(internalCallContext);
+        final List<TagDefinitionModelDao> definitionList = tagDefinitionDao.getTagDefinitions(internalCallContext);
         assertTrue(definitionList.size() >= ControlTagType.values().length);
     }
-
-    @Test
-    public void testTagInsertAudit() {
-        final UUID accountId = UUID.randomUUID();
-
-        final TagStore tagStore = new DefaultTagStore(accountId, ObjectType.ACCOUNT);
-        final Tag tag = new DescriptiveTag(testTag.getId());
-        tagStore.add(tag);
-
-        tagDao.saveEntities(accountId, ObjectType.ACCOUNT, tagStore.getEntityList(), internalCallContext);
-
-        final Map<String, Tag> savedTags = tagDao.loadEntities(accountId, ObjectType.ACCOUNT, internalCallContext);
-        assertEquals(savedTags.size(), 1);
-
-        final Tag savedTag = savedTags.get(tag.getId().toString());
-        assertEquals(savedTag.getTagDefinitionId(), tag.getTagDefinitionId());
-        assertEquals(savedTag.getId(), tag.getId());
-
-        final Handle handle = dbi.open();
-        final String query = String.format("select * from audit_log a inner join tag_history th on a.record_id = th.history_record_id where a.table_name = 'tag_history' and th.id='%s' and a.change_type='INSERT'",
-                                           tag.getId().toString());
-        final List<Map<String, Object>> result = handle.select(query);
-        handle.close();
-
-        assertNotNull(result);
-        assertEquals(result.size(), 1);
-        assertEquals(result.get(0).get("change_type"), "INSERT");
-        assertNotNull(result.get(0).get("change_date"));
-        final DateTime changeDate = new DateTime(result.get(0).get("change_date"));
-        assertTrue(Seconds.secondsBetween(changeDate, internalCallContext.getCreatedDate()).getSeconds() < 2);
-        assertEquals(result.get(0).get("changed_by"), internalCallContext.getUserName());
-    }
-
-    @Test
-    public void testTagDeleteAudit() {
-        final UUID accountId = UUID.randomUUID();
-
-        final TagStore tagStore = new DefaultTagStore(accountId, ObjectType.ACCOUNT);
-        final Tag tag = new DescriptiveTag(testTag.getId());
-        tagStore.add(tag);
-
-        tagDao.saveEntities(accountId, ObjectType.ACCOUNT, tagStore.getEntityList(), internalCallContext);
-
-        tagStore.remove(tag);
-        tagDao.saveEntities(accountId, ObjectType.ACCOUNT, tagStore.getEntityList(), internalCallContext);
-
-        final Map<String, Tag> savedTags = tagDao.loadEntities(accountId, ObjectType.ACCOUNT, internalCallContext);
-        assertEquals(savedTags.size(), 0);
-
-        final Handle handle = dbi.open();
-        final String query = String.format("select * from audit_log a inner join tag_history th on a.record_id = th.history_record_id where a.table_name = 'tag_history' and th.id='%s' and a.change_type='DELETE'",
-                                           tag.getId().toString());
-        final List<Map<String, Object>> result = handle.select(query);
-        handle.close();
-
-        assertNotNull(result);
-        assertEquals(result.size(), 1);
-        assertNotNull(result.get(0).get("change_date"));
-        final DateTime changeDate = new DateTime(result.get(0).get("change_date"));
-        assertTrue(Seconds.secondsBetween(changeDate, internalCallContext.getUpdatedDate()).getSeconds() < 2);
-        assertEquals(result.get(0).get("changed_by"), internalCallContext.getUserName());
-    }
-
-    @Test
-    public void testAddTag() throws TagApiException, TagDefinitionApiException {
-        final UUID objectId = UUID.randomUUID();
-        final ObjectType objectType = ObjectType.INVOICE;
-        final TagDefinition tagDefinition = tagDefinitionDao.create("test tag", "test", internalCallContext);
-        tagDao.insertTag(objectId, objectType, tagDefinition.getId(), internalCallContext);
-        final Map<String, Tag> savedTags = tagDao.loadEntities(objectId, objectType, internalCallContext);
-        assertEquals(savedTags.size(), 1);
-    }
-
-    @Test
-    public void testRemoveTag() throws TagApiException, TagDefinitionApiException {
-        final UUID objectId = UUID.randomUUID();
-        final ObjectType objectType = ObjectType.INVOICE;
-        final TagDefinition tagDefinition = tagDefinitionDao.create("test tag", "test", internalCallContext);
-        tagDao.insertTag(objectId, objectType, tagDefinition.getId(), internalCallContext);
-        Map<String, Tag> savedTags = tagDao.loadEntities(objectId, objectType, internalCallContext);
-        assertEquals(savedTags.size(), 1);
-
-        tagDao.deleteTag(objectId, objectType, tagDefinition.getId(), internalCallContext);
-        savedTags = tagDao.loadEntities(objectId, objectType, internalCallContext);
-        assertEquals(savedTags.size(), 0);
-    }
-
-    @Test
-    public void testSetTags() {
-        final UUID objectId = UUID.randomUUID();
-        final ObjectType objectType = ObjectType.INVOICE;
-
-        final List<Tag> tags = new ArrayList<Tag>();
-        tags.add(new DescriptiveTag(UUID.randomUUID()));
-        tags.add(new DescriptiveTag(UUID.randomUUID()));
-        tags.add(new DefaultControlTag(ControlTagType.AUTO_INVOICING_OFF));
-        tagDao.saveEntities(objectId, objectType, tags, internalCallContext);
-
-        Map<String, Tag> savedTags = tagDao.loadEntities(objectId, objectType, internalCallContext);
-        assertEquals(savedTags.size(), 3);
-
-        tags.remove(1);
-        assertEquals(tags.size(), 2);
-
-        tagDao.saveEntities(objectId, objectType, tags, internalCallContext);
-
-        savedTags = tagDao.loadEntities(objectId, objectType, internalCallContext);
-        assertEquals(savedTags.size(), 2);
-    }
 }
diff --git a/util/src/test/resources/com/ning/billing/util/dao/Kombucha.sql.stg b/util/src/test/resources/com/ning/billing/util/dao/Kombucha.sql.stg
new file mode 100644
index 0000000..4fe6ff0
--- /dev/null
+++ b/util/src/test/resources/com/ning/billing/util/dao/Kombucha.sql.stg
@@ -0,0 +1,21 @@
+group Kombucha: EntitySqlDao;
+
+tableName() ::= "kombucha"
+
+historyTableName() ::= "kombucha_history"
+
+tableFields(prefix) ::= <<
+  <prefix>tea
+, <prefix>mushroom
+, <prefix>sugar
+>>
+
+tableValues() ::= <<
+  :tea
+, :mushroom
+, :sugar
+>>
+
+isIsTimeForKombucha() ::= <<
+select hour(current_timestamp()) = 17 as is_time;
+>>
\ No newline at end of file
diff --git a/util/src/test/resources/com/ning/billing/util/ddl_test.sql b/util/src/test/resources/com/ning/billing/util/ddl_test.sql
index 1322a02..e219d76 100644
--- a/util/src/test/resources/com/ning/billing/util/ddl_test.sql
+++ b/util/src/test/resources/com/ning/billing/util/ddl_test.sql
@@ -19,3 +19,15 @@ CREATE TABLE validation_test (
     column3 numeric(10,4),
     column4 datetime
 ) ENGINE = innodb;
+
+DROP TABLE IF EXISTS kombucha;
+CREATE TABLE kombucha (
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    id char(36) NOT NULL,
+    tea varchar(50) NOT NULL,
+    mushroom varchar(50) NOT NULL,
+    sugar varchar(50) NOT NULL,
+    account_record_id int(11) unsigned default null,
+    tenant_record_id int(11) unsigned default null,
+    PRIMARY KEY(record_id)
+) ENGINE = innodb;