killbill-uncached

auditing rework

3/20/2012 1:33:19 PM

Changes

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 2b916bf..febcd6f 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
@@ -19,6 +19,8 @@ package com.ning.billing.account.api;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.customfield.CustomField;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 
@@ -50,90 +52,73 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
 	private final String country;
 	private final String postalCode;
 	private final String phone;
-	private final DateTime createdDate;
-	private final DateTime updatedDate;
+    private final String updatedBy;
+    private final DateTime updatedDate;
 
-	/**
-	 * This call is used to create a new account
-	 * @param data
-	 * @param createdDate
-	 */
-	public DefaultAccount(final AccountData data, DateTime createdDate) {
-		this(UUID.randomUUID(), data.getExternalKey(), data.getEmail(), data.getName(), data.getFirstNameLength(),
-				data.getCurrency(), data.getBillCycleDay(), data.getPaymentProviderName(),
-				data.getTimeZone(), data.getLocale(),
-				data.getAddress1(), data.getAddress2(), data.getCompanyName(),
-				data.getCity(), data.getStateOrProvince(), data.getCountry(),
-				data.getPostalCode(), data.getPhone(), createdDate, createdDate);
+	//intended for creation and migration
+	public DefaultAccount(final String createdBy, final DateTime createdDate,
+                          final String updatedBy, final DateTime updatedDate,
+                          final AccountData data) {
+		this(UUID.randomUUID(), createdBy, createdDate, updatedBy, updatedDate, data);
 	}
 
-	//intended for creation
-	public DefaultAccount(final AccountData data) {
-		this(UUID.randomUUID(), data, null, null);
-	}
-	
-	// Intended for migration
-	public DefaultAccount(final AccountData data, DateTime createdDate, DateTime updatedDate) {
-		this(UUID.randomUUID(), data, createdDate, updatedDate);
+    public DefaultAccount(final AccountData data) {
+		this(UUID.randomUUID(), null, null, null, null, data);
 	}
 
-	//intended for update
-	public DefaultAccount(final UUID id, final AccountData data, DateTime createdDate, DateTime updatedDate) {
-		this(id, data.getExternalKey(), data.getEmail(), data.getName(), data.getFirstNameLength(),
-				data.getCurrency(), data.getBillCycleDay(), data.getPaymentProviderName(),
-				data.getTimeZone(), data.getLocale(),
-				data.getAddress1(), data.getAddress2(), data.getCompanyName(),
-				data.getCity(), data.getStateOrProvince(), data.getCountry(),
-				data.getPostalCode(), data.getPhone(), createdDate, updatedDate);
+    public DefaultAccount(final UUID id, final AccountData data) {
+		this(id, null, null, null, null, data);
 	}
 
-	
 	/**
 	 * This call is used to update an existing account
 	 *  
-	 * @param id
-	 * @param data
+	 * @param id UUID id of the existing account to update
+	 * @param data AccountData new data for the existing account
 	 */
-	public DefaultAccount(final UUID id, final AccountData data) {
+	public DefaultAccount(final UUID id, final String createdBy, final DateTime createdDate,
+                          final String updatedBy, final DateTime updatedDate, final AccountData data) {
 		this(id, data.getExternalKey(), data.getEmail(), data.getName(), data.getFirstNameLength(),
 				data.getCurrency(), data.getBillCycleDay(), data.getPaymentProviderName(),
 				data.getTimeZone(), data.getLocale(),
 				data.getAddress1(), data.getAddress2(), data.getCompanyName(),
 				data.getCity(), data.getStateOrProvince(), data.getCountry(),
-				data.getPostalCode(), data.getPhone(), null, null);
+				data.getPostalCode(), data.getPhone(), createdBy, createdDate,
+                updatedBy, updatedDate);
 	}
 
 	/**
 	 * This call is used for testing 
-	 * @param id
-	 * @param externalKey
-	 * @param email
-	 * @param name
-	 * @param firstNameLength
-	 * @param currency
-	 * @param billCycleDay
-	 * @param paymentProviderName
-	 * @param timeZone
-	 * @param locale
-	 * @param address1
-	 * @param address2
-	 * @param companyName
-	 * @param city
-	 * @param stateOrProvince
-	 * @param country
-	 * @param postalCode
-	 * @param phone
-	 * @param createdDate
-	 * @param updatedDate
+	 * @param id UUID system-generated account id
+	 * @param externalKey String key for external systems
+	 * @param email String account owner's e-mail address
+	 * @param name String account owner's name
+	 * @param firstNameLength String the length of the account owner's first name
+	 * @param currency Currency the currency for billing for the account
+	 * @param billCycleDay int the day of the month upon which invoices should be generated for this account
+	 * @param paymentProviderName String payment provider name
+	 * @param timeZone String the name of the time zone to be used for invoice generation
+	 * @param locale String the locale for internationalization
+	 * @param address1 String address information for the account owner
+	 * @param address2 String (optional) more address information for the account owner
+	 * @param companyName String (optional) the company of the account owner
+	 * @param city String the city of the account owner
+	 * @param stateOrProvince String the state or province of the account owner
+	 * @param country String the country of the account owner
+	 * @param postalCode String the postal code of the account owner
+	 * @param phone String the phone number of the account owner
 	 */
-	public DefaultAccount(final UUID id, final String externalKey, final String email, final String name, final int firstNameLength,
-			final Currency currency, final int billCycleDay, final String paymentProviderName,
-			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, DateTime createdDate, DateTime updatedDate) {
-
-		super(id);
+	public DefaultAccount(final UUID id, final String externalKey, final String email,
+                          final String name, final int firstNameLength,
+                          final Currency currency, final int billCycleDay, final String paymentProviderName,
+                          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 String createdBy, final DateTime createdDate,
+                          final String updatedBy, final DateTime updatedDate) {
+
+		super(id, createdBy, createdDate);
 		this.externalKey = externalKey;
 		this.email = email;
 		this.name = name;
@@ -151,12 +136,27 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
 		this.postalCode = postalCode;
 		this.country = country;
 		this.phone = phone;
-		this.createdDate = createdDate == null ? new DateTime(DateTimeZone.UTC) : createdDate; // This is a fallback, we are only expecting these to be set to null 
-		this.updatedDate = updatedDate == null ? new DateTime(DateTimeZone.UTC) : updatedDate; // in the case that the account is being updated. In which case the values are ignored anyway
 		this.tags = new DefaultTagStore(id, getObjectName());
+        this.updatedBy = updatedBy;
+        this.updatedDate = updatedDate;
 	}
 
-	@Override
+    @Override
+    public void saveFieldValue(String fieldName, String fieldValue, CallContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void saveFields(List<CustomField> fields, CallContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void clearPersistedFields(CallContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
 	public String getObjectName() {
 		return "Account";
 	}
@@ -176,20 +176,6 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
 		return email;
 	}
 
-	public DefaultTagStore getTags() {
-		return tags;
-	}
-
-	@Override
-	public DateTime getCreatedDate() {
-		return createdDate;
-	}
-
-	@Override
-	public DateTime getUpdatedDate() {
-		return updatedDate;
-	}
-
 	@Override
 	public int getFirstNameLength() {
 		return firstNameLength;
@@ -255,7 +241,17 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
 		return country;
 	}
 
-	@Override
+    @Override
+    public String getUpdatedBy() {
+        return updatedBy;
+    }
+
+    @Override
+    public DateTime getUpdatedDate() {
+        return updatedDate;
+    }
+
+    @Override
 	public String getPhone() {
 		return phone;
 	}
@@ -271,8 +267,8 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
 	}
 
 	@Override
-	public void addTag(TagDefinition definition, String addedBy, DateTime dateAdded) {
-		Tag tag = new DescriptiveTag(definition, addedBy, dateAdded);
+	public void addTag(TagDefinition definition) {
+		Tag tag = new DescriptiveTag(definition);
 		tags.add(tag) ;
 	}
 
@@ -305,24 +301,24 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
 
 	@Override
 	public String toString() {
-		return "DefaultAccount [externalKey=" + externalKey + ", email=" + email + 
-				", name=" + name + ", " +
-				"firstNameLength=" + firstNameLength + 
-				", phone=" + phone + ", " +
-				"currency=" + currency + 
-				", billCycleDay=" + billCycleDay + 
-				", paymentProviderName=" + paymentProviderName + 
+		return "DefaultAccount [externalKey=" + externalKey +
+                ", email=" + email +
+				", name=" + name +
+				", firstNameLength=" + firstNameLength +
+				", phone=" + phone +
+				", currency=" + currency +
+				", billCycleDay=" + billCycleDay +
+				", paymentProviderName=" + paymentProviderName +
 				", timezone=" + timeZone +
 				", locale=" +  locale +
-				", address1" + address1 +
-				", address2" + address2 +
-				", companyName" + companyName +
-				", city" + city +
-				", stateOrProvince" + stateOrProvince +
-				", postalCode" + postalCode +
-				", country" +
-				", tags=" + tags + 
-				", createdDate=" + createdDate + 
-				", updatedDate=" + updatedDate + "]";
+				", address1=" + address1 +
+				", address2=" + address2 +
+				", companyName=" + companyName +
+				", city=" + city +
+				", stateOrProvince=" + stateOrProvince +
+				", postalCode=" + postalCode +
+				", country=" + country +
+				", tags=" + tags +
+                "]";
 	}
 }
\ No newline at end of file
diff --git a/account/src/main/java/com/ning/billing/account/api/user/AccountBuilder.java b/account/src/main/java/com/ning/billing/account/api/user/AccountBuilder.java
index 95eb5d8..ed18ffb 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/AccountBuilder.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/AccountBuilder.java
@@ -43,7 +43,9 @@ public class AccountBuilder {
     private String country;
     private String postalCode;
     private String phone;
+    private String createdBy;
     private DateTime createdDate;
+    private String updatedBy;
     private DateTime updatedDate;
 
     public AccountBuilder() {
@@ -139,12 +141,22 @@ public class AccountBuilder {
         return this;
     }
 
-    public AccountBuilder createdDate(DateTime createdDate) {
+    public AccountBuilder createdBy(final String createdBy) {
+        this.createdBy = createdBy;
+        return this;
+    }
+
+    public AccountBuilder createdDate(final DateTime createdDate) {
         this.createdDate = createdDate;
         return this;
     }
 
-    public AccountBuilder updatedDate(DateTime updatedDate) {
+    public AccountBuilder updatedBy(final String updatedBy) {
+        this.updatedBy = updatedBy;
+        return this;
+    }
+
+    public AccountBuilder updatedDate(final DateTime updatedDate) {
         this.updatedDate = updatedDate;
         return this;
     }
@@ -154,7 +166,6 @@ public class AccountBuilder {
                                   currency, billingCycleDay, paymentProviderName,
                                   timeZone, locale,
                                   address1, address2, companyName, city, stateOrProvince, country,
-                                  postalCode, phone,
-                                  createdDate, updatedDate);
+                                  postalCode, phone, createdBy, createdDate, updatedBy, updatedDate);
     }
 }
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 3e9074f..4ce9587 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
@@ -27,29 +27,28 @@ import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.DefaultAccount;
 import com.ning.billing.account.api.MigrationAccountData;
 import com.ning.billing.account.dao.AccountDao;
-import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.CallContext;
 import com.ning.billing.util.customfield.CustomField;
 import com.ning.billing.util.entity.EntityPersistenceException;
 import com.ning.billing.util.tag.Tag;
 
 public class DefaultAccountUserApi implements com.ning.billing.account.api.AccountUserApi {
     private final AccountDao dao;
-	private Clock clock;
 
     @Inject
-    public DefaultAccountUserApi(final AccountDao dao, final Clock clock) {
+    public DefaultAccountUserApi(final AccountDao dao) {
         this.dao = dao;
-        this.clock = clock;
     }
 
     @Override
-    public Account createAccount(final AccountData data, final List<CustomField> fields, List<Tag> tags) throws AccountApiException {
-        Account account = new DefaultAccount(data, clock.getUTCNow());
-        account.addFields(fields);
+    public Account createAccount(final AccountData data, final List<CustomField> fields,
+                                 final List<Tag> tags, final CallContext context) throws AccountApiException {
+        Account account = new DefaultAccount(data);
+        account.setFields(fields);
         account.addTags(tags);
 
         try {
-            dao.create(account);
+            dao.create(account, context);
         } catch (EntityPersistenceException e) {
             throw new AccountApiException(e, ErrorCode.ACCOUNT_CREATION_FAILED);
         }
@@ -78,16 +77,17 @@ public class DefaultAccountUserApi implements com.ning.billing.account.api.Accou
     }
 
     @Override
-    public void updateAccount(final Account account) throws AccountApiException {
+    public void updateAccount(final Account account, final CallContext context) throws AccountApiException {
         try {
-            dao.update(account);
+            dao.update(account, context);
         } catch (EntityPersistenceException e) {
             throw new AccountApiException(e, ErrorCode.ACCOUNT_UPDATE_FAILED);
         }
     }
 
     @Override
-    public void updateAccount(final String externalKey, final AccountData accountData) throws AccountApiException {
+    public void updateAccount(final String externalKey, final AccountData accountData,
+                              final CallContext context) throws AccountApiException {
     	UUID accountId = getIdFromKey(externalKey);
     	if(accountId == null) {
     		throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_KEY, externalKey);
@@ -96,28 +96,28 @@ public class DefaultAccountUserApi implements com.ning.billing.account.api.Accou
         Account account = new DefaultAccount(accountId, accountData);
 
         try {
-            dao.update(account);
+            dao.update(account, context);
         } catch (EntityPersistenceException e) {
             throw new AccountApiException(e, ErrorCode.ACCOUNT_UPDATE_FAILED);
         }
     }
 
 	@Override
-	public void deleteAccountByKey(String externalKey) throws AccountApiException {
-		dao.deleteByKey(externalKey);
+	public void deleteAccountByKey(final String externalKey, final CallContext context) throws AccountApiException {
+		dao.deleteByKey(externalKey, context);
 	}
 
 	@Override
-	public Account migrateAccount(MigrationAccountData data,
-			List<CustomField> fields, List<Tag> tags)
-			throws AccountApiException {
+	public Account migrateAccount(final MigrationAccountData data, final List<CustomField> fields,
+                                  final List<Tag> tags, final CallContext context)
+            throws AccountApiException {
 		
 		Account account = new DefaultAccount(data);
-        account.addFields(fields);
+        account.setFields(fields);
         account.addTags(tags);
 
         try {
-            dao.create(account);
+            dao.create(account, context);
         } catch (EntityPersistenceException e) {
             throw new AccountApiException(e, ErrorCode.ACCOUNT_CREATION_FAILED);
         }
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 774a1e6..8fcda98 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
@@ -19,6 +19,7 @@ package com.ning.billing.account.dao;
 import java.util.UUID;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.util.CallContext;
 import com.ning.billing.util.entity.EntityDao;
 import com.ning.billing.util.entity.UpdatableEntityDao;
 
@@ -33,5 +34,5 @@ public interface AccountDao extends UpdatableEntityDao<Account> {
      */
     public UUID getIdFromKey(String externalKey) throws AccountApiException;
 
-	public void deleteByKey(String externalKey) throws AccountApiException;
+	public void deleteByKey(String externalKey, CallContext context) throws AccountApiException;
 }
\ No newline at end of file
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 3fb159f..02f6682 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
@@ -27,6 +27,9 @@ import java.sql.Timestamp;
 import java.util.Date;
 import java.util.UUID;
 
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.entity.CallContextBinder;
+import com.ning.billing.util.entity.MapperBase;
 import com.ning.billing.util.entity.UpdatableEntityDao;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
@@ -48,7 +51,6 @@ import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.user.AccountBuilder;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.util.UuidMapper;
-import com.ning.billing.util.entity.EntityDao;
 
 @ExternalizedSqlViaStringTemplate3
 @RegisterMapper({UuidMapper.class, AccountSqlDao.AccountMapper.class})
@@ -61,22 +63,16 @@ public interface AccountSqlDao extends UpdatableEntityDao<Account>, Transactiona
 
     @Override
     @SqlUpdate
-    public void create(@AccountBinder Account account);
+    public void create(@AccountBinder Account account, @CallContextBinder final CallContext context);
 
     @Override
     @SqlUpdate
-    public void update(@AccountBinder Account account);
+    public void update(@AccountBinder Account account, @CallContextBinder final CallContext context);
 
     @SqlUpdate
     public void deleteByKey(@Bind("externalKey") final String key);
 
-    public static class AccountMapper implements ResultSetMapper<Account> {
-
-        private DateTime getDate(ResultSet rs, String fieldName) throws SQLException {
-            final Timestamp resultStamp = rs.getTimestamp(fieldName);
-            return rs.wasNull() ? null : new DateTime(resultStamp).toDateTime(DateTimeZone.UTC);
-        }
-
+    public static class AccountMapper extends MapperBase implements ResultSetMapper<Account> {
         @Override
         public Account map(int index, ResultSet result, StatementContext context) throws SQLException {
 
@@ -91,8 +87,6 @@ public interface AccountSqlDao extends UpdatableEntityDao<Account>, Transactiona
             Currency currency = (currencyString == null) ? null : Currency.valueOf(currencyString);
 
             String paymentProviderName = result.getString("payment_provider_name");
-            DateTime createdDate = getDate(result, "created_dt");
-            DateTime updatedDate = getDate(result, "updated_dt");
 
             String timeZoneId = result.getString("time_zone");
             DateTimeZone timeZone = (timeZoneId == null) ? null : DateTimeZone.forID(timeZoneId);
@@ -108,6 +102,11 @@ public interface AccountSqlDao extends UpdatableEntityDao<Account>, Transactiona
             String country = result.getString("country");
             String phone = result.getString("phone");
 
+            String createdBy = result.getString("created_by");
+            DateTime createdDate = getDate(result, "created_date");
+            String updatedBy = result.getString("updated_by");
+            DateTime updatedDate = getDate(result, "updated_date");
+
             return new AccountBuilder(id).externalKey(externalKey).email(email)
                                          .name(name).firstNameLength(firstNameLength)
                                          .phone(phone).currency(currency)
@@ -118,8 +117,8 @@ public interface AccountSqlDao extends UpdatableEntityDao<Account>, Transactiona
                                          .companyName(companyName)
                                          .city(city).stateOrProvince(stateOrProvince)
                                          .postalCode(postalCode).country(country)
-                                         .createdDate(createdDate)
-                                         .updatedDate(updatedDate)
+                                         .createdBy(createdBy).createdDate(createdDate)
+                                         .updatedBy(updatedBy).updatedDate(updatedDate)
                                          .build();
         }
     }
@@ -158,8 +157,6 @@ public interface AccountSqlDao extends UpdatableEntityDao<Account>, Transactiona
                         q.bind("country", account.getCountry());
                         q.bind("postalCode", account.getPostalCode());
                         q.bind("phone", account.getPhone());
-                        q.bind("createdDate", getDate(account.getCreatedDate()));
-                        q.bind("updatedDate", getDate(account.getUpdatedDate()));
                     }
                 };
             }
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
index 429e0fb..754c4ea 100644
--- a/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
@@ -20,7 +20,10 @@ import java.sql.DataTruncation;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.customfield.dao.AuditedCustomFieldDao;
 import com.ning.billing.util.entity.EntityPersistenceException;
+import com.ning.billing.util.tag.dao.AuditedTagDao;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
@@ -33,10 +36,10 @@ import com.ning.billing.account.api.AccountCreationNotification;
 import com.ning.billing.account.api.user.DefaultAccountChangeNotification;
 import com.ning.billing.account.api.user.DefaultAccountCreationEvent;
 import com.ning.billing.util.customfield.CustomField;
-import com.ning.billing.util.customfield.dao.FieldStoreDao;
+import com.ning.billing.util.customfield.dao.CustomFieldSqlDao;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.tag.Tag;
-import com.ning.billing.util.tag.dao.TagStoreSqlDao;
+import com.ning.billing.util.tag.dao.TagSqlDao;
 
 public class DefaultAccountDao implements AccountDao {
     private final AccountSqlDao accountSqlDao;
@@ -103,7 +106,7 @@ public class DefaultAccountDao implements AccountDao {
     }
 
     @Override
-    public void create(final Account account) throws EntityPersistenceException {
+    public void create(final Account account, final CallContext context) throws EntityPersistenceException {
         final String key = account.getExternalKey();
         try {
             accountSqlDao.inTransaction(new Transaction<Void, AccountSqlDao>() {
@@ -113,10 +116,10 @@ public class DefaultAccountDao implements AccountDao {
                     if (currentAccount != null) {
                         throw new AccountApiException(ErrorCode.ACCOUNT_ALREADY_EXISTS, key);
                     }
-                    transactionalDao.create(account);
+                    transactionalDao.create(account, context);
 
-                    saveTagsFromWithinTransaction(account, transactionalDao, true);
-                    saveCustomFieldsFromWithinTransaction(account, transactionalDao, true);
+                    saveTagsFromWithinTransaction(account, transactionalDao , context);
+                    saveCustomFieldsFromWithinTransaction(account, transactionalDao, context);
                     AccountCreationNotification creationEvent = new DefaultAccountCreationEvent(account);
                     eventBus.post(creationEvent);
                     return null;
@@ -134,7 +137,7 @@ public class DefaultAccountDao implements AccountDao {
     }
 
     @Override
-    public void update(final Account account) throws EntityPersistenceException {
+    public void update(final Account account, final CallContext context) throws EntityPersistenceException {
         try {
             accountSqlDao.inTransaction(new Transaction<Void, AccountSqlDao>() {
                 @Override
@@ -150,10 +153,10 @@ public class DefaultAccountDao implements AccountDao {
                         throw new EntityPersistenceException(ErrorCode.ACCOUNT_CANNOT_CHANGE_EXTERNAL_KEY, currentKey);
                     }
 
-                    accountSqlDao.update(account);
+                    accountSqlDao.update(account, context);
 
-                    saveTagsFromWithinTransaction(account, accountSqlDao, false);
-                    saveCustomFieldsFromWithinTransaction(account, accountSqlDao, false);
+                    saveTagsFromWithinTransaction(account, accountSqlDao, context);
+                    saveCustomFieldsFromWithinTransaction(account, accountSqlDao, context);
 
                     AccountChangeNotification changeEvent = new DefaultAccountChangeNotification(account.getId(), currentAccount, account);
                     if (changeEvent.hasChanges()) {
@@ -172,7 +175,7 @@ public class DefaultAccountDao implements AccountDao {
     }
 
     @Override
-	public void deleteByKey(final String externalKey) throws AccountApiException {
+	public void deleteByKey(final String externalKey, final CallContext context) throws AccountApiException {
     	try {
             accountSqlDao.inTransaction(new Transaction<Void, AccountSqlDao>() {
                 @Override
@@ -197,18 +200,18 @@ public class DefaultAccountDao implements AccountDao {
     }
 
     private void setCustomFieldsFromWithinTransaction(final Account account, final AccountSqlDao transactionalDao) {
-        FieldStoreDao fieldStoreDao = transactionalDao.become(FieldStoreDao.class);
-        List<CustomField> fields = fieldStoreDao.load(account.getId().toString(), account.getObjectName());
+        CustomFieldSqlDao customFieldSqlDao = transactionalDao.become(CustomFieldSqlDao.class);
+        List<CustomField> fields = customFieldSqlDao.load(account.getId().toString(), account.getObjectName());
 
         account.clearFields();
         if (fields != null) {
-            account.addFields(fields);
+            account.setFields(fields);
         }
     }
 
     private void setTagsFromWithinTransaction(final Account account, final AccountSqlDao transactionalDao) {
-        TagStoreSqlDao tagStoreDao = transactionalDao.become(TagStoreSqlDao.class);
-        List<Tag> tags = tagStoreDao.load(account.getId().toString(), account.getObjectName());
+        TagSqlDao tagDao = transactionalDao.become(TagSqlDao.class);
+        List<Tag> tags = tagDao.load(account.getId().toString(), account.getObjectName());
         account.clearTags();
 
         if (tags != null) {
@@ -216,33 +219,15 @@ public class DefaultAccountDao implements AccountDao {
         }
     }
 
-    private void saveTagsFromWithinTransaction(final Account account, final AccountSqlDao transactionalDao, final boolean isCreation) {
-        String accountId = account.getId().toString();
-        String objectType = account.getObjectName();
-
-        TagStoreSqlDao tagStoreDao = transactionalDao.become(TagStoreSqlDao.class);
-        if (!isCreation) {
-            tagStoreDao.clear(accountId, objectType);
-        }
-
-        List<Tag> tagList = account.getTagList();
-        if (tagList != null) {
-            tagStoreDao.batchSaveFromTransaction(accountId, objectType, tagList);
-        }
+    private void saveTagsFromWithinTransaction(final Account account, final AccountSqlDao transactionalDao,
+                                               final CallContext context) {
+        AuditedTagDao tagDao = new AuditedTagDao();
+        tagDao.saveTags(transactionalDao, account.getId(), account.getObjectName(), account.getTagList(), context);
     }
 
-    private void saveCustomFieldsFromWithinTransaction(final Account account, final AccountSqlDao transactionalDao, final boolean isCreation) {
-        String accountId = account.getId().toString();
-        String objectType = account.getObjectName();
-
-        FieldStoreDao fieldStoreDao = transactionalDao.become(FieldStoreDao.class);
-        if (!isCreation) {
-            fieldStoreDao.clear(accountId, objectType);
-        }
-
-        List<CustomField> fieldList = account.getFieldList();
-        if (fieldList != null) {
-            fieldStoreDao.batchSaveFromTransaction(accountId, objectType, fieldList);
-        }
+    private void saveCustomFieldsFromWithinTransaction(final Account account, final AccountSqlDao transactionalDao,
+                                                       final CallContext context) {
+        AuditedCustomFieldDao customFieldDao = new AuditedCustomFieldDao();
+        customFieldDao.saveFields(transactionalDao, account.getId(), account.getObjectName(), account.getFieldList(), context);
     }
 }
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 7e93ff2..eb47b88 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
@@ -19,11 +19,12 @@ accountFields(prefix) ::= <<
     <prefix>country, 
     <prefix>postal_code,
     <prefix>phone,	
-    <prefix>created_dt,
-    <prefix>updated_dt
+    <prefix>created_by,
+    <prefix>created_date,
+    <prefix>updated_by,
+    <prefix>updated_date
 >>
 
-
 create() ::= <<
     INSERT INTO accounts
       (<accountFields()>)
@@ -31,7 +32,7 @@ create() ::= <<
       (:id, :externalKey, :email, :name, :firstNameLength, :currency, :billingCycleDay,
       :paymentProviderName, :timeZone, :locale,
       :address1, :address2, :companyName, :city, :stateOrProvince, :country, :postalCode, :phone,
-      :createdDate, :updatedDate);
+      :userName, :date, null, null);
 >>
 
 update() ::= <<
@@ -41,7 +42,7 @@ update() ::= <<
         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,
-        updated_dt = NOW()
+        updated_date = :date, updated_by = :userName
     WHERE id = :id;
 >>
 
@@ -59,19 +60,19 @@ getAccountByKey() ::= <<
 
 getById() ::= <<
     SELECT <accountFields()>
-      FROM accounts
-     WHERE id = :id;
+    FROM accounts
+    WHERE id = :id;
 >>
 
 get() ::= <<
     SELECT <accountFields()>
-      FROM accounts;
+    FROM accounts;
 >>
 
 getIdFromKey() ::= <<
     SELECT id
-      FROM accounts
-     WHERE external_key = :externalKey;
+    FROM accounts
+    WHERE external_key = :externalKey;
 >>
 
 test() ::= <<
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 fd2f6f8..acabc3a 100644
--- a/account/src/main/resources/com/ning/billing/account/ddl.sql
+++ b/account/src/main/resources/com/ning/billing/account/ddl.sql
@@ -18,8 +18,10 @@ CREATE TABLE accounts (
     country varchar(50) DEFAULT NULL,
     postal_code varchar(11) DEFAULT NULL,
     phone varchar(25) DEFAULT NULL,
-    created_dt datetime,
-    updated_dt datetime,
+    created_date datetime NOT NULL,
+    created_by varchar(30) NOT NULL,
+    updated_date datetime DEFAULT NULL,
+    updated_by varchar(30) DEFAULT NULL,
     PRIMARY KEY(id)
 ) ENGINE=innodb;
 CREATE UNIQUE INDEX accounts_external_key ON accounts(external_key);
@@ -45,28 +47,8 @@ CREATE TABLE account_history (
     country varchar(50) DEFAULT NULL,
     postal_code varchar(11) DEFAULT NULL,
     phone varchar(25) DEFAULT NULL,
+    change_type char(6) NOT NULL,
+    updated_by varchar(30) NOT NULL,
     date datetime
 ) ENGINE=innodb;
-CREATE INDEX account_id ON account_history(id);
-
-CREATE TRIGGER store_account_history_on_insert AFTER INSERT ON accounts
-    FOR EACH ROW
-        INSERT INTO account_history (id, external_key, email, name, first_name_length, currency,
-                                    billing_cycle_day, payment_provider_name, time_zone, locale, 
-                                    address1, address2, company_name, city, state_or_province, 
-                                    country, postal_code, phone, date)
-        VALUES (NEW.id, NEW.external_key, NEW.email, NEW.name, NEW.first_name_length, NEW.currency,
-                NEW.billing_cycle_day, NEW.payment_provider_name, NEW.time_zone, NEW.locale, 
-                NEW.address1, NEW.address2, NEW.company_name, NEW.city, NEW.state_or_province, 
-                NEW.country, NEW.postal_code, NEW.phone, NEW.created_dt);
-
-CREATE TRIGGER store_account_history_on_update AFTER UPDATE ON accounts
-    FOR EACH ROW
-        INSERT INTO account_history (id, external_key, email, name, first_name_length, currency,
-                                    billing_cycle_day, payment_provider_name, time_zone, locale, 
-                                    address1, address2, company_name, city, state_or_province, 
-                                    country, postal_code, phone, date)
-        VALUES (NEW.id, NEW.external_key, NEW.email, NEW.name, NEW.first_name_length, NEW.currency,
-                NEW.billing_cycle_day, NEW.payment_provider_name, NEW.time_zone, NEW.locale, 
-                NEW.address1, NEW.address2, NEW.company_name, NEW.city, NEW.state_or_province, 
-                NEW.country, NEW.postal_code, NEW.phone, NEW.updated_dt);
+CREATE INDEX account_id ON account_history(id);
\ No newline at end of file
diff --git a/account/src/test/java/com/ning/billing/account/api/MockAccountUserApi.java b/account/src/test/java/com/ning/billing/account/api/MockAccountUserApi.java
index ac74b68..dc5131f 100644
--- a/account/src/test/java/com/ning/billing/account/api/MockAccountUserApi.java
+++ b/account/src/test/java/com/ning/billing/account/api/MockAccountUserApi.java
@@ -16,13 +16,12 @@
 
 package com.ning.billing.account.api;
 
-import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.CopyOnWriteArrayList;
 
-import org.joda.time.DateTime;
+import com.ning.billing.util.CallContext;
 import org.joda.time.DateTimeZone;
 
 import com.ning.billing.catalog.api.Currency;
@@ -40,7 +39,6 @@ public class MockAccountUserApi implements AccountUserApi {
                                  Currency currency,
                                  int billCycleDay,
                                  String paymentProviderName,
-                                 BigDecimal balance,
                                  final DateTimeZone timeZone, 
                                  final String locale,
                                  final String address1, 
@@ -55,14 +53,15 @@ public class MockAccountUserApi implements AccountUserApi {
 		Account result = new DefaultAccount(id, externalKey, email, name,
 				firstNameLength, currency, billCycleDay, paymentProviderName,
 				timeZone, locale, address1, address2, companyName, city,
-				stateOrProvince, country, postalCode, phone, null, null);
+				stateOrProvince, country, postalCode, phone, null, null, null, null);
 		accounts.add(result);
 		return result;
 	}
 
     @Override
-    public Account createAccount(AccountData data, List<CustomField> fields, List<Tag> tags) throws AccountApiException {
-        Account result = new DefaultAccount(data, new DateTime());
+    public Account createAccount(final AccountData data, final List<CustomField> fields,
+                                 final List<Tag> tags, final CallContext context) throws AccountApiException {
+        Account result = new DefaultAccount(data);
         accounts.add(result);
         return result;
     }
@@ -103,32 +102,32 @@ public class MockAccountUserApi implements AccountUserApi {
     }
 
     @Override
-    public void updateAccount(Account account) {
+    public void updateAccount(final Account account, final CallContext context) {
         throw new UnsupportedOperationException();
     }
 
 	@Override
-	public void deleteAccountByKey(String externalKey)
+	public void deleteAccountByKey(final String externalKey, final CallContext context)
 			throws AccountApiException {
 		for (Account account : accounts) {
             if (externalKey.equals(account.getExternalKey())) {
-                accounts.remove(account.getId());
+                accounts.remove(account);
             }
         }	
 		
 	}
 
 	@Override
-	public Account migrateAccount(MigrationAccountData data,
-			List<CustomField> fields, List<Tag> tags)
+	public Account migrateAccount(final MigrationAccountData data,
+			final List<CustomField> fields, final List<Tag> tags, final CallContext context)
 			throws AccountApiException {
-		Account result = new DefaultAccount(data, data.getCreatedDate(), data.getUpdatedDate());
+		Account result = new DefaultAccount(data);
         accounts.add(result);
         return result;
 	}
 
 	@Override
-	public void updateAccount(String key, AccountData accountData)
+	public void updateAccount(String key, AccountData accountData, final CallContext context)
 			throws AccountApiException {
 		throw new UnsupportedOperationException();
 	}
diff --git a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
index 964eb6a..d62d84c 100644
--- a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
+++ b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
@@ -20,6 +20,11 @@ import static org.testng.Assert.fail;
 
 import java.io.IOException;
 
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.CallOrigin;
+import com.ning.billing.util.UserType;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.entity.DefaultCallContext;
 import org.apache.commons.io.IOUtils;
 import org.skife.jdbi.v2.Handle;
 import org.skife.jdbi.v2.IDBI;
@@ -41,6 +46,8 @@ public abstract class AccountDaoTestBase {
     protected AccountDao accountDao;
     protected IDBI dbi;
 
+    protected CallContext context;
+
     @BeforeClass(alwaysRun = true)
     protected void setup() throws IOException {
         // Health check test to make sure MySQL is setup properly
@@ -59,6 +66,10 @@ public abstract class AccountDaoTestBase {
             accountDao = injector.getInstance(AccountDao.class);
             accountDao.test();
 
+            Clock clock = injector.getInstance(Clock.class);
+            context = new DefaultCallContext(clock, "Vizzini", CallOrigin.TEST, UserType.TEST);
+
+
             BusService busService = injector.getInstance(BusService.class);
             ((DefaultBusService) busService).startBus();
         }
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 b351709..9641426 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
@@ -28,6 +28,7 @@ import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountChangeNotification;
 import com.ning.billing.account.api.user.DefaultAccountChangeNotification;
 import com.ning.billing.account.api.user.DefaultAccountCreationEvent;
+import com.ning.billing.util.CallContext;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.bus.Bus.EventBusException;
 
@@ -41,7 +42,7 @@ public class MockAccountDao implements AccountDao {
     }
 
     @Override
-    public void create(Account account) {
+    public void create(Account account, CallContext context) {
         accounts.put(account.getId().toString(), account);
 
         try {
@@ -83,7 +84,7 @@ public class MockAccountDao implements AccountDao {
     }
 
     @Override
-    public void update(Account account) {
+    public void update(Account account, CallContext context) {
         Account currentAccount = accounts.put(account.getId().toString(), account);
 
         AccountChangeNotification changeEvent = new DefaultAccountChangeNotification(account.getId(), currentAccount, account);
@@ -98,10 +99,10 @@ public class MockAccountDao implements AccountDao {
     }
 
 	@Override
-	public void deleteByKey(String externalKey) throws AccountApiException {
+	public void deleteByKey(String externalKey, CallContext context) throws AccountApiException {
 		for (Account account : accounts.values()) {
             if (externalKey.equals(account.getExternalKey())) {
-                accounts.remove(account.getId());
+                accounts.remove(account.getId().toString());
             }
         }		
 	}
diff --git a/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
index 8c0e3c2..e2fb351 100644
--- a/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
@@ -25,7 +25,6 @@ import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.util.entity.EntityPersistenceException;
-import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.testng.annotations.Test;
 
@@ -35,7 +34,6 @@ import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.DefaultAccount;
 import com.ning.billing.account.api.user.AccountBuilder;
 import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.util.clock.DefaultClock;
 import com.ning.billing.util.tag.DefaultTagDefinition;
 import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.TagDefinition;
@@ -53,9 +51,6 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
         String locale = "EN-US";
         DateTimeZone timeZone = DateTimeZone.forID("America/Los_Angeles");
 
-        DateTime createdDate = new DateTime(DateTimeZone.UTC);
-        DateTime updatedDate = new DateTime(DateTimeZone.UTC);
-
         int firstNameLength = firstName.length();
         return new AccountBuilder().externalKey(thisKey)
                                    .name(name)
@@ -64,15 +59,13 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
                                    .email(thisEmail)
                                    .currency(Currency.USD)
                                    .locale(locale)
-                                   .timeZone(timeZone)
-                                   .createdDate(createdDate)
-                                   .updatedDate(updatedDate);
+                                   .timeZone(timeZone);
     }
 
     @Test
     public void testBasic() throws EntityPersistenceException {
         Account a = createTestAccountBuilder().build();
-        accountDao.create(a);
+        accountDao.create(a, context);
         String key = a.getExternalKey();
 
         Account r = accountDao.getAccountByKey(key);
@@ -92,7 +85,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
     @Test
     public void testLongPhoneNumber() throws EntityPersistenceException {
         Account account = createTestAccountBuilder().phone("123456789012345678901234").build();
-        accountDao.create(account);
+        accountDao.create(account, context);
 
         Account saved = accountDao.getAccountByKey(account.getExternalKey());
         assertNotNull(saved);
@@ -102,7 +95,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
     @Test(expectedExceptions = {EntityPersistenceException.class})
     public void testOverlyLongPhoneNumber() throws EntityPersistenceException {
         Account account = createTestAccountBuilder().phone("12345678901234567890123456").build();
-        accountDao.create(account);
+        accountDao.create(account, context);
     }
 
     @Test
@@ -113,7 +106,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
         String name = account.getName();
         int firstNameLength = account.getFirstNameLength();
 
-        accountDao.create(account);
+        accountDao.create(account, context);
 
         account = accountDao.getById(id.toString());
         assertNotNull(account);
@@ -131,7 +124,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
         String fieldValue = "testField1_value";
         account.setFieldValue(fieldName, fieldValue);
 
-        accountDao.create(account);
+        accountDao.create(account, context);
 
         Account thisAccount = accountDao.getAccountByKey(account.getExternalKey());
         assertNotNull(thisAccount);
@@ -142,29 +135,25 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
     @Test
     public void testTags() throws EntityPersistenceException {
         Account account = createTestAccountBuilder().build();
-        TagDefinition definition = new DefaultTagDefinition("Test Tag", "For testing only", "Test System");
+        TagDefinition definition = new DefaultTagDefinition("Test Tag", "For testing only");
         TagDefinitionSqlDao tagDescriptionDao = dbi.onDemand(TagDefinitionSqlDao.class);
-        tagDescriptionDao.create(definition);
+        tagDescriptionDao.create(definition, context);
 
-        String addedBy = "testTags()";
-        DateTime dateAdded = new DefaultClock().getUTCNow();
-        account.addTag(definition, addedBy, dateAdded);
+        account.addTag(definition);
         assertEquals(account.getTagList().size(), 1);
-        accountDao.create(account);
+        accountDao.create(account, context);
 
         Account thisAccount = accountDao.getById(account.getId().toString());
         List<Tag> tagList = thisAccount.getTagList();
         assertEquals(tagList.size(), 1);
         Tag tag = tagList.get(0);
         assertEquals(tag.getTagDefinitionName(), definition.getName());
-        assertEquals(tag.getAddedBy(), addedBy);
-        assertEquals(tag.getAddedDate().compareTo(dateAdded), 0);
     }
 
     @Test
     public void testGetIdFromKey() throws EntityPersistenceException {
         Account account = createTestAccountBuilder().build();
-        accountDao.create(account);
+        accountDao.create(account, context);
 
         try {
             UUID accountId = accountDao.getIdFromKey(account.getExternalKey());
@@ -183,7 +172,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
     @Test
     public void testUpdate() throws Exception {
         final Account account = createTestAccountBuilder().build();
-        accountDao.create(account);
+        accountDao.create(account, context);
 
         AccountData accountData = new AccountData() {
             @Override
@@ -270,8 +259,8 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
             }
         };
 
-        Account updatedAccount = new DefaultAccount(account.getId(), accountData);
-        accountDao.update(updatedAccount);
+        Account updatedAccount = new DefaultAccount(account.getId(), null, null, null, null, accountData);
+        accountDao.update(updatedAccount, context);
 
         Account savedAccount = accountDao.getAccountByKey(account.getExternalKey());
 
@@ -298,8 +287,8 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
         DefaultAccount account = new DefaultAccount(accountId, "extKey123456", "myemail123456@glam.com",
                                                     "John Smith", 4, Currency.USD, 15, null,
                                                     DateTimeZone.forID("America/Cambridge_Bay"), "EN-CA",
-                                                    null, null, null, null, null, null, null, null,null,null);
-        accountDao.create(account);
+                                                    null, null, null, null, null, null, null, null, null, null, null, null);
+        accountDao.create(account, context);
 
         String address1 = "123 address 1";
         String address2 = "456 address 2";
@@ -314,9 +303,9 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
                                                     "John Smith", 4, Currency.USD, 15, null,
                                                     DateTimeZone.forID("America/Cambridge_Bay"), "EN-CA",
                                                     address1, address2, companyName, city, stateOrProvince, country,
-                                                    postalCode, phone, null,null);
+                                                    postalCode, phone, null, null, null, null);
 
-        accountDao.update(updatedAccount);
+        accountDao.update(updatedAccount, context);
 
         Account savedAccount = accountDao.getById(accountId.toString());
 
@@ -340,15 +329,17 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
                                                     "John Smith", 4, Currency.USD, 15, null,
                                                     DateTimeZone.forID("America/Cambridge_Bay"), "EN-CA",
                                                     "123 address 1", "456 address 2", null, "Cambridge Bay",
-                                                    "Nunavut", "Canada", "X0B 0C0", "18001112222", null, null);
-        accountDao.create(account);
+                                                    "Nunavut", "Canada", "X0B 0C0", "18001112222",
+                                                    null, null, null, null);
+        accountDao.create(account, context);
 
         DefaultAccount updatedAccount = new DefaultAccount(accountId, "extKey654321", "myemail654321@glam.com",
                                                     "John Smith", 4, Currency.USD, 15, null,
                                                     DateTimeZone.forID("America/Cambridge_Bay"), "EN-CA",
-                                                    null, null, null, null, null, null, null, null, null, null);
+                                                    null, null, null, null, null, null, null, null,
+                                                    null, null, null, null);
 
-        accountDao.update(updatedAccount);
+        accountDao.update(updatedAccount, context);
 
         Account savedAccount = accountDao.getById(accountId.toString());
 
@@ -371,27 +362,29 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
 
         DefaultAccount account = new DefaultAccount(accountId, originalExternalKey, "myemail1337@glam.com",
                                                     "John Smith", 4, Currency.USD, 15, null,
-                                                    null, null, null, null, null, null, null, null, null, null, null, null);
-        accountDao.create(account);
+                                                    null, null, null, null, null, null, null, null, null, null,
+                                                    null, null, null, null);
+        accountDao.create(account, context);
 
         DefaultAccount updatedAccount = new DefaultAccount(accountId, "extKey1338", "myemail1337@glam.com",
                                                     "John Smith", 4, Currency.USD, 15, null,
-                                                    null, null, null, null, null, null, null, null, null, null,null, null);
-        accountDao.update(updatedAccount);
+                                                    null, null, null, null, null, null, null, null, null, null,
+                                                    null, null, null, null);
+        accountDao.update(updatedAccount, context);
     }
 
     @Test(groups={"slow"},enabled=true)
     public void testDelete() throws AccountApiException, EntityPersistenceException {
 
         Account a = createTestAccountBuilder().build();
-        accountDao.create(a);
+        accountDao.create(a, context);
         String key = a.getExternalKey();
 
         Account r = accountDao.getAccountByKey(key);
         assertNotNull(r);
         assertEquals(r.getExternalKey(), a.getExternalKey());
         
-        accountDao.deleteByKey(key);
+        accountDao.deleteByKey(key, context);
         
         Account s = accountDao.getAccountByKey(key);
         assertTrue(s==null);
diff --git a/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithMocks.java b/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithMocks.java
index 1c65c3c..1720cd6 100644
--- a/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithMocks.java
+++ b/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithMocks.java
@@ -27,7 +27,6 @@ public class AccountModuleWithMocks extends AccountModule {
         bind(AccountDao.class).to(MockAccountDao.class);
     }
 
-
     @Override
     protected void configure() {
         super.configure();
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 c88d255..3924858 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
@@ -26,6 +26,10 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.CallOrigin;
+import com.ning.billing.util.UserType;
+import com.ning.billing.util.entity.DefaultCallContext;
 import org.apache.commons.io.IOUtils;
 import org.joda.time.DateTime;
 import org.testng.Assert;
@@ -87,13 +91,14 @@ public class TestAnalyticsService {
     private static final String KEY = "12345";
     private static final String ACCOUNT_KEY = "pierre-12345";
     private static final Currency ACCOUNT_CURRENCY = Currency.EUR;
-    private static final DefaultTagDefinition TAG_ONE = new DefaultTagDefinition("batch20", "something", "pierre");
-    private static final DefaultTagDefinition TAG_TWO = new DefaultTagDefinition("awesome", "something", "pierre");
+    private static final DefaultTagDefinition TAG_ONE = new DefaultTagDefinition("batch20", "something");
+    private static final DefaultTagDefinition TAG_TWO = new DefaultTagDefinition("awesome", "something");
     private static final BigDecimal INVOICE_AMOUNT = BigDecimal.valueOf(1243.11);
     private static final String PAYMENT_METHOD = "Paypal";
     private static final String CARD_COUNTRY = "France";
 
     private final Clock clock = new DefaultClock();
+    private final CallContext context = new DefaultCallContext(clock, "Analytics Test", CallOrigin.TEST, UserType.TEST);
 
     @Inject
     private AccountUserApi accountApi;
@@ -137,16 +142,16 @@ public class TestAnalyticsService {
         // Killbill generic setup
         setupBusAndMySQL();
 
-        tagDao.create(TAG_ONE);
-        tagDao.create(TAG_TWO);
+        tagDao.create(TAG_ONE, context);
+        tagDao.create(TAG_TWO, context);
 
         final MockAccount account = new MockAccount(UUID.randomUUID(), ACCOUNT_KEY, ACCOUNT_CURRENCY);
         try {
             final List<Tag> tags = new ArrayList<Tag>();
-            tags.add(new DescriptiveTag(TAG_ONE, "pierre", clock.getUTCNow()));
-            tags.add(new DescriptiveTag(TAG_TWO, "pierre", clock.getUTCNow()));
+            tags.add(new DescriptiveTag(TAG_ONE));
+            tags.add(new DescriptiveTag(TAG_TWO));
 
-            final Account storedAccount = accountApi.createAccount(account, null, tags);
+            final Account storedAccount = accountApi.createAccount(account, null, tags, context);
 
             // Create events for the bus and expected results
             createSubscriptionTransitionEvent(storedAccount);
@@ -227,14 +232,13 @@ public class TestAnalyticsService {
     }
 
     private void createInvoiceAndPaymentCreationEvents(final Account account) {
-        final DefaultInvoice invoice = new DefaultInvoice(account.getId(), clock.getUTCNow(), ACCOUNT_CURRENCY, clock);
+        final DefaultInvoice invoice = new DefaultInvoice(account.getId(), clock.getUTCNow(), clock.getUTCNow(), ACCOUNT_CURRENCY);
         final FixedPriceInvoiceItem invoiceItem = new FixedPriceInvoiceItem(
                 UUID.randomUUID(), invoice.getId(), UUID.randomUUID(), "somePlan", "somePhase", clock.getUTCNow(), clock.getUTCNow().plusDays(1),
-                INVOICE_AMOUNT, ACCOUNT_CURRENCY, clock.getUTCNow()
-        );
+                INVOICE_AMOUNT, ACCOUNT_CURRENCY, context.getUserName(), clock.getUTCNow());
         invoice.addInvoiceItem(invoiceItem);
 
-        invoiceDao.create(invoice);
+        invoiceDao.create(invoice, context);
         Assert.assertEquals(invoiceDao.getInvoicesByAccount(account.getId()).size(), 1);
         Assert.assertEquals(invoiceDao.getInvoicesByAccount(account.getId()).get(0).getInvoiceItems().size(), 1);
 
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java b/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
index d57de25..87ee4cd 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
@@ -19,7 +19,7 @@ package com.ning.billing.analytics;
 import java.util.List;
 import java.util.UUID;
 
-import org.apache.commons.lang.NotImplementedException;
+import com.ning.billing.util.CallContext;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 
@@ -139,83 +139,107 @@ public class MockAccount implements Account
     }
 
     @Override
+    public String getCreatedBy() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public DateTime getCreatedDate() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public String getFieldValue(String fieldName) {
-        throw new NotImplementedException();
+        throw new UnsupportedOperationException();
     }
 
     @Override
     public void setFieldValue(String fieldName, String fieldValue) {
-        throw new NotImplementedException();
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void saveFieldValue(String fieldName, String fieldValue, CallContext context) {
+        throw new UnsupportedOperationException();
     }
 
     @Override
     public List<CustomField> getFieldList() {
-        throw new NotImplementedException();
+        throw new UnsupportedOperationException();
     }
 
     @Override
-    public void addFields(List<CustomField> fields) {
-        throw new NotImplementedException();
+    public void setFields(List<CustomField> fields) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void saveFields(List<CustomField> fields, CallContext context) {
+        throw new UnsupportedOperationException();
     }
 
     @Override
     public void clearFields() {
-        throw new NotImplementedException();
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void clearPersistedFields(CallContext context) {
+        throw new UnsupportedOperationException();
     }
 
     @Override
     public String getObjectName() {
-        throw new NotImplementedException();
+        throw new UnsupportedOperationException();
     }
 
     @Override
     public List<Tag> getTagList() {
-        throw new NotImplementedException();
+        throw new UnsupportedOperationException();
     }
 
     @Override
     public boolean hasTag(String tagName) {
-        throw new NotImplementedException();
+        throw new UnsupportedOperationException();
     }
 
     @Override
-    public void addTag(TagDefinition definition, String addedBy, DateTime dateAdded) {
-        throw new NotImplementedException();
+    public void addTag(TagDefinition definition) {
+        throw new UnsupportedOperationException();
     }
 
     @Override
     public void addTags(List<Tag> tags) {
-        throw new NotImplementedException();
+        throw new UnsupportedOperationException();
     }
 
     @Override
     public void clearTags() {
-        throw new NotImplementedException();
+        throw new UnsupportedOperationException();
     }
 
     @Override
     public void removeTag(TagDefinition definition) {
-        throw new NotImplementedException();
+        throw new UnsupportedOperationException();
     }
 
     @Override
     public boolean generateInvoice() {
-        throw new NotImplementedException();
+        throw new UnsupportedOperationException();
     }
 
     @Override
     public boolean processPayment() {
-        throw new NotImplementedException();
+        throw new UnsupportedOperationException();
     }
 
     @Override
-    public DateTime getCreatedDate() {
-        return new DateTime(DateTimeZone.UTC);
+    public String getUpdatedBy() {
+        throw new UnsupportedOperationException();
     }
 
     @Override
     public DateTime getUpdatedDate() {
-        return new DateTime(DateTimeZone.UTC);
+        throw new UnsupportedOperationException();
     }
-
 }
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java b/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
index c6d2369..3305879 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
@@ -26,7 +26,7 @@ import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.account.api.DefaultAccount;
 import com.ning.billing.account.api.MigrationAccountData;
 import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.CallContext;
 import com.ning.billing.util.customfield.CustomField;
 import com.ning.billing.util.tag.Tag;
 
@@ -34,23 +34,21 @@ public class MockIAccountUserApi implements AccountUserApi
 {
     private final AccountData account;
     private final UUID id;
-	private Clock clock;
 
-    public MockIAccountUserApi(final String accountKey, final Currency currency, final Clock clock)
+    public MockIAccountUserApi(final String accountKey, final Currency currency)
     {
         this.id = UUID.randomUUID();
         account = new MockAccount(id, accountKey, currency);
-        this.clock = clock;
     }
 
     @Override
-    public Account createAccount(final AccountData data, final List<CustomField> fields, final List<Tag> tags)
+    public Account createAccount(final AccountData data, final List<CustomField> fields, final List<Tag> tags, final CallContext context)
     {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public void updateAccount(final Account account) {
+    public void updateAccount(final Account account, final CallContext context) {
         throw new UnsupportedOperationException();
     }
 
@@ -62,7 +60,7 @@ public class MockIAccountUserApi implements AccountUserApi
 
     @Override
     public Account getAccountById(final UUID uid) {
-        return new DefaultAccount(account, clock.getUTCNow());
+        return new DefaultAccount(account);
     }
 
     @Override
@@ -77,19 +75,19 @@ public class MockIAccountUserApi implements AccountUserApi
     }
 
 	@Override
-	public void deleteAccountByKey(String externalKey) {
+	public void deleteAccountByKey(String externalKey, final CallContext context) {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
 	public Account migrateAccount(MigrationAccountData data,
-			List<CustomField> fields, List<Tag> tags)
+			List<CustomField> fields, List<Tag> tags, final CallContext context)
 			throws AccountApiException {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
-	public void updateAccount(String key, AccountData accountData)
+	public void updateAccount(String key, AccountData accountData, final CallContext context)
 			throws AccountApiException {
 		throw new UnsupportedOperationException();
 	}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java b/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
index 69abfb4..dc2dbdf 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
@@ -24,6 +24,7 @@ import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.util.CallContext;
 import com.ning.billing.util.customfield.CustomField;
 
 import org.joda.time.DateTime;
@@ -68,6 +69,16 @@ public class MockSubscription implements Subscription
     }
 
     @Override
+    public String getCreatedBy() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public DateTime getCreatedDate() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public UUID getBundleId()
     {
         return BUNDLE_ID;
@@ -168,12 +179,22 @@ public class MockSubscription implements Subscription
     }
 
     @Override
+    public void saveFieldValue(String fieldName, String fieldValue, CallContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public List<CustomField> getFieldList() {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public void addFields(List<CustomField> fields) {
+    public void setFields(List<CustomField> fields) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void saveFields(List<CustomField> fields, CallContext context) {
         throw new UnsupportedOperationException();
     }
 
@@ -183,6 +204,11 @@ public class MockSubscription implements Subscription
     }
 
     @Override
+    public void clearPersistedFields(CallContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public String getObjectName() {
         throw new UnsupportedOperationException();
     }
diff --git a/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java b/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
index 6d8e587..b57afc0 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
@@ -16,7 +16,6 @@
 
 package com.ning.billing.analytics;
 
-
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.Plan;
@@ -27,16 +26,15 @@ import com.ning.billing.entitlement.api.user.Subscription;
 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.util.clock.ClockMock;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import javax.annotation.Nullable;
 import java.util.UUID;
 
-
 public class TestAnalyticsListener
 {
     private static final String KEY = "1234";
@@ -56,7 +54,7 @@ public class TestAnalyticsListener
     @BeforeMethod(alwaysRun = true)
     public void setUp() throws Exception
     {
-        final BusinessSubscriptionTransitionRecorder recorder = new BusinessSubscriptionTransitionRecorder(dao, new MockIEntitlementUserApi(bundleUUID, KEY), new MockIAccountUserApi(ACCOUNT_KEY, CURRENCY, new ClockMock()));
+        final BusinessSubscriptionTransitionRecorder recorder = new BusinessSubscriptionTransitionRecorder(dao, new MockIEntitlementUserApi(bundleUUID, KEY), new MockIAccountUserApi(ACCOUNT_KEY, CURRENCY));
         listener = new AnalyticsListener(recorder, null);
     }
 
@@ -100,8 +98,8 @@ public class TestAnalyticsListener
         final BusinessSubscriptionEvent eventType,
         final DateTime requestedTransitionTime,
         final DateTime effectiveTransitionTime,
-        final BusinessSubscription previousSubscription,
-        final Subscription.SubscriptionState nextState
+        @Nullable final BusinessSubscription previousSubscription,
+        @Nullable final Subscription.SubscriptionState nextState
     )
     {
         return new BusinessSubscriptionTransition(
@@ -174,33 +172,4 @@ public class TestAnalyticsListener
             true
         );
     }
-
-    private SubscriptionTransitionData createSubscriptionTransition(
-        final ApiEventType eventType,
-        final DateTime requestedTransitionTime,
-        final DateTime effectiveTransitionTime,
-        final Subscription.SubscriptionState previousState,
-        final Subscription.SubscriptionState nextState
-    )
-    {
-        return new SubscriptionTransitionData(
-            UUID.randomUUID(),
-            subscriptionId,
-            bundleUUID,
-            EntitlementEvent.EventType.API_USER,
-            eventType,
-            requestedTransitionTime,
-            effectiveTransitionTime,
-            previousState,
-            plan,
-            phase,
-            priceList,
-            nextState,
-            plan,
-            phase,
-            priceList,
-            1L,
-            true
-        );
-    }
 }
\ No newline at end of file
diff --git a/api/src/main/java/com/ning/billing/account/api/Account.java b/api/src/main/java/com/ning/billing/account/api/Account.java
index 9f5ad02..d93864a 100644
--- a/api/src/main/java/com/ning/billing/account/api/Account.java
+++ b/api/src/main/java/com/ning/billing/account/api/Account.java
@@ -17,15 +17,9 @@
 package com.ning.billing.account.api;
 
 import com.ning.billing.util.entity.UpdatableEntity;
-import org.joda.time.DateTime;
 
 import com.ning.billing.util.customfield.CustomizableEntity;
 import com.ning.billing.util.tag.Taggable;
-import org.skife.jdbi.v2.Update;
 
 public interface Account extends AccountData, CustomizableEntity, UpdatableEntity, Taggable {
-    public DateTime getCreatedDate();
-
-    public DateTime getUpdatedDate();
-
 }
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 9ee1920..d698374 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
@@ -18,14 +18,19 @@ package com.ning.billing.account.api;
 
 import java.util.List;
 import java.util.UUID;
+
+import com.ning.billing.util.CallContext;
 import com.ning.billing.util.customfield.CustomField;
 import com.ning.billing.util.tag.Tag;
 
-public interface AccountUserApi {
+import javax.annotation.Nullable;
 
-    public Account createAccount(AccountData data, List<CustomField> fields, List<Tag> tags) throws AccountApiException;
+public interface AccountUserApi {
+    public Account createAccount(AccountData data, @Nullable List<CustomField> fields,
+                                 @Nullable List<Tag> tags, CallContext context) throws AccountApiException;
 
-    public Account migrateAccount(MigrationAccountData data, List<CustomField> fields, List<Tag> tags) throws AccountApiException;
+    public Account migrateAccount(MigrationAccountData data, @Nullable List<CustomField> fields,
+                                  @Nullable List<Tag> tags, CallContext context) throws AccountApiException;
 
     /***
      *
@@ -33,9 +38,9 @@ public interface AccountUserApi {
      * @param account
      * @throws AccountApiException
      */
-    public void updateAccount(Account account) throws AccountApiException;
+    public void updateAccount(Account account, CallContext context) throws AccountApiException;
 
-    public void updateAccount(String key, AccountData accountData) throws AccountApiException;
+    public void updateAccount(String key, AccountData accountData, CallContext context) throws AccountApiException;
 
     public Account getAccountByKey(String key);
 
@@ -45,5 +50,5 @@ public interface AccountUserApi {
 
     public UUID getIdFromKey(String externalKey) throws AccountApiException;
 
-	public void deleteAccountByKey(String externalKey) throws AccountApiException;
+	public void deleteAccountByKey(String externalKey, CallContext context) throws AccountApiException;
 }
diff --git a/api/src/main/java/com/ning/billing/config/InvoiceConfig.java b/api/src/main/java/com/ning/billing/config/InvoiceConfig.java
index 78cc02f..f56d3c2 100644
--- a/api/src/main/java/com/ning/billing/config/InvoiceConfig.java
+++ b/api/src/main/java/com/ning/billing/config/InvoiceConfig.java
@@ -36,4 +36,8 @@ public interface InvoiceConfig {
     @Config("killbill.notifications.off")
     @Default("false")
     public boolean isEventProcessingOff();
+
+    @Config("killbill.invoice.maxNumberOfMonthsInFuture")
+    @Default("36")
+    public int getNumberOfMonthsInFuture();
 }
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
index 75337e3..08d649f 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.invoice.api;
 
+import com.ning.billing.util.CallContext;
 import org.joda.time.DateTime;
 
 import java.math.BigDecimal;
@@ -38,5 +39,5 @@ public interface InvoiceUserApi {
 
     public Collection<Invoice> getUnpaidInvoicesByAccountId(UUID accountId, DateTime upToDate);
     
-    public Invoice triggerInvoiceGeneration(UUID accountId, DateTime targetDate, boolean dryrun) throws InvoiceApiException;
+    public Invoice triggerInvoiceGeneration(UUID accountId, DateTime targetDate, boolean dryRun, CallContext context) throws InvoiceApiException;
 }
diff --git a/api/src/main/java/com/ning/billing/invoice/api/test/InvoiceTestApi.java b/api/src/main/java/com/ning/billing/invoice/api/test/InvoiceTestApi.java
index 9edcd8c..248284e 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/test/InvoiceTestApi.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/test/InvoiceTestApi.java
@@ -17,7 +17,8 @@
 package com.ning.billing.invoice.api.test;
 
 import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.util.CallContext;
 
 public interface InvoiceTestApi {
-    public void create(Invoice invoice);
+    public void create(Invoice invoice, CallContext context);
 }
diff --git a/api/src/main/java/com/ning/billing/util/api/TagDefinitionUserApi.java b/api/src/main/java/com/ning/billing/util/api/TagDefinitionUserApi.java
index 0bd985c..3c462ed 100644
--- a/api/src/main/java/com/ning/billing/util/api/TagDefinitionUserApi.java
+++ b/api/src/main/java/com/ning/billing/util/api/TagDefinitionUserApi.java
@@ -17,13 +17,14 @@
 package com.ning.billing.util.api;
 
 import java.util.List;
-import java.util.UUID;
+
+import com.ning.billing.util.CallContext;
 import com.ning.billing.util.tag.TagDefinition;
 
 public interface TagDefinitionUserApi {
     /***
      *
-     * @return
+     * @return the list of all available tag definitions
      */
     public List<TagDefinition> getTagDefinitions();
 
@@ -31,23 +32,25 @@ public interface TagDefinitionUserApi {
      *
      * @param name Identifies the definition.
      * @param description Describes the use of the definition.
-     * @param createdBy The name of person who created the definition.
-     * @return
+     * @param context The call context, for auditing purposes
+     * @return the newly created tag definition
      * @throws TagDefinitionApiException
      */
-    public TagDefinition create(String name, String description, String createdBy) throws TagDefinitionApiException;
+    public TagDefinition create(String name, String description, CallContext context) throws TagDefinitionApiException;
 
     /***
      *
      * @param definitionName Identifies the definition.
+     * @param context The call context, for auditing purposes
      * @throws TagDefinitionApiException
      */
-    public void deleteAllTagsForDefinition(String definitionName) throws TagDefinitionApiException;
+    public void deleteAllTagsForDefinition(String definitionName, CallContext context) throws TagDefinitionApiException;
 
     /***
      *
      * @param definitionName Identifies the definition.
+     * @param context The call context, for auditing purposes
      * @throws TagDefinitionApiException
      */
-    public void deleteTagDefinition(String definitionName) throws TagDefinitionApiException;
+    public void deleteTagDefinition(String definitionName, CallContext context) throws TagDefinitionApiException;
 }
diff --git a/api/src/main/java/com/ning/billing/util/CallContext.java b/api/src/main/java/com/ning/billing/util/CallContext.java
new file mode 100644
index 0000000..a76581d
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/CallContext.java
@@ -0,0 +1,10 @@
+package com.ning.billing.util;
+
+import org.joda.time.DateTime;
+
+public interface CallContext {
+    public String getUserName();
+    public CallOrigin getCallOrigin();
+    public UserType getUserType();
+    public DateTime getUTCNow();
+}
diff --git a/api/src/main/java/com/ning/billing/util/CallOrigin.java b/api/src/main/java/com/ning/billing/util/CallOrigin.java
new file mode 100644
index 0000000..74a02cf
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/CallOrigin.java
@@ -0,0 +1,7 @@
+package com.ning.billing.util;
+
+public enum CallOrigin {
+    INTERNAL,
+    EXTERNAL,
+    TEST
+}
diff --git a/api/src/main/java/com/ning/billing/util/customfield/CustomizableEntity.java b/api/src/main/java/com/ning/billing/util/customfield/CustomizableEntity.java
index e21fd83..97b354b 100644
--- a/api/src/main/java/com/ning/billing/util/customfield/CustomizableEntity.java
+++ b/api/src/main/java/com/ning/billing/util/customfield/CustomizableEntity.java
@@ -17,6 +17,8 @@
 package com.ning.billing.util.customfield;
 
 import java.util.List;
+
+import com.ning.billing.util.CallContext;
 import com.ning.billing.util.entity.Entity;
 
 public interface CustomizableEntity extends Entity {
@@ -24,11 +26,17 @@ public interface CustomizableEntity extends Entity {
 
     public void setFieldValue(String fieldName, String fieldValue);
 
+    public void saveFieldValue(String fieldName, String fieldValue, CallContext context);
+
     public List<CustomField> getFieldList();
 
-    public void addFields(List<CustomField> fields);
+    public void setFields(List<CustomField> fields);
+
+    public void saveFields(List<CustomField> fields, CallContext context);
 
     public void clearFields();
 
+    public void clearPersistedFields(CallContext context);
+
     public String getObjectName();
 }
diff --git a/api/src/main/java/com/ning/billing/util/entity/Entity.java b/api/src/main/java/com/ning/billing/util/entity/Entity.java
index f363534..3369538 100644
--- a/api/src/main/java/com/ning/billing/util/entity/Entity.java
+++ b/api/src/main/java/com/ning/billing/util/entity/Entity.java
@@ -16,8 +16,12 @@
 
 package com.ning.billing.util.entity;
 
+import org.joda.time.DateTime;
+
 import java.util.UUID;
 
-public interface Entity {
+public interface Entity<T> {
     public UUID getId();
+    public String getCreatedBy();
+    public DateTime getCreatedDate();
 }
diff --git a/api/src/main/java/com/ning/billing/util/entity/UpdatableEntity.java b/api/src/main/java/com/ning/billing/util/entity/UpdatableEntity.java
index 758d7dc..c860ddb 100644
--- a/api/src/main/java/com/ning/billing/util/entity/UpdatableEntity.java
+++ b/api/src/main/java/com/ning/billing/util/entity/UpdatableEntity.java
@@ -16,5 +16,9 @@
 
 package com.ning.billing.util.entity;
 
+import org.joda.time.DateTime;
+
 public interface UpdatableEntity extends Entity {
+    public String getUpdatedBy();
+    public DateTime getUpdatedDate();
 }
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 5e9008b..0a592cf 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
@@ -16,14 +16,8 @@
 
 package com.ning.billing.util.tag;
 
-import java.util.UUID;
-import org.joda.time.DateTime;
 import com.ning.billing.util.entity.Entity;
 
 public interface Tag extends Entity {
     String getTagDefinitionName();
-
-    String getAddedBy();
-
-    DateTime getAddedDate();
 }
diff --git a/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java b/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java
index f6c2388..408e09d 100644
--- a/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java
+++ b/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java
@@ -16,12 +16,10 @@
 
 package com.ning.billing.util.tag;
 
-import com.ning.billing.util.entity.UpdatableEntity;
+import com.ning.billing.util.entity.Entity;
 
-public interface TagDefinition extends UpdatableEntity {
+public interface TagDefinition extends Entity {
     String getName();
 
-    String getCreatedBy();
-
     String getDescription();
 }
diff --git a/api/src/main/java/com/ning/billing/util/tag/Taggable.java b/api/src/main/java/com/ning/billing/util/tag/Taggable.java
index 5e2f425..3b2e8b0 100644
--- a/api/src/main/java/com/ning/billing/util/tag/Taggable.java
+++ b/api/src/main/java/com/ning/billing/util/tag/Taggable.java
@@ -22,7 +22,7 @@ import org.joda.time.DateTime;
 public interface Taggable {
     public List<Tag> getTagList();
     public boolean hasTag(String tagName);
-    public void addTag(TagDefinition definition, String addedBy, DateTime dateAdded);
+    public void addTag(TagDefinition definition);
     public void addTags(List<Tag> tags);
     public void clearTags();
     public void removeTag(TagDefinition definition);
diff --git a/api/src/main/java/com/ning/billing/util/UserType.java b/api/src/main/java/com/ning/billing/util/UserType.java
new file mode 100644
index 0000000..5bc1175
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/UserType.java
@@ -0,0 +1,8 @@
+package com.ning.billing.util;
+
+public enum UserType {
+    SYSTEM,
+    ADMIN,
+    CUSTOMER,
+    TEST
+}
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 78655e1..ba70513 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
@@ -33,6 +33,10 @@ import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.CallOrigin;
+import com.ning.billing.util.UserType;
+import com.ning.billing.util.entity.DefaultCallContext;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.RandomStringUtils;
 import org.joda.time.DateTime;
@@ -53,7 +57,6 @@ import org.testng.annotations.BeforeSuite;
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
-
 import com.google.inject.Inject;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountData;
@@ -95,6 +98,7 @@ public class TestIntegration {
 
     @Inject
     private ClockMock clock;
+    private final CallContext context = new DefaultCallContext(clock, "Integration Test", CallOrigin.TEST, UserType.TEST);
 
     @Inject
     private Lifecycle lifecycle;
@@ -302,7 +306,7 @@ public class TestIntegration {
         int billingDay = 2;
 
         log.info("Beginning test with BCD of " + billingDay);
-        Account account = accountUserApi.createAccount(getAccountData(billingDay), null, null);
+        Account account = accountUserApi.createAccount(getAccountData(billingDay), null, null, context);
         UUID accountId = account.getId();
         assertNotNull(account);
 
@@ -368,7 +372,7 @@ public class TestIntegration {
                                       boolean proRationExpected) throws Exception {
 
         log.info("Beginning test with BCD of " + billingDay);
-        Account account = accountUserApi.createAccount(getAccountData(billingDay), null, null);
+        Account account = accountUserApi.createAccount(getAccountData(billingDay), null, null, context);
         UUID accountId = account.getId();
         assertNotNull(account);
 
@@ -560,7 +564,7 @@ public class TestIntegration {
 
     @Test(groups = "slow")
     public void testHappyPath() throws AccountApiException, EntitlementUserApiException {
-        Account account = accountUserApi.createAccount(getAccountData(3), null, null);
+        Account account = accountUserApi.createAccount(getAccountData(3), null, null, context);
         assertNotNull(account);
 
         SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever");
@@ -597,7 +601,7 @@ public class TestIntegration {
     public void testForMultipleRecurringPhases() throws AccountApiException, EntitlementUserApiException, InterruptedException {
         clock.setDeltaFromReality(new DateTime().getMillis() - clock.getUTCNow().getMillis());
 
-        Account account = accountUserApi.createAccount(getAccountData(15), null, null);
+        Account account = accountUserApi.createAccount(getAccountData(15), null, null, context);
         UUID accountId = account.getId();
 
         String productName = "Blowdart";
@@ -636,7 +640,7 @@ public class TestIntegration {
     protected AccountData getAccountData(final int billingDay) {
 
         final String someRandomKey = RandomStringUtils.randomAlphanumeric(10);
-        AccountData accountData = new AccountData() {
+        return new AccountData() {
             @Override
             public String getName() {
                 return "firstName lastName";
@@ -715,6 +719,5 @@ public class TestIntegration {
                 return null;
             }
         };
-        return accountData;
     }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java
index 7739ba7..467c435 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java
@@ -23,6 +23,11 @@ import java.util.TreeSet;
 import java.util.UUID;
 
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.CallOrigin;
+import com.ning.billing.util.UserType;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.entity.DefaultCallContext;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
@@ -52,25 +57,26 @@ import com.ning.billing.entitlement.api.user.SubscriptionTransition.Subscription
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.engine.dao.SubscriptionSqlDao;
 
-
 public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
 	private static final Logger log = LoggerFactory.getLogger(DefaultEntitlementBillingApi.class);
+    private static final String API_USER_NAME = "Entitlement Billing Api";
 
+    private final Clock clock;
     private final EntitlementDao entitlementDao;
     private final AccountUserApi accountApi;
     private final CatalogService catalogService;
 
     @Inject
-    public DefaultEntitlementBillingApi(final EntitlementDao dao, final AccountUserApi accountApi, final CatalogService catalogService) {
+    public DefaultEntitlementBillingApi(final Clock clock, final EntitlementDao dao, final AccountUserApi accountApi, final CatalogService catalogService) {
         super();
+        this.clock = clock;
         this.entitlementDao = dao;
         this.accountApi = accountApi;
         this.catalogService = catalogService;
     }
 
     @Override
-    public SortedSet<BillingEvent> getBillingEventsForAccount(
-            final UUID accountId) {
+    public SortedSet<BillingEvent> getBillingEventsForAccount(final UUID accountId) {
         Account account = accountApi.getAccountById(accountId);
         Currency currency = account.getCurrency();
 
@@ -101,7 +107,8 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
         return entitlementDao.getAccountIdFromSubscriptionId(subscriptionId);
     }
 
-    private int calculateBcd(SubscriptionBundle bundle, Subscription subscription, final SubscriptionTransition transition, final UUID accountId) throws CatalogApiException, AccountApiException {
+    private int calculateBcd(final SubscriptionBundle bundle, final Subscription subscription,
+                             final SubscriptionTransition transition, final UUID accountId) throws CatalogApiException, AccountApiException {
     	Catalog catalog = catalogService.getFullCatalog();
     	Plan plan =  (transition.getTransitionType() != SubscriptionTransitionType.CANCEL) ?
     	        transition.getNextPlan() : transition.getPreviousPlan();
@@ -124,7 +131,9 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
     			result = account.getBillCycleDay();
     			
     			if(result == 0) {
-    				result = calculateBcdFromSubscription(subscription, plan, account);
+                    // in this case, we're making an internal call from the entitlement API to set the BCD for the account
+                    CallContext context = new DefaultCallContext(clock, API_USER_NAME, CallOrigin.INTERNAL, UserType.SYSTEM);
+    				result = calculateBcdFromSubscription(subscription, plan, account, context);
     			}
     		break;
     		case BUNDLE :
@@ -141,7 +150,8 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
 
     }
     
-   	private int calculateBcdFromSubscription(Subscription subscription, Plan plan, Account account) throws AccountApiException {
+   	private int calculateBcdFromSubscription(Subscription subscription, Plan plan, Account account,
+                                             final CallContext context) throws AccountApiException {
 		int result = account.getBillCycleDay();
         if(result != 0) {
             return result;
@@ -153,7 +163,6 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
         } catch (CatalogApiException e) {
             log.error("Unexpected catalog error encountered when updating BCD",e);
         }
-        
 
         Account modifiedAccount = new DefaultAccount(
                 account.getId(),
@@ -174,10 +183,8 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
                 account.getCountry(),
                 account.getPostalCode(),
                 account.getPhone(),
-                account.getCreatedDate(),
-                null // Updated date will be set internally
-        );
-        accountApi.updateAccount(modifiedAccount);
+                null, null, null, null);
+        accountApi.updateAccount(modifiedAccount, context);
         return result;
     }
 
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
index 9899990..9bd7dd5 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
@@ -29,6 +29,7 @@ import com.ning.billing.entitlement.events.phase.PhaseEvent;
 import com.ning.billing.entitlement.events.phase.PhaseEventData;
 import com.ning.billing.entitlement.events.user.*;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
+import com.ning.billing.util.CallContext;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.DefaultClock;
 import org.joda.time.DateTime;
@@ -52,8 +53,6 @@ public class SubscriptionApiService {
         this.dao = dao;
     }
 
-
-
     public SubscriptionData createPlan(SubscriptionBuilder builder, Plan plan, PhaseType initialPhase,
             String realPriceList, DateTime requestedDate, DateTime effectiveDate, DateTime processedDate)
         throws EntitlementUserApiException {
@@ -281,8 +280,8 @@ public class SubscriptionApiService {
         }
     }
 
-    public void commitCustomFields(SubscriptionData subscription) {
-        dao.saveCustomFields(subscription);
+    public void commitCustomFields(SubscriptionData subscription, CallContext context) {
+        dao.saveCustomFields(subscription, context);
     }
 
     private void validateRequestedDate(SubscriptionData subscription, DateTime now, DateTime requestedDate)
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
index 138528d..823f0eb 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
@@ -31,6 +31,7 @@ import com.ning.billing.entitlement.events.phase.PhaseEvent;
 import com.ning.billing.entitlement.events.user.ApiEvent;
 import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
+import com.ning.billing.util.CallContext;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.customfield.CustomField;
 import com.ning.billing.util.customfield.CustomizableEntityBase;
@@ -39,6 +40,7 @@ import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.annotation.Nullable;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
@@ -79,8 +81,9 @@ public class SubscriptionData extends CustomizableEntityBase implements Subscrip
         this(builder, null, null);
     }
 
-    public SubscriptionData(SubscriptionBuilder builder, SubscriptionApiService apiService, Clock clock) {
-        super(builder.getId());
+    public SubscriptionData(SubscriptionBuilder builder, @Nullable SubscriptionApiService apiService,
+                            @Nullable Clock clock) {
+        super(builder.getId(), null, null);
         this.apiService = apiService;
         this.clock = clock;
         this.bundleId = builder.getBundleId();
@@ -97,57 +100,34 @@ public class SubscriptionData extends CustomizableEntityBase implements Subscrip
         return "Subscription";
     }
 
-
     @Override
-    public void setFieldValue(String fieldName, String fieldValue) {
-        setFieldValueInternal(fieldName, fieldValue, true);
-    }
-
-    public void setFieldValueInternal(String fieldName, String fieldValue, boolean commit) {
+    public void saveFieldValue(String fieldName, String fieldValue, CallContext context) {
         super.setFieldValue(fieldName, fieldValue);
-        if (commit) {
-            apiService.commitCustomFields(this);
-        }
+        apiService.commitCustomFields(this, context);
     }
 
-
     @Override
-    public void addFields(List<CustomField> fields) {
-        addFieldsInternal(fields, true);
-    }
-
-    public void addFieldsInternal(List<CustomField> fields, boolean commit) {
-        super.addFields(fields);
-        if (commit) {
-            apiService.commitCustomFields(this);
-        }
+    public void saveFields(List<CustomField> fields, CallContext context) {
+        super.setFields(fields);
+        apiService.commitCustomFields(this, context);
     }
 
     @Override
-    public void clearFields() {
-        clearFieldsInternal(true);
-    }
-
-    public void clearFieldsInternal(boolean commit) {
+    public void clearPersistedFields(CallContext context) {
         super.clearFields();
-        if (commit) {
-            apiService.commitCustomFields(this);
-        }
+        apiService.commitCustomFields(this, context);
     }
 
-
     @Override
     public UUID getBundleId() {
         return bundleId;
     }
 
-
     @Override
     public DateTime getStartDate() {
         return startDate;
     }
 
-
     @Override
     public SubscriptionState getState() {
         return (getPreviousTransition() == null) ? null : getPreviousTransition().getNextState();
@@ -336,7 +316,6 @@ public class SubscriptionData extends CustomizableEntityBase implements Subscrip
         return false;
     }
 
-
     public DateTime getPlanChangeEffectiveDate(ActionPolicy policy, DateTime requestedDate) {
 
         if (policy == ActionPolicy.IMMEDIATE) {
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 d2b9f11..88123ca 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
@@ -18,8 +18,9 @@ package com.ning.billing.entitlement.engine.dao;
 
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
+import com.ning.billing.util.entity.BinderBase;
+import com.ning.billing.util.entity.MapperBase;
 import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
@@ -35,8 +36,6 @@ import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.sql.Timestamp;
-import java.util.Date;
 import java.util.List;
 import java.util.UUID;
 
@@ -61,12 +60,7 @@ public interface BundleSqlDao extends Transactional<BundleSqlDao>, CloseMe, Tran
     @Mapper(ISubscriptionBundleSqlMapper.class)
     public List<SubscriptionBundle> getBundleFromAccount(@Bind("account_id") String accountId);
 
-    public static class SubscriptionBundleBinder implements Binder<Bind, SubscriptionBundleData> {
-
-        private Date getDate(DateTime dateTime) {
-            return dateTime == null ? null : dateTime.toDate();
-        }
-
+    public static class SubscriptionBundleBinder extends BinderBase implements Binder<Bind, SubscriptionBundleData> {
         @Override
         public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, SubscriptionBundleData bundle) {
             stmt.bind("id", bundle.getId().toString());
@@ -76,13 +70,7 @@ public interface BundleSqlDao extends Transactional<BundleSqlDao>, CloseMe, Tran
         }
     }
 
-    public static class ISubscriptionBundleSqlMapper implements ResultSetMapper<SubscriptionBundle> {
-
-        private DateTime getDate(ResultSet r, String fieldName) throws SQLException {
-            final Timestamp resultStamp = r.getTimestamp(fieldName);
-            return r.wasNull() ? null : new DateTime(resultStamp).toDateTime(DateTimeZone.UTC);
-        }
-
+    public static class ISubscriptionBundleSqlMapper extends MapperBase implements ResultSetMapper<SubscriptionBundle> {
         @Override
         public SubscriptionBundle map(int arg, ResultSet r,
                 StatementContext ctx) throws SQLException {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
index fba1b3d..8a72ca4 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
@@ -19,22 +19,16 @@ package com.ning.billing.entitlement.engine.dao;
 import java.util.List;
 import java.util.UUID;
 
-import org.joda.time.DateTime;
+import com.ning.billing.util.CallContext;
 
-import com.ning.billing.account.api.Account;
-import com.ning.billing.account.dao.AccountSqlDao;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.events.EntitlementEvent;
-import com.ning.billing.util.customfield.CustomField;
-import com.ning.billing.util.customfield.dao.FieldStoreDao;
 
 public interface EntitlementDao {
-
-
     // Bundle apis
     public List<SubscriptionBundle> getSubscriptionBundleForAccount(UUID accountId);
 
@@ -79,11 +73,11 @@ public interface EntitlementDao {
 
     public void changePlan(UUID subscriptionId, List<EntitlementEvent> changeEvents);
 
-    public void migrate(UUID acountId, AccountMigrationData data);
+    public void migrate(UUID accountId, AccountMigrationData data);
 
     public void undoMigration(UUID accountId);
 
     // Custom Fields
-    public void saveCustomFields(SubscriptionData subscription);
+    public void saveCustomFields(SubscriptionData subscription, CallContext context);
 }
 
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
index cd4267e..356879c 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
@@ -24,6 +24,8 @@ import java.util.Date;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.customfield.dao.AuditedCustomFieldDao;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
@@ -36,10 +38,7 @@ import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.inject.Inject;
 import com.ning.billing.ErrorCode;
-import com.ning.billing.account.api.Account;
-import com.ning.billing.account.dao.AccountSqlDao;
 import com.ning.billing.catalog.api.Plan;
-import com.ning.billing.catalog.api.Product;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
@@ -62,7 +61,7 @@ import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.customfield.CustomField;
-import com.ning.billing.util.customfield.dao.FieldStoreDao;
+import com.ning.billing.util.customfield.dao.CustomFieldSqlDao;
 import com.ning.billing.util.notificationq.NotificationKey;
 import com.ning.billing.util.notificationq.NotificationQueue;
 import com.ning.billing.util.notificationq.NotificationQueueService;
@@ -408,18 +407,11 @@ public class EntitlementSqlDao implements EntitlementDao {
         }
     }
 
-    private void updateCustomFieldsFromTransaction(SubscriptionSqlDao transactionalDao, final SubscriptionData subscription) {
-
-        String subscriptionId = subscription.getId().toString();
-        String objectType = subscription.getObjectName();
-
-        FieldStoreDao fieldStoreDao = transactionalDao.become(FieldStoreDao.class);
-        fieldStoreDao.clear(subscriptionId, objectType);
-
-        List<CustomField> fieldList = subscription.getFieldList();
-        if (fieldList != null) {
-            fieldStoreDao.batchSaveFromTransaction(subscriptionId, objectType, fieldList);
-        }
+    private void updateCustomFieldsFromTransaction(final SubscriptionSqlDao transactionalDao,
+                                                   final SubscriptionData subscription,
+                                                   final CallContext context) {
+        AuditedCustomFieldDao auditedDao = new AuditedCustomFieldDao();
+        auditedDao.saveFields(transactionalDao, subscription.getId(), subscription.getObjectName(), subscription.getFieldList(), context);
     }
 
     private Subscription buildSubscription(Subscription input) {
@@ -444,10 +436,7 @@ public class EntitlementSqlDao implements EntitlementDao {
          throw new EntitlementError(String.format("Unexpected code path in buildSubscription"));
     }
 
-
-
     private List<Subscription> buildBundleSubscriptions(List<Subscription> input) {
-
         // Make sure BasePlan -- if exists-- is first
         Collections.sort(input, new Comparator<Subscription>() {
             @Override
@@ -607,23 +596,23 @@ public class EntitlementSqlDao implements EntitlementDao {
     }
 
     @Override
-    public void saveCustomFields(final SubscriptionData subscription) {
+    public void saveCustomFields(final SubscriptionData subscription, final CallContext context) {
         subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
             @Override
             public Void inTransaction(SubscriptionSqlDao transactionalDao,
                     TransactionStatus status) throws Exception {
-                updateCustomFieldsFromTransaction(transactionalDao, subscription);
+                updateCustomFieldsFromTransaction(transactionalDao, subscription, context);
                 return null;
             }
         });
     }
 
     private void loadCustomFields(final SubscriptionData subscription) {
-        FieldStoreDao fieldStoreDao = subscriptionsDao.become(FieldStoreDao.class);
-        List<CustomField> fields = fieldStoreDao.load(subscription.getId().toString(), subscription.getObjectName());
-        subscription.clearFieldsInternal(false);
+        CustomFieldSqlDao customFieldSqlDao = subscriptionsDao.become(CustomFieldSqlDao.class);
+        List<CustomField> fields = customFieldSqlDao.load(subscription.getId().toString(), subscription.getObjectName());
+        subscription.clearFields();
         if (fields != null) {
-            subscription.addFieldsInternal(fields, false);
+            subscription.setFields(fields);
         }
     }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
index 203877f..5a7681e 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
@@ -24,8 +24,9 @@ import com.ning.billing.entitlement.events.phase.PhaseEventBuilder;
 import com.ning.billing.entitlement.events.phase.PhaseEventData;
 import com.ning.billing.entitlement.events.user.*;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
+import com.ning.billing.util.entity.BinderBase;
+import com.ning.billing.util.entity.MapperBase;
 import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
@@ -40,7 +41,6 @@ import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTempla
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.sql.Timestamp;
 import java.util.Date;
 import java.util.List;
 import java.util.UUID;
@@ -72,12 +72,7 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
     @Mapper(EventSqlMapper.class)
     public List<EntitlementEvent> getEventsForSubscription(@Bind("subscription_id") String subscriptionId);
 
-    public static class EventSqlDaoBinder implements Binder<Bind, EntitlementEvent> {
-
-        private Date getDate(DateTime dateTime) {
-            return dateTime == null ? null : dateTime.toDate();
-        }
-
+    public static class EventSqlDaoBinder extends BinderBase implements Binder<Bind, EntitlementEvent> {
         @Override
         public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, EntitlementEvent evt) {
             stmt.bind("event_id", evt.getId().toString());
@@ -96,13 +91,7 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
         }
     }
 
-    public static class EventSqlMapper implements ResultSetMapper<EntitlementEvent> {
-
-        private DateTime getDate(ResultSet r, String fieldName) throws SQLException {
-            final Timestamp resultStamp = r.getTimestamp(fieldName);
-            return r.wasNull() ? null : new DateTime(resultStamp).toDateTime(DateTimeZone.UTC);
-        }
-
+    public static class EventSqlMapper extends MapperBase implements ResultSetMapper<EntitlementEvent> {
         @Override
         public EntitlementEvent map(int index, ResultSet r, StatementContext ctx)
         throws SQLException {
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 7b2ddcc..64e81e1 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
@@ -20,6 +20,8 @@ import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.util.entity.BinderBase;
+import com.ning.billing.util.entity.MapperBase;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.skife.jdbi.v2.SQLStatement;
@@ -64,12 +66,7 @@ public interface SubscriptionSqlDao extends Transactional<SubscriptionSqlDao>, C
     @SqlUpdate
     public void updateSubscription(@Bind("id") String id, @Bind("active_version") long activeVersion, @Bind("ctd_dt") Date ctd, @Bind("ptd_dt") Date ptd);
    
-    public static class ISubscriptionDaoBinder implements Binder<Bind, SubscriptionData> {
-
-        private Date getDate(DateTime dateTime) {
-            return dateTime == null ? null : dateTime.toDate();
-        }
-
+    public static class ISubscriptionDaoBinder extends BinderBase implements Binder<Bind, SubscriptionData> {
         @Override
         public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, SubscriptionData sub) {
             stmt.bind("id", sub.getId().toString());
@@ -83,13 +80,7 @@ public interface SubscriptionSqlDao extends Transactional<SubscriptionSqlDao>, C
         }
     }
 
-    public static class ISubscriptionDaoSqlMapper implements ResultSetMapper<SubscriptionData> {
-
-        private DateTime getDate(ResultSet r, String fieldName) throws SQLException {
-            final Timestamp resultStamp = r.getTimestamp(fieldName);
-            return r.wasNull() ? null : new DateTime(resultStamp).toDateTime(DateTimeZone.UTC);
-        }
-
+    public static class ISubscriptionDaoSqlMapper extends MapperBase implements ResultSetMapper<SubscriptionData> {
         @Override
         public SubscriptionData map(int arg0, ResultSet r, StatementContext ctx)
                 throws SQLException {
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccount.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccount.java
index 73b7369..41de5c0 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccount.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccount.java
@@ -19,6 +19,7 @@ package com.ning.billing.entitlement.api.billing;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.CallContext;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 
@@ -37,7 +38,8 @@ public class BrainDeadAccount implements Account {
 
 	@Override
 	public String getName() {
-		throw new UnsupportedOperationException();	}
+		throw new UnsupportedOperationException();
+    }
 
 	@Override
 	public int getFirstNameLength() {
@@ -124,17 +126,37 @@ public class BrainDeadAccount implements Account {
 		throw new UnsupportedOperationException();
 	}
 
-	@Override
+    @Override
+    public void saveFieldValue(String fieldName, String fieldValue, CallContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
 	public List<CustomField> getFieldList() {
 		throw new UnsupportedOperationException();
 	}
 
-	@Override
+    @Override
+    public void setFields(List<CustomField> fields) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void saveFields(List<CustomField> fields, CallContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
 	public void clearFields() {
 		throw new UnsupportedOperationException();
 	}
 
-	@Override
+    @Override
+    public void clearPersistedFields(CallContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
 	public String getObjectName() {
 		throw new UnsupportedOperationException();
 	}
@@ -144,7 +166,17 @@ public class BrainDeadAccount implements Account {
 		throw new UnsupportedOperationException();
 	}
 
-	@Override
+    @Override
+    public String getCreatedBy() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public DateTime getCreatedDate() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
 	public List<Tag> getTagList() {
 		throw new UnsupportedOperationException();
 	}
@@ -155,7 +187,7 @@ public class BrainDeadAccount implements Account {
 	}
 
     @Override
-    public void addTag(final TagDefinition definition, final String addedBy, final DateTime dateAdded) {
+    public void addTag(final TagDefinition definition) {
         throw new UnsupportedOperationException();
     }
 
@@ -183,20 +215,14 @@ public class BrainDeadAccount implements Account {
 	public boolean processPayment() {
 		throw new UnsupportedOperationException();
 	}
-	@Override
-	public void addFields(List<CustomField> fields) {
-		throw new UnsupportedOperationException();
-
-	}
 
     @Override
-    public DateTime getCreatedDate() {
-        return new DateTime(DateTimeZone.UTC);
+    public String getUpdatedBy() {
+        throw new UnsupportedOperationException();
     }
 
     @Override
     public DateTime getUpdatedDate() {
-        return new DateTime(DateTimeZone.UTC);
+        throw new UnsupportedOperationException();
     }
-
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccountUserApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccountUserApi.java
index 4602fe6..bb2f60c 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccountUserApi.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccountUserApi.java
@@ -24,12 +24,11 @@ import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.account.api.MigrationAccountData;
+import com.ning.billing.util.CallContext;
 import com.ning.billing.util.customfield.CustomField;
 import com.ning.billing.util.tag.Tag;
 
 public class BrainDeadAccountUserApi implements AccountUserApi {
-
-
 	@Override
 	public Account getAccountByKey(String key) {
 		throw new UnsupportedOperationException();
@@ -52,30 +51,30 @@ public class BrainDeadAccountUserApi implements AccountUserApi {
 
 	@Override
 	public Account createAccount(AccountData data, List<CustomField> fields,
-			List<Tag> tags) throws AccountApiException {
+			List<Tag> tags, CallContext context) throws AccountApiException {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
-	public void updateAccount(Account account) {
+	public void updateAccount(Account account, CallContext context) {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
-	public void deleteAccountByKey(String externalKey)
+	public void deleteAccountByKey(String externalKey, CallContext context)
 			throws AccountApiException {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
 	public Account migrateAccount(MigrationAccountData data,
-			List<CustomField> fields, List<Tag> tags)
+			List<CustomField> fields, List<Tag> tags, CallContext context)
 			throws AccountApiException {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
-	public void updateAccount(String key, AccountData accountData)
+	public void updateAccount(String key, AccountData accountData, CallContext context)
 			throws AccountApiException {
 		throw new UnsupportedOperationException();
 	}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
index 513a5ca..0f3c37d 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
@@ -28,7 +28,6 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.testng.Assert;
-import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.BeforeSuite;
 import org.testng.annotations.Test;
@@ -135,7 +134,7 @@ public class TestDefaultEntitlementBillingApi {
 		AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
         ((ZombieControl) accountApi).addResult("getAccountById", account);
 
-		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao, accountApi, catalogService);
+		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(clock, dao, accountApi, catalogService);
 		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
 		Assert.assertEquals(events.size(), 0);
 	}
@@ -167,7 +166,7 @@ public class TestDefaultEntitlementBillingApi {
                     }
                 };
 			}} ;
-		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
+		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(clock, dao,accountApi,catalogService);
 		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
 		checkFirstEvent(events, nextPlan, 32, oneId, now, nextPhase, ApiEventType.CREATE.toString());
 	}
@@ -191,7 +190,7 @@ public class TestDefaultEntitlementBillingApi {
 		AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
 		((ZombieControl)accountApi).addResult("getAccountById", account);
 
-		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
+		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(clock, dao, accountApi, catalogService);
 		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
 		checkFirstEvent(events, nextPlan, subscription.getStartDate().getDayOfMonth(), oneId, now, nextPhase, ApiEventType.CREATE.toString());
 	}
@@ -223,7 +222,7 @@ public class TestDefaultEntitlementBillingApi {
                     }
                 };
 			}} ;
-		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
+		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(clock, dao, accountApi, catalogService);
 		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
 		checkFirstEvent(events, nextPlan, 32, oneId, now, nextPhase, ApiEventType.CREATE.toString());
 	}
@@ -246,7 +245,7 @@ public class TestDefaultEntitlementBillingApi {
 		AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
 		((ZombieControl)accountApi).addResult("getAccountById", account);
 
-		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
+		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(clock, dao, accountApi, catalogService);
 		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
 		checkFirstEvent(events, nextPlan, bundles.get(0).getStartDate().getDayOfMonth(), oneId, now, nextPhase, ApiEventType.CREATE.toString());
 	}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
index 094faba..3d553bd 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
@@ -74,6 +74,8 @@ import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.bus.DefaultBusService;
 import com.ning.billing.util.bus.BusService;
 
+import javax.annotation.Nullable;
+
 
 public abstract class TestApiBase {
 
@@ -401,7 +403,9 @@ public abstract class TestApiBase {
         return accountData;
     }
 
-    protected PlanPhaseSpecifier getProductSpecifier(final String productName, final String priceList, final BillingPeriod term, final PhaseType phaseType) {
+    protected PlanPhaseSpecifier getProductSpecifier(final String productName, final String priceList,
+                                                     final BillingPeriod term,
+                                                     @Nullable final PhaseType phaseType) {
         return new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, priceList, phaseType);
     }
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserCustomFieldsSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserCustomFieldsSql.java
index 788cf2d..65d3cac 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserCustomFieldsSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserCustomFieldsSql.java
@@ -21,6 +21,11 @@ import java.util.List;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.CallOrigin;
+import com.ning.billing.util.UserType;
+import com.ning.billing.util.clock.DefaultClock;
+import com.ning.billing.util.entity.DefaultCallContext;
 import org.joda.time.DateTime;
 import org.testng.Assert;
 import org.testng.annotations.Test;
@@ -37,14 +42,14 @@ import com.ning.billing.util.customfield.CustomField;
 
 
 public class TestUserCustomFieldsSql extends TestApiBase {
+    private static final String USER_NAME = "Entitlement Test";
+    private final CallContext context = new DefaultCallContext(new DefaultClock(), USER_NAME, CallOrigin.TEST, UserType.TEST);
 
     @Override
     protected Injector getInjector() {
         return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
     }
 
-
-
     @Test(enabled=false, groups={"slow"})
     public void stress() {
         cleanupTest();
@@ -79,12 +84,12 @@ public class TestUserCustomFieldsSql extends TestApiBase {
 
             assertEquals(subscription.getFieldValue("nonExistent"), null);
 
-            subscription.setFieldValue("field1", "value1");
+            subscription.saveFieldValue("field1", "value1", context);
             assertEquals(subscription.getFieldValue("field1"), "value1");
             List<CustomField> allFields = subscription.getFieldList();
             assertEquals(allFields.size(), 1);
 
-            subscription.setFieldValue("field1", "valueNew1");
+            subscription.saveFieldValue("field1", "valueNew1", context);
             assertEquals(subscription.getFieldValue("field1"), "valueNew1");
             allFields = subscription.getFieldList();
             assertEquals(allFields.size(), 1);
@@ -94,7 +99,7 @@ public class TestUserCustomFieldsSql extends TestApiBase {
             allFields = subscription.getFieldList();
             assertEquals(allFields.size(), 1);
 
-            subscription.setFieldValue("field1", "valueSuperNew1");
+            subscription.saveFieldValue("field1", "valueSuperNew1", context);
             assertEquals(subscription.getFieldValue("field1"), "valueSuperNew1");
             allFields = subscription.getFieldList();
             assertEquals(allFields.size(), 1);
@@ -104,14 +109,11 @@ public class TestUserCustomFieldsSql extends TestApiBase {
             allFields = subscription.getFieldList();
             assertEquals(allFields.size(), 1);
 
-            /*
-             * BROKEN
-            subscription.setFieldValue("field1", null);
+            subscription.saveFieldValue("field1", null, context);
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
             assertEquals(subscription.getFieldValue("field1"), null);
             allFields = subscription.getFieldList();
             assertEquals(allFields.size(), 1);
-             */
         } catch (EntitlementUserApiException e) {
             log.error("Unexpected exception",e);
             Assert.fail(e.getMessage());
@@ -137,7 +139,7 @@ public class TestUserCustomFieldsSql extends TestApiBase {
             assertNotNull(subscription);
 
 
-            subscription.setFieldValue("field1", "value1");
+            subscription.saveFieldValue("field1", "value1", context);
             assertEquals(subscription.getFieldValue("field1"), "value1");
             List<CustomField> allFields = subscription.getFieldList();
             assertEquals(allFields.size(), 1);
@@ -148,8 +150,8 @@ public class TestUserCustomFieldsSql extends TestApiBase {
 
             subscription.clearFields();
 
-            subscription.setFieldValue("field2", "value2");
-            subscription.setFieldValue("field3", "value3");
+            subscription.saveFieldValue("field2", "value2", context);
+            subscription.saveFieldValue("field3", "value3", context);
             assertEquals(subscription.getFieldValue("field2"), "value2");
             assertEquals(subscription.getFieldValue("field3"), "value3");
             allFields = subscription.getFieldList();
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 085afb9..59d5607 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
@@ -24,6 +24,7 @@ import java.util.List;
 import java.util.TreeSet;
 import java.util.UUID;
 
+import com.ning.billing.util.CallContext;
 import org.apache.commons.lang.NotImplementedException;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
@@ -441,17 +442,16 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
 
     private void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao, final DateTime effectiveDate, final NotificationKey notificationKey) {
         try {
-            NotificationQueue subscritionEventQueue = notificationQueueService.getNotificationQueue(Engine.ENTITLEMENT_SERVICE_NAME,
+            NotificationQueue subscriptionEventQueue = notificationQueueService.getNotificationQueue(Engine.ENTITLEMENT_SERVICE_NAME,
                 Engine.NOTIFICATION_QUEUE_NAME);
-            subscritionEventQueue.recordFutureNotificationFromTransaction(transactionalDao, effectiveDate, notificationKey);
+            subscriptionEventQueue.recordFutureNotificationFromTransaction(transactionalDao, effectiveDate, notificationKey);
         } catch (NoSuchNotificationQueue e) {
             throw new RuntimeException(e);
         }
     }
 
     @Override
-    public void saveCustomFields(SubscriptionData subscription) {
+    public void saveCustomFields(SubscriptionData subscription, CallContext context) {
         throw new NotImplementedException();
     }
-
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/test/DefaultInvoiceTestApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/test/DefaultInvoiceTestApi.java
index bbec57f..d2216a7 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/test/DefaultInvoiceTestApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/test/DefaultInvoiceTestApi.java
@@ -19,6 +19,7 @@ package com.ning.billing.invoice.api.test;
 import com.google.inject.Inject;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.util.CallContext;
 
 public class DefaultInvoiceTestApi implements InvoiceTestApi {
     private final InvoiceDao invoiceDao;
@@ -29,7 +30,7 @@ public class DefaultInvoiceTestApi implements InvoiceTestApi {
     }
 
     @Override
-    public void create(Invoice invoice) {
-        invoiceDao.create(invoice);
+    public void create(final Invoice invoice, final CallContext context) {
+        invoiceDao.create(invoice, context);
     }
 }
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 1800154..87d3d0c 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
@@ -20,6 +20,7 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.CallContext;
 import org.joda.time.DateTime;
 
 import com.google.inject.Inject;
@@ -77,8 +78,9 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
     }
 
 	@Override
-	public Invoice triggerInvoiceGeneration(UUID accountId,
-			DateTime targetDate, boolean dryrun) throws InvoiceApiException {
-		return dispatcher.processAccount(accountId, targetDate, dryrun);
+	public Invoice triggerInvoiceGeneration(final UUID accountId,
+			final DateTime targetDate, final boolean dryRun,
+            final CallContext context) throws InvoiceApiException {
+		return dispatcher.processAccount(accountId, targetDate, dryRun, context);
 	}
 }
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
index f797ff5..c250b7f 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
@@ -17,13 +17,13 @@
 package com.ning.billing.invoice.dao;
 
 import java.math.BigDecimal;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import com.ning.billing.util.CallContext;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
@@ -47,8 +47,6 @@ public class DefaultInvoiceDao implements InvoiceDao {
     private final static Logger log = LoggerFactory.getLogger(DefaultInvoiceDao.class);
 
     private final InvoiceSqlDao invoiceSqlDao;
-    private final RecurringInvoiceItemSqlDao recurringInvoiceItemSqlDao;
-    private final FixedPriceInvoiceItemSqlDao fixedPriceInvoiceItemSqlDao;
     private final InvoicePaymentSqlDao invoicePaymentSqlDao;
     private final EntitlementBillingApi entitlementBillingApi;
 
@@ -61,8 +59,6 @@ public class DefaultInvoiceDao implements InvoiceDao {
                              final EntitlementBillingApi entitlementBillingApi,
                              NextBillingDatePoster nextBillingDatePoster) {
         this.invoiceSqlDao = dbi.onDemand(InvoiceSqlDao.class);
-        this.recurringInvoiceItemSqlDao = dbi.onDemand(RecurringInvoiceItemSqlDao.class);
-        this.fixedPriceInvoiceItemSqlDao = dbi.onDemand(FixedPriceInvoiceItemSqlDao.class);
         this.invoicePaymentSqlDao = dbi.onDemand(InvoicePaymentSqlDao.class);
         this.eventBus = eventBus;
         this.entitlementBillingApi = entitlementBillingApi;
@@ -132,7 +128,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public void create(final Invoice invoice) {
+    public void create(final Invoice invoice, final CallContext context) {
         invoiceSqlDao.inTransaction(new Transaction<Void, InvoiceSqlDao>() {
             @Override
             public Void inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
@@ -141,7 +137,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
                 Invoice currentInvoice = invoiceDao.getById(invoice.getId().toString());
 
                 if (currentInvoice == null) {
-                    invoiceDao.create(invoice);
+                    invoiceDao.create(invoice, context);
 
                     List<InvoiceItem> recurringInvoiceItems = invoice.getInvoiceItems(RecurringInvoiceItem.class);
                     RecurringInvoiceItemSqlDao recurringInvoiceItemDao = invoiceDao.become(RecurringInvoiceItemSqlDao.class);
@@ -151,7 +147,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
 
                     List<InvoiceItem> fixedPriceInvoiceItems = invoice.getInvoiceItems(FixedPriceInvoiceItem.class);
                     FixedPriceInvoiceItemSqlDao fixedPriceInvoiceItemDao = invoiceDao.become(FixedPriceInvoiceItemSqlDao.class);
-                    fixedPriceInvoiceItemDao.batchCreateFromTransaction(fixedPriceInvoiceItems);
+                    fixedPriceInvoiceItemDao.batchCreateFromTransaction(fixedPriceInvoiceItems, context);
 
                     setChargedThroughDates(invoiceSqlDao, fixedPriceInvoiceItems, recurringInvoiceItems);
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.java
index 6fdd74d..7a43cc1 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.java
@@ -19,6 +19,8 @@ package com.ning.billing.invoice.dao;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.entity.CallContextBinder;
 import com.ning.billing.util.entity.EntityDao;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.SQLStatement;
@@ -59,13 +61,13 @@ public interface FixedPriceInvoiceItemSqlDao extends EntityDao<InvoiceItem> {
 
     @Override
     @SqlUpdate
-    void create(@FixedPriceInvoiceItemBinder final InvoiceItem invoiceItem);
+    void create(@FixedPriceInvoiceItemBinder final InvoiceItem invoiceItem, @CallContextBinder final CallContext context);
 
     @SqlBatch
-    void create(@FixedPriceInvoiceItemBinder final List<InvoiceItem> items);
+    void create(@FixedPriceInvoiceItemBinder final List<InvoiceItem> items, @CallContextBinder final CallContext context);
 
     @SqlBatch(transactional=false)
-    void batchCreateFromTransaction(@FixedPriceInvoiceItemBinder final List<InvoiceItem> items);
+    void batchCreateFromTransaction(@FixedPriceInvoiceItemBinder final List<InvoiceItem> items, @CallContextBinder final CallContext context);
 
     @BindingAnnotation(FixedPriceInvoiceItemBinder.FixedPriceInvoiceItemBinderFactory.class)
     @Retention(RetentionPolicy.RUNTIME)
@@ -103,10 +105,11 @@ public interface FixedPriceInvoiceItemSqlDao extends EntityDao<InvoiceItem> {
             DateTime endDate = new DateTime(result.getTimestamp("end_date"));
             BigDecimal amount = result.getBigDecimal("amount");
             Currency currency = Currency.valueOf(result.getString("currency"));
+            String createdBy = result.getString("created_by");
             DateTime createdDate = new DateTime(result.getTimestamp("created_date"));
 
             return new FixedPriceInvoiceItem(id, invoiceId, subscriptionId, planName, phaseName,
-                                            startDate, endDate, amount, currency, createdDate);
+                                            startDate, endDate, amount, currency, createdBy, createdDate);
         }
     }
 }
\ No newline at end of file
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 08f6e06..1c7932f 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
@@ -19,6 +19,7 @@ package com.ning.billing.invoice.dao;
 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.util.CallContext;
 import org.joda.time.DateTime;
 
 import java.math.BigDecimal;
@@ -26,7 +27,7 @@ import java.util.List;
 import java.util.UUID;
 
 public interface InvoiceDao {
-    void create(Invoice invoice);
+    void create(Invoice invoice, CallContext context);
 
     Invoice getById(final UUID id);
 
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 b8c9438..9c2d10c 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
@@ -29,6 +29,7 @@ import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.entity.MapperBase;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.skife.jdbi.v2.SQLStatement;
@@ -70,12 +71,7 @@ public interface InvoicePaymentSqlDao {
     @SqlUpdate
     void notifyOfPaymentAttempt(@InvoicePaymentBinder InvoicePayment invoicePayment);
 
-    public static class InvoicePaymentMapper implements ResultSetMapper<InvoicePayment> {
-        private DateTime getDate(ResultSet rs, String fieldName) throws SQLException {
-            final Timestamp resultStamp = rs.getTimestamp(fieldName);
-            return rs.wasNull() ? null : new DateTime(resultStamp).toDateTime(DateTimeZone.UTC);
-        }
-
+    public static class InvoicePaymentMapper extends MapperBase implements ResultSetMapper<InvoicePayment> {
         @Override
         public InvoicePayment map(int index, ResultSet result, StatementContext context) throws SQLException {
             final UUID paymentAttemptId = UUID.fromString(result.getString("payment_attempt_id"));
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 36a2ee3..c43c8f4 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
@@ -19,7 +19,9 @@ package com.ning.billing.invoice.dao;
 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;
 import com.ning.billing.util.UuidMapper;
+import com.ning.billing.util.entity.CallContextBinder;
 import com.ning.billing.util.entity.EntityDao;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.SQLStatement;
@@ -54,7 +56,7 @@ import java.util.UUID;
 public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<InvoiceSqlDao>, Transmogrifier, CloseMe {
     @Override
     @SqlUpdate
-    void create(@InvoiceBinder Invoice invoice);
+    void create(@InvoiceBinder Invoice invoice, @CallContextBinder final CallContext context);
 
     @SqlQuery
     List<Invoice> getInvoicesByAccount(@Bind("accountId") final String accountId);
@@ -113,8 +115,10 @@ public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<Invoice
             DateTime invoiceDate = new DateTime(result.getTimestamp("invoice_date"));
             DateTime targetDate = new DateTime(result.getTimestamp("target_date"));
             Currency currency = Currency.valueOf(result.getString("currency"));
+            String createdBy = result.getString("created_by");
+            DateTime createdDate = new DateTime(result.getTimestamp("created_date"));
 
-            return new DefaultInvoice(id, accountId, invoiceNumber, invoiceDate, targetDate, currency);
+            return new DefaultInvoice(id, accountId, invoiceNumber, invoiceDate, targetDate, currency, createdBy, createdDate);
         }
     }
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/RecurringInvoiceItemSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/RecurringInvoiceItemSqlDao.java
index d588cb1..850687b 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/RecurringInvoiceItemSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/RecurringInvoiceItemSqlDao.java
@@ -19,6 +19,8 @@ package com.ning.billing.invoice.dao;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.entity.CallContextBinder;
 import com.ning.billing.util.entity.EntityDao;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.SQLStatement;
@@ -59,7 +61,7 @@ public interface RecurringInvoiceItemSqlDao extends EntityDao<InvoiceItem> {
 
     @Override
     @SqlUpdate
-    void create(@RecurringInvoiceItemBinder final InvoiceItem invoiceItem);
+    void create(@RecurringInvoiceItemBinder final InvoiceItem invoiceItem, @CallContextBinder final CallContext context);
 
     @SqlBatch(transactional = false)
     void batchCreateFromTransaction(@RecurringInvoiceItemBinder final List<InvoiceItem> items);
@@ -107,10 +109,11 @@ public interface RecurringInvoiceItemSqlDao extends EntityDao<InvoiceItem> {
             Currency currency = Currency.valueOf(result.getString("currency"));
             String reversedItemString = result.getString("reversed_item_id");
             UUID reversedItemId = (reversedItemString == null) ? null : UUID.fromString(reversedItemString);
+            String createdBy = result.getString("created_by");
             DateTime createdDate = new DateTime(result.getTimestamp("created_date"));
 
             return new RecurringInvoiceItem(id, invoiceId, subscriptionId, planName, phaseName, startDate, endDate,
-                    amount, rate, currency, reversedItemId, createdDate);
+                    amount, rate, currency, reversedItemId, createdBy, createdDate);
         }
     }
 }
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 c4a3b6a..90be1b3 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
@@ -21,6 +21,7 @@ import java.util.List;
 import java.util.SortedSet;
 import java.util.UUID;
 
+import com.ning.billing.util.CallContext;
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -68,18 +69,20 @@ public class InvoiceDispatcher {
         this.locker = locker;
 
         String verboseOutputValue = System.getProperty("VERBOSE_OUTPUT");
-        VERBOSE_OUTPUT = (verboseOutputValue == null) ? false : Boolean.parseBoolean(verboseOutputValue);
+        VERBOSE_OUTPUT = (verboseOutputValue != null) && Boolean.parseBoolean(verboseOutputValue);
     }
 
-    public void processSubscription(final SubscriptionTransition transition) throws InvoiceApiException {
+    public void processSubscription(final SubscriptionTransition transition,
+                                    final CallContext context) throws InvoiceApiException {
         UUID subscriptionId = transition.getSubscriptionId();
         DateTime targetDate = transition.getEffectiveTransitionTime();
         log.info("Got subscription transition from InvoiceListener. id: " + subscriptionId.toString() + "; targetDate: " + targetDate.toString());
         log.info("Transition type: " + transition.getTransitionType().toString());
-        processSubscription(subscriptionId, targetDate);
+        processSubscription(subscriptionId, targetDate, context);
     }
 
-    public void processSubscription(final UUID subscriptionId, final DateTime targetDate) throws InvoiceApiException {
+    public void processSubscription(final UUID subscriptionId, final DateTime targetDate,
+                                    final CallContext context) throws InvoiceApiException {
         if (subscriptionId == null) {
             log.error("Failed handling entitlement change.", new InvoiceApiException(ErrorCode.INVOICE_INVALID_TRANSITION));
             return;
@@ -92,15 +95,16 @@ public class InvoiceDispatcher {
             return;
         }
 
-        processAccount(accountId, targetDate, false);
+        processAccount(accountId, targetDate, false, context);
     }
     
-    public Invoice processAccount(UUID accountId, DateTime targetDate, boolean dryrun) throws InvoiceApiException {
+    public Invoice processAccount(final UUID accountId, final DateTime targetDate,
+                                  final boolean dryRun, final CallContext context) throws InvoiceApiException {
 		GlobalLock lock = null;
         try {
             lock = locker.lockWithNumberOfTries(LockerService.INVOICE, accountId.toString(), NB_LOCK_TRY);
 
-            return processAccountWithLock(accountId, targetDate, dryrun);
+            return processAccountWithLock(accountId, targetDate, dryRun, context);
 
         } catch (LockFailedException e) {
             // Not good!
@@ -114,7 +118,8 @@ public class InvoiceDispatcher {
         return null;
     }
 
-    private Invoice processAccountWithLock(final UUID accountId, final DateTime targetDate, boolean dryrun) throws InvoiceApiException {
+    private Invoice processAccountWithLock(final UUID accountId, final DateTime targetDate,
+                                           final boolean dryRun, final CallContext context) throws InvoiceApiException {
 
         Account account = accountUserApi.getAccountById(accountId);
         if (account == null) {
@@ -145,8 +150,8 @@ public class InvoiceDispatcher {
             }
             outputDebugData(events, invoices);
 
-            if (invoice.getNumberOfItems() > 0 && !dryrun) {
-                invoiceDao.create(invoice);
+            if (invoice.getNumberOfItems() > 0 && !dryRun) {
+                invoiceDao.create(invoice, context);
             }
         }
         
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 d31e98b..6a57755 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
@@ -18,6 +18,11 @@ package com.ning.billing.invoice;
 
 import java.util.UUID;
 
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.CallOrigin;
+import com.ning.billing.util.UserType;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.entity.DefaultCallContext;
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -30,16 +35,19 @@ import com.ning.billing.invoice.api.InvoiceApiException;
 public class InvoiceListener {
     private final static Logger log = LoggerFactory.getLogger(InvoiceListener.class);
 	private final InvoiceDispatcher dispatcher;
+    private final Clock clock;
 
     @Inject
-    public InvoiceListener(InvoiceDispatcher dispatcher) {
+    public InvoiceListener(Clock clock, InvoiceDispatcher dispatcher) {
+        this.clock = clock;
         this.dispatcher = dispatcher;
     }
 
     @Subscribe
     public void handleSubscriptionTransition(final SubscriptionTransition transition) {
         try {
-        	dispatcher.processSubscription(transition);
+            CallContext context = new DefaultCallContext(clock, "Transition", CallOrigin.INTERNAL, UserType.SYSTEM);
+        	dispatcher.processSubscription(transition, context);
         } catch (InvoiceApiException e) {
             log.error(e.getMessage());
         }
@@ -47,7 +55,8 @@ public class InvoiceListener {
 
     public void handleNextBillingDateEvent(final UUID subscriptionId, final DateTime eventDateTime) {
         try {
-        	dispatcher.processSubscription(subscriptionId, eventDateTime);
+            CallContext context = new DefaultCallContext(clock, "Next Billing Date", CallOrigin.INTERNAL, UserType.SYSTEM);
+        	dispatcher.processSubscription(subscriptionId, eventDateTime, context);
         } catch (InvoiceApiException e) {
             log.error(e.getMessage());
         }
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 72921c3..8f93a2d 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
@@ -20,7 +20,7 @@ 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.util.clock.Clock;
+import com.ning.billing.util.entity.EntityBase;
 import org.joda.time.DateTime;
 
 import javax.annotation.Nullable;
@@ -29,23 +29,24 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
-public class DefaultInvoice implements Invoice {
+public class DefaultInvoice extends EntityBase implements Invoice {
     private final InvoiceItemList invoiceItems = new InvoiceItemList();
     private final List<InvoicePayment> payments = new ArrayList<InvoicePayment>();
-    private final UUID id;
     private final UUID accountId;
     private final Integer invoiceNumber;
     private final DateTime invoiceDate;
     private final DateTime targetDate;
     private final Currency currency;
 
-    public DefaultInvoice(UUID accountId, DateTime targetDate, Currency currency, Clock clock) {
-        this(UUID.randomUUID(), accountId, null, clock.getUTCNow(), targetDate, currency);
+    // used to create a new invoice
+    public DefaultInvoice(UUID accountId, DateTime invoiceDate, DateTime targetDate, Currency currency) {
+        this(UUID.randomUUID(), accountId, null, invoiceDate, targetDate, currency, null, null);
     }
 
-    public DefaultInvoice(UUID invoiceId, UUID accountId, @Nullable Integer invoiceNumber, DateTime invoiceDate, DateTime targetDate,
-                          Currency currency) {
-        this.id = invoiceId;
+    // used to hydrate invoice from persistence layer
+    public DefaultInvoice(UUID invoiceId, UUID accountId, @Nullable Integer invoiceNumber, DateTime invoiceDate,
+                          DateTime targetDate, Currency currency, @Nullable String createdBy, @Nullable DateTime createdDate) {
+        super(invoiceId, createdBy, createdDate);
         this.accountId = accountId;
         this.invoiceNumber = invoiceNumber;
         this.invoiceDate = invoiceDate;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
index 66c71bd..f1643d7 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
@@ -21,6 +21,7 @@ import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.api.Duration;
+import com.ning.billing.config.InvoiceConfig;
 import com.ning.billing.entitlement.api.billing.BillingEvent;
 import com.ning.billing.entitlement.api.billing.BillingModeType;
 import com.ning.billing.invoice.api.Invoice;
@@ -41,13 +42,14 @@ import javax.annotation.Nullable;
 public class DefaultInvoiceGenerator implements InvoiceGenerator {
     private static final int ROUNDING_MODE = InvoicingConfiguration.getRoundingMode();
     private static final int NUMBER_OF_DECIMALS = InvoicingConfiguration.getNumberOfDecimals();
-    public static final String NUMBER_OF_MONTHS = "killbill.invoice.maxNumberOfMonthsInFuture";
 
     private final Clock clock;
+    private final InvoiceConfig config;
 
     @Inject
-    public DefaultInvoiceGenerator(Clock clock) {
+    public DefaultInvoiceGenerator(Clock clock, InvoiceConfig config) {
         this.clock = clock;
+        this.config = config;
     }
 
    /*
@@ -77,7 +79,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
 
         targetDate = adjustTargetDate(existingInvoices, targetDate);
 
-        DefaultInvoice invoice = new DefaultInvoice(accountId, targetDate, targetCurrency, clock);
+        DefaultInvoice invoice = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate, targetCurrency);
         UUID invoiceId = invoice.getId();
         List<InvoiceItem> proposedItems = generateInvoiceItems(invoiceId, events, targetDate, targetCurrency);
 
@@ -100,8 +102,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
     }
 
     private void validateTargetDate(DateTime targetDate) throws InvoiceApiException {
-        String maximumNumberOfMonthsValue = System.getProperty(NUMBER_OF_MONTHS);
-        int maximumNumberOfMonths= (maximumNumberOfMonthsValue == null) ? 36 : Integer.parseInt(maximumNumberOfMonthsValue);
+        int maximumNumberOfMonths = config.getNumberOfMonthsInFuture();
 
         if (Months.monthsBetween(clock.getUTCNow(), targetDate).getMonths() > maximumNumberOfMonths) {
             throw new InvoiceApiException(ErrorCode.INVOICE_TARGET_DATE_TOO_FAR_IN_THE_FUTURE, targetDate.toString());
@@ -214,7 +215,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
                                 thisEvent.getPlan().getName(),
                                 thisEvent.getPlanPhase().getName(),
                                 itemDatum.getStartDate(), itemDatum.getEndDate(),
-                                amount, rate, currency, clock.getUTCNow());
+                                amount, rate, currency);
                         items.add(recurringItem);
                     }
                 }
@@ -246,8 +247,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
 
                 return new FixedPriceInvoiceItem(invoiceId, thisEvent.getSubscription().getId(),
                                                  thisEvent.getPlan().getName(), thisEvent.getPlanPhase().getName(),
-                                                 thisEvent.getEffectiveDate(), endDate, fixedPrice, currency,
-                                                 clock.getUTCNow());
+                                                 thisEvent.getEffectiveDate(), endDate, fixedPrice, currency);
             } else {
                 return null;
             }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java
index 4a1bc1d..d712ffb 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java
@@ -25,15 +25,14 @@ import java.util.UUID;
 
 public class FixedPriceInvoiceItem extends InvoiceItemBase {
     public FixedPriceInvoiceItem(UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
-                                 DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency,
-                                 DateTime createdDate) {
-        super(invoiceId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, createdDate);
+                                 DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
+        super(invoiceId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
     }
 
     public FixedPriceInvoiceItem(UUID id, UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
                                  DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency,
-                                 DateTime createdDate) {
-        super(id, invoiceId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, createdDate);
+                                 String createdBy, DateTime createdDate) {
+        super(id, invoiceId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, createdBy, createdDate);
     }
 
     @Override
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java
index 6f69e33..7d2b517 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java
@@ -18,13 +18,13 @@ package com.ning.billing.invoice.model;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.util.entity.EntityBase;
 import org.joda.time.DateTime;
 
 import java.math.BigDecimal;
 import java.util.UUID;
 
-public abstract class InvoiceItemBase implements InvoiceItem {
-    protected final UUID id;
+public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem {
     protected final UUID invoiceId;
     protected final UUID subscriptionId;
     protected final String planName;
@@ -33,19 +33,17 @@ public abstract class InvoiceItemBase implements InvoiceItem {
     protected final DateTime endDate;
     protected final BigDecimal amount;
     protected final Currency currency;
-    protected final DateTime createdDate;
 
     public InvoiceItemBase(UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
-                           DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency,
-                           DateTime createdDate) {
+                           DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
         this(UUID.randomUUID(), invoiceId, subscriptionId, planName, phaseName,
-                startDate, endDate, amount, currency, createdDate);
+                startDate, endDate, amount, currency, null, null);
     }
 
     public InvoiceItemBase(UUID id, UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
                            DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency,
-                           DateTime createdDate) {
-        this.id = id;
+                           String createdBy, DateTime createdDate) {
+        super(id, createdBy, createdDate);
         this.invoiceId = invoiceId;
         this.subscriptionId = subscriptionId;
         this.planName = planName;
@@ -54,7 +52,6 @@ public abstract class InvoiceItemBase implements InvoiceItem {
         this.endDate = endDate;
         this.amount = amount;
         this.currency = currency;
-        this.createdDate = createdDate;
     }
 
     public DateTime getCreatedDate() {
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java
index 8c7517e..c47d53e 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java
@@ -30,27 +30,28 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
     public RecurringInvoiceItem(UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
                                 DateTime startDate, DateTime endDate,
                                 BigDecimal amount, BigDecimal rate,
-                                Currency currency,
-                                DateTime createdDate) {
-        this(UUID.randomUUID(), invoiceId, subscriptionId, planName, phaseName, startDate, endDate,
-                amount, rate, currency, createdDate);
+                                Currency currency) {
+        super(invoiceId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+        this.rate = rate;
+        this.reversedItemId = null;
     }
 
     public RecurringInvoiceItem(UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
                                 DateTime startDate, DateTime endDate,
                                 BigDecimal amount, BigDecimal rate,
-                                Currency currency, UUID reversedItemId,
-                                DateTime createdDate) {
-        this(UUID.randomUUID(), invoiceId, subscriptionId, planName, phaseName, startDate, endDate,
-                amount, rate, currency, reversedItemId, createdDate);
+                                Currency currency, UUID reversedItemId) {
+        super(invoiceId, subscriptionId, planName, phaseName, startDate, endDate,
+                amount, currency);
+        this.rate = rate;
+        this.reversedItemId = reversedItemId;
     }
 
     public RecurringInvoiceItem(UUID id, UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
                                 DateTime startDate, DateTime endDate,
                                 BigDecimal amount, BigDecimal rate,
                                 Currency currency,
-                                DateTime createdDate) {
-        super(id, invoiceId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, createdDate);
+                                String createdBy, DateTime createdDate) {
+        super(id, invoiceId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, createdBy, createdDate);
 
         this.rate = rate;
         this.reversedItemId = null;
@@ -60,8 +61,8 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
                                 DateTime startDate, DateTime endDate,
                                 BigDecimal amount, BigDecimal rate,
                                 Currency currency, UUID reversedItemId,
-                                DateTime createdDate) {
-        super(id, invoiceId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, createdDate);
+                                String createdBy, DateTime createdDate) {
+        super(id, invoiceId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, createdBy, createdDate);
 
         this.rate = rate;
         this.reversedItemId = reversedItemId;
@@ -71,7 +72,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
     public InvoiceItem asCredit() {
         BigDecimal amountNegated = amount == null ? null : amount.negate();
         return new RecurringInvoiceItem(invoiceId, subscriptionId, planName, phaseName, startDate, endDate,
-                amountNegated, rate, currency, id, createdDate);
+                amountNegated, rate, currency, id);
     }
 
     @Override
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 203be9e..87f9293 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
@@ -14,7 +14,8 @@ invoiceSetFields(prefix) ::= <<
     <prefix>account_id,
     <prefix>invoice_date,
     <prefix>target_date,
-    <prefix>currency
+    <prefix>currency,
+    <prefix>updated_by
 >>
 
 get() ::= <<
@@ -74,7 +75,7 @@ getAccountBalance() ::= <<
 
 create() ::= <<
   INSERT INTO invoices(<invoiceSetFields()>)
-  VALUES (:id, :accountId, :invoiceDate, :targetDate, :currency);
+  VALUES (:id, :accountId, :invoiceDate, :targetDate, :currency, :userName);
 >>
 
 getInvoiceIdByPaymentAttemptId() ::= <<
@@ -95,7 +96,6 @@ getUnpaidInvoicesByAccountId() ::= <<
   ORDER BY i.target_date ASC;
 >>
 
-
 test() ::= <<
   SELECT 1
   FROM invoices;
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql b/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
index 503ec7f..dfae5bc 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
@@ -45,6 +45,7 @@ CREATE TABLE invoices (
   invoice_date datetime NOT NULL,
   target_date datetime NOT NULL,
   currency char(3) NOT NULL,
+  updated_by varchar(30) NOT NULL,
   PRIMARY KEY(invoice_number)
 ) ENGINE=innodb;
 CREATE INDEX invoices_invoice_number ON invoices(invoice_number ASC);
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 eabcc26..dc4a9e6 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
@@ -21,7 +21,15 @@ import static org.testng.Assert.fail;
 
 import java.io.IOException;
 
+import com.ning.billing.config.InvoiceConfig;
+import com.ning.billing.invoice.model.DefaultInvoiceGenerator;
+import com.ning.billing.invoice.model.InvoiceGenerator;
 import com.ning.billing.invoice.tests.InvoicingTestBase;
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.CallOrigin;
+import com.ning.billing.util.UserType;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.entity.DefaultCallContext;
 import org.apache.commons.io.IOUtils;
 import org.skife.jdbi.v2.Handle;
 import org.skife.jdbi.v2.TransactionCallback;
@@ -42,10 +50,25 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
     protected RecurringInvoiceItemSqlDao recurringInvoiceItemDao;
     protected InvoicePaymentSqlDao invoicePaymentDao;
     protected InvoiceModuleWithEmbeddedDb module;
+    protected Clock clock;
+    protected CallContext context;
+    protected InvoiceGenerator generator;
+
+    private final InvoiceConfig invoiceConfig = new InvoiceConfig() {
+        @Override
+        public long getDaoClaimTimeMs() {throw new UnsupportedOperationException();}
+        @Override
+        public int getDaoMaxReadyEvents() {throw new UnsupportedOperationException();}
+        @Override
+        public long getNotificationSleepTimeMs() {throw new UnsupportedOperationException();}
+        @Override
+        public boolean isEventProcessingOff() {throw new UnsupportedOperationException();}
+        @Override
+        public int getNumberOfMonthsInFuture() {return 36;}
+    };
 
     @BeforeClass(alwaysRun = true)
     protected void setup() throws IOException {
-        // Health check test to make sure MySQL is setup properly
         try {
             module = new InvoiceModuleWithEmbeddedDb();
             final String accountDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
@@ -65,6 +88,9 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
             recurringInvoiceItemDao = module.getInvoiceItemSqlDao();
 
             invoicePaymentDao = module.getInvoicePaymentSqlDao();
+            clock = injector.getInstance(Clock.class);
+            context = new DefaultCallContext(clock, "Count Rogan", CallOrigin.TEST, UserType.TEST);
+            generator = new DefaultInvoiceGenerator(clock, invoiceConfig);
 
             BusService busService = injector.getInstance(BusService.class);
             ((DefaultBusService) busService).startBus();
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
index 2289a78..c23cf92 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
@@ -26,6 +26,7 @@ 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.config.InvoiceConfig;
 import com.ning.billing.entitlement.api.billing.BillingEvent;
 import com.ning.billing.entitlement.api.billing.BillingModeType;
 import com.ning.billing.entitlement.api.billing.DefaultBillingEvent;
@@ -43,8 +44,6 @@ import com.ning.billing.invoice.model.InvoiceGenerator;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
 import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
-import com.ning.billing.util.clock.Clock;
-import com.ning.billing.util.clock.DefaultClock;
 import org.joda.time.DateTime;
 import org.testng.annotations.Test;
 
@@ -62,16 +61,14 @@ import static org.testng.Assert.assertTrue;
 @Test(groups = {"invoicing", "invoicing-invoiceDao"})
 public class InvoiceDaoTests extends InvoiceDaoTestBase {
     private final int NUMBER_OF_DAY_BETWEEN_RETRIES = 8;
-    private final Clock clock = new DefaultClock();
-    private final InvoiceGenerator generator = new DefaultInvoiceGenerator(clock);
 
     @Test
     public void testCreationAndRetrievalByAccount() {
         UUID accountId = UUID.randomUUID();
-        Invoice invoice = new DefaultInvoice(accountId, clock.getUTCNow(), Currency.USD, clock);
+        Invoice invoice = new DefaultInvoice(accountId, clock.getUTCNow(), clock.getUTCNow(), Currency.USD);
         DateTime invoiceDate = invoice.getInvoiceDate();
 
-        invoiceDao.create(invoice);
+        invoiceDao.create(invoice, context);
 
         List<Invoice> invoices = invoiceDao.getInvoicesByAccount(accountId);
         assertNotNull(invoices);
@@ -87,15 +84,15 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
     @Test
     public void testInvoicePayment() {
         UUID accountId = UUID.randomUUID();
-        Invoice invoice = new DefaultInvoice(accountId, clock.getUTCNow(), Currency.USD, clock);
+        Invoice invoice = new DefaultInvoice(accountId, clock.getUTCNow(), clock.getUTCNow(), Currency.USD);
         UUID invoiceId = invoice.getId();
         UUID subscriptionId = UUID.randomUUID();
         DateTime startDate = new DateTime(2010, 1, 1, 0, 0, 0, 0);
         DateTime endDate = new DateTime(2010, 4, 1, 0, 0, 0, 0);
         InvoiceItem invoiceItem = new RecurringInvoiceItem(invoiceId, subscriptionId, "test plan", "test phase", startDate, endDate,
-                new BigDecimal("21.00"), new BigDecimal("7.00"), Currency.USD, clock.getUTCNow());
+                new BigDecimal("21.00"), new BigDecimal("7.00"), Currency.USD);
         invoice.addInvoiceItem(invoiceItem);
-        invoiceDao.create(invoice);
+        invoiceDao.create(invoice, context);
 
         Invoice savedInvoice = invoiceDao.getById(invoiceId);
         assertNotNull(savedInvoice);
@@ -127,13 +124,13 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
     public void testAddPayment() {
         UUID accountId = UUID.randomUUID();
         DateTime targetDate = new DateTime(2011, 10, 6, 0, 0, 0, 0);
-        Invoice invoice = new DefaultInvoice(accountId, targetDate, Currency.USD, clock);
+        Invoice invoice = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate, Currency.USD);
 
         UUID paymentAttemptId = UUID.randomUUID();
         DateTime paymentAttemptDate = new DateTime(2011, 6, 24, 12, 14, 36, 0);
         BigDecimal paymentAmount = new BigDecimal("14.0");
 
-        invoiceDao.create(invoice);
+        invoiceDao.create(invoice, context);
         invoiceDao.notifyOfPaymentAttempt(new DefaultInvoicePayment(paymentAttemptId, invoice.getId(), paymentAttemptDate, paymentAmount, Currency.USD));
 
         invoice = invoiceDao.getById(invoice.getId());
@@ -146,11 +143,11 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
     public void testAddPaymentAttempt() {
         UUID accountId = UUID.randomUUID();
         DateTime targetDate = new DateTime(2011, 10, 6, 0, 0, 0, 0);
-        Invoice invoice = new DefaultInvoice(accountId, targetDate, Currency.USD, clock);
+        Invoice invoice = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate, Currency.USD);
 
         DateTime paymentAttemptDate = new DateTime(2011, 6, 24, 12, 14, 36, 0);
 
-        invoiceDao.create(invoice);
+        invoiceDao.create(invoice, context);
         invoiceDao.notifyOfPaymentAttempt(new DefaultInvoicePayment(invoice.getId(), paymentAttemptDate));
 
         invoice = invoiceDao.getById(invoice.getId());
@@ -167,9 +164,9 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         int existingInvoiceCount = invoices.size();
 
         UUID accountId = UUID.randomUUID();
-        Invoice invoice = new DefaultInvoice(accountId, targetDate, Currency.USD, clock);
+        Invoice invoice = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate, Currency.USD);
 
-        invoiceDao.create(invoice);
+        invoiceDao.create(invoice, context);
         invoices = invoiceDao.getInvoicesForPayment(notionalDate, NUMBER_OF_DAY_BETWEEN_RETRIES);
         assertEquals(invoices.size(), existingInvoiceCount);
     }
@@ -182,7 +179,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         // create a new invoice with one item
         UUID accountId = UUID.randomUUID();
         DateTime targetDate = new DateTime(2011, 10, 6, 0, 0, 0, 0);
-        Invoice invoice = new DefaultInvoice(accountId, targetDate, Currency.USD, clock);
+        Invoice invoice = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate, Currency.USD);
 
         UUID invoiceId = invoice.getId();
         UUID subscriptionId = UUID.randomUUID();
@@ -191,9 +188,9 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         BigDecimal amount = rate.multiply(new BigDecimal("3.0"));
 
         RecurringInvoiceItem item = new RecurringInvoiceItem(invoiceId, subscriptionId, "test plan", "test phase", targetDate, endDate,
-                amount, rate, Currency.USD, clock.getUTCNow());
+                amount, rate, Currency.USD);
         invoice.addInvoiceItem(item);
-        invoiceDao.create(invoice);
+        invoiceDao.create(invoice, context);
 
         // ensure that the number of invoices for payment has increased by 1
         int count;
@@ -282,8 +279,8 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
 
 
         // create invoice 1 (subscriptions 1-4)
-        Invoice invoice1 = new DefaultInvoice(accountId, targetDate, Currency.USD, clock);
-        invoiceDao.create(invoice1);
+        Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate, Currency.USD);
+        invoiceDao.create(invoice1, context);
 
         UUID invoiceId1 = invoice1.getId();
 
@@ -291,24 +288,24 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         DateTime endDate = startDate.plusMonths(1);
 
         RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoiceId1, subscriptionId1, "test plan", "test A", startDate, endDate,
-                rate1, rate1, Currency.USD, clock.getUTCNow());
-        recurringInvoiceItemDao.create(item1);
+                rate1, rate1, Currency.USD);
+        recurringInvoiceItemDao.create(item1, context);
 
         RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoiceId1, subscriptionId2, "test plan", "test B", startDate, endDate,
-                rate2, rate2, Currency.USD, clock.getUTCNow());
-        recurringInvoiceItemDao.create(item2);
+                rate2, rate2, Currency.USD);
+        recurringInvoiceItemDao.create(item2, context);
 
         RecurringInvoiceItem item3 = new RecurringInvoiceItem(invoiceId1, subscriptionId3, "test plan", "test C", startDate, endDate,
-                rate3, rate3, Currency.USD, clock.getUTCNow());
-        recurringInvoiceItemDao.create(item3);
+                rate3, rate3, Currency.USD);
+        recurringInvoiceItemDao.create(item3, context);
 
         RecurringInvoiceItem item4 = new RecurringInvoiceItem(invoiceId1, subscriptionId4, "test plan", "test D", startDate, endDate,
-                rate4, rate4, Currency.USD, clock.getUTCNow());
-        recurringInvoiceItemDao.create(item4);
+                rate4, rate4, Currency.USD);
+        recurringInvoiceItemDao.create(item4, context);
 
         // create invoice 2 (subscriptions 1-3)
-        DefaultInvoice invoice2 = new DefaultInvoice(accountId, targetDate, Currency.USD, clock);
-        invoiceDao.create(invoice2);
+        DefaultInvoice invoice2 = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate, Currency.USD);
+        invoiceDao.create(invoice2, context);
 
         UUID invoiceId2 = invoice2.getId();
 
@@ -316,16 +313,16 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         endDate = startDate.plusMonths(1);
 
         RecurringInvoiceItem item5 = new RecurringInvoiceItem(invoiceId2, subscriptionId1, "test plan", "test phase A", startDate, endDate,
-                rate1, rate1, Currency.USD, clock.getUTCNow());
-        recurringInvoiceItemDao.create(item5);
+                rate1, rate1, Currency.USD);
+        recurringInvoiceItemDao.create(item5, context);
 
         RecurringInvoiceItem item6 = new RecurringInvoiceItem(invoiceId2, subscriptionId2, "test plan", "test phase B", startDate, endDate,
-                rate2, rate2, Currency.USD, clock.getUTCNow());
-        recurringInvoiceItemDao.create(item6);
+                rate2, rate2, Currency.USD);
+        recurringInvoiceItemDao.create(item6, context);
 
         RecurringInvoiceItem item7 = new RecurringInvoiceItem(invoiceId2, subscriptionId3, "test plan", "test phase C", startDate, endDate,
-                rate3, rate3, Currency.USD, clock.getUTCNow());
-        recurringInvoiceItemDao.create(item7);
+                rate3, rate3, Currency.USD);
+        recurringInvoiceItemDao.create(item7, context);
 
         // check that each subscription returns the correct number of invoices
         List<Invoice> items1 = invoiceDao.getInvoicesBySubscription(subscriptionId1);
@@ -345,12 +342,12 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
     public void testGetInvoicesForAccountAfterDate() {
         UUID accountId = UUID.randomUUID();
         DateTime targetDate1 = new DateTime(2011, 10, 6, 0, 0, 0, 0);
-        Invoice invoice1 = new DefaultInvoice(accountId, targetDate1, Currency.USD, clock);
-        invoiceDao.create(invoice1);
+        Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate1, Currency.USD);
+        invoiceDao.create(invoice1, context);
 
         DateTime targetDate2 = new DateTime(2011, 12, 6, 0, 0, 0, 0);
-        Invoice invoice2 = new DefaultInvoice(accountId, targetDate2, Currency.USD, clock);
-        invoiceDao.create(invoice2);
+        Invoice invoice2 = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate2, Currency.USD);
+        invoiceDao.create(invoice2, context);
 
 
         List<Invoice> invoices;
@@ -374,8 +371,8 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
     public void testAccountBalance() {
         UUID accountId = UUID.randomUUID();
         DateTime targetDate1 = new DateTime(2011, 10, 6, 0, 0, 0, 0);
-        Invoice invoice1 = new DefaultInvoice(accountId, targetDate1, Currency.USD, clock);
-        invoiceDao.create(invoice1);
+        Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate1, Currency.USD);
+        invoiceDao.create(invoice1, context);
 
         DateTime startDate = new DateTime(2011, 3, 1, 0, 0, 0, 0);
         DateTime endDate = startDate.plusMonths(1);
@@ -384,12 +381,12 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         BigDecimal rate2 = new BigDecimal("42.0");
 
         RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice1.getId(), UUID.randomUUID(), "test plan", "test phase A", startDate,
-                endDate, rate1, rate1, Currency.USD, clock.getUTCNow());
-        recurringInvoiceItemDao.create(item1);
+                endDate, rate1, rate1, Currency.USD);
+        recurringInvoiceItemDao.create(item1, context);
 
         RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), UUID.randomUUID(), "test plan", "test phase B", startDate,
-                endDate, rate2, rate2, Currency.USD, clock.getUTCNow());
-        recurringInvoiceItemDao.create(item2);
+                endDate, rate2, rate2, Currency.USD);
+        recurringInvoiceItemDao.create(item2, context);
 
         BigDecimal payment1 = new BigDecimal("48.0");
         InvoicePayment payment = new DefaultInvoicePayment(invoice1.getId(), new DateTime(), payment1, Currency.USD);
@@ -403,8 +400,8 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
     public void testAccountBalanceWithNoPayments() {
         UUID accountId = UUID.randomUUID();
         DateTime targetDate1 = new DateTime(2011, 10, 6, 0, 0, 0, 0);
-        Invoice invoice1 = new DefaultInvoice(accountId, targetDate1, Currency.USD, clock);
-        invoiceDao.create(invoice1);
+        Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate1, Currency.USD);
+        invoiceDao.create(invoice1, context);
 
         DateTime startDate = new DateTime(2011, 3, 1, 0, 0, 0, 0);
         DateTime endDate = startDate.plusMonths(1);
@@ -413,12 +410,12 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         BigDecimal rate2 = new BigDecimal("42.0");
 
         RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice1.getId(), UUID.randomUUID(), "test plan", "test phase A", startDate, endDate,
-                rate1, rate1, Currency.USD, clock.getUTCNow());
-        recurringInvoiceItemDao.create(item1);
+                rate1, rate1, Currency.USD);
+        recurringInvoiceItemDao.create(item1, context);
 
         RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), UUID.randomUUID(), "test plan", "test phase B", startDate, endDate,
-                rate2, rate2, Currency.USD, clock.getUTCNow());
-        recurringInvoiceItemDao.create(item2);
+                rate2, rate2, Currency.USD);
+        recurringInvoiceItemDao.create(item2, context);
 
         BigDecimal balance = invoiceDao.getAccountBalance(accountId);
         assertEquals(balance.compareTo(rate1.add(rate2)), 0);
@@ -428,8 +425,8 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
     public void testAccountBalanceWithNoInvoiceItems() {
         UUID accountId = UUID.randomUUID();
         DateTime targetDate1 = new DateTime(2011, 10, 6, 0, 0, 0, 0);
-        Invoice invoice1 = new DefaultInvoice(accountId, targetDate1, Currency.USD, clock);
-        invoiceDao.create(invoice1);
+        Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate1, Currency.USD);
+        invoiceDao.create(invoice1, context);
 
         BigDecimal payment1 = new BigDecimal("48.0");
         InvoicePayment payment = new DefaultInvoicePayment(invoice1.getId(), new DateTime(), payment1, Currency.USD);
@@ -443,8 +440,8 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
     public void testGetUnpaidInvoicesByAccountId() {
         UUID accountId = UUID.randomUUID();
         DateTime targetDate1 = new DateTime(2011, 10, 6, 0, 0, 0, 0);
-        Invoice invoice1 = new DefaultInvoice(accountId, targetDate1, Currency.USD, clock);
-        invoiceDao.create(invoice1);
+        Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate1, Currency.USD);
+        invoiceDao.create(invoice1, context);
 
         DateTime startDate = new DateTime(2011, 3, 1, 0, 0, 0, 0);
         DateTime endDate = startDate.plusMonths(1);
@@ -453,12 +450,12 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         BigDecimal rate2 = new BigDecimal("42.0");
 
         RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice1.getId(), UUID.randomUUID(), "test plan", "test phase A", startDate, endDate,
-                rate1, rate1, Currency.USD, clock.getUTCNow());
-        recurringInvoiceItemDao.create(item1);
+                rate1, rate1, Currency.USD);
+        recurringInvoiceItemDao.create(item1, context);
 
         RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), UUID.randomUUID(), "test plan", "test phase B", startDate, endDate,
-                rate2, rate2, Currency.USD, clock.getUTCNow());
-        recurringInvoiceItemDao.create(item2);
+                rate2, rate2, Currency.USD);
+        recurringInvoiceItemDao.create(item2, context);
 
         DateTime upToDate;
         Collection<Invoice> invoices;
@@ -472,8 +469,8 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(invoices.size(), 1);
 
         DateTime targetDate2 = new DateTime(2011, 7, 1, 0, 0, 0, 0);
-        Invoice invoice2 = new DefaultInvoice(accountId, targetDate2, Currency.USD, clock);
-        invoiceDao.create(invoice2);
+        Invoice invoice2 = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate2, Currency.USD);
+        invoiceDao.create(invoice2, context);
 
         DateTime startDate2 = new DateTime(2011, 6, 1, 0, 0, 0, 0);
         DateTime endDate2 = startDate2.plusMonths(3);
@@ -481,8 +478,8 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         BigDecimal rate3 = new BigDecimal("21.0");
 
         RecurringInvoiceItem item3 = new RecurringInvoiceItem(invoice2.getId(), UUID.randomUUID(), "test plan", "test phase C", startDate2, endDate2,
-                rate3, rate3, Currency.USD, clock.getUTCNow());
-        recurringInvoiceItemDao.create(item3);
+                rate3, rate3, Currency.USD);
+        recurringInvoiceItemDao.create(item3, context);
 
         upToDate = new DateTime(2011, 1, 1, 0, 0, 0, 0);
         invoices = invoiceDao.getUnpaidInvoicesByAccountId(accountId, upToDate);
@@ -544,8 +541,8 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(invoice2.getBalance(), FIVE);
         invoiceList.add(invoice2);
 
-        invoiceDao.create(invoice1);
-        invoiceDao.create(invoice2);
+        invoiceDao.create(invoice1, context);
+        invoiceDao.create(invoice2, context);
 
         Invoice savedInvoice1 = invoiceDao.getById(invoice1.getId());
         assertEquals(savedInvoice1.getTotalAmount(), ZERO);
@@ -676,7 +673,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(invoice.getNumberOfItems(), 2);
         assertEquals(invoice.getTotalAmount().compareTo(cheapAmount), 0);
 
-        invoiceDao.create(invoice);
+        invoiceDao.create(invoice, context);
         Invoice savedInvoice = invoiceDao.getById(invoice.getId());
 
         assertNotNull(savedInvoice);
@@ -713,7 +710,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
 
         Invoice invoice1 = generator.generateInvoice(UUID.randomUUID(), events, invoices, targetDate1, Currency.USD);
         invoices.add(invoice1);
-        invoiceDao.create(invoice1);
+        invoiceDao.create(invoice1, context);
         invoice1 = invoiceDao.getById(invoice1.getId());
         assertNotNull(invoice1.getInvoiceNumber());
 
@@ -723,7 +720,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
                                                       "testEvent2", 2L, SubscriptionTransitionType.CHANGE);
         events.add(event2);
         Invoice invoice2 = generator.generateInvoice(UUID.randomUUID(), events, invoices, targetDate2, Currency.USD);
-        invoiceDao.create(invoice2);
+        invoiceDao.create(invoice2, context);
         invoice2 = invoiceDao.getById(invoice2.getId());
         assertNotNull(invoice2.getInvoiceNumber());
     }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
index 63ad021..574faa9 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
@@ -20,8 +20,12 @@ import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.CallOrigin;
+import com.ning.billing.util.UserType;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.DefaultClock;
+import com.ning.billing.util.entity.DefaultCallContext;
 import org.joda.time.DateTime;
 import org.testng.annotations.Test;
 
@@ -34,8 +38,6 @@ import static org.testng.Assert.assertNotNull;
 
 @Test(groups = {"invoicing", "invoicing-invoiceDao"})
 public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
-    private final Clock clock = new DefaultClock();
-
     @Test(groups = "slow")
     public void testInvoiceItemCreation() {
         UUID invoiceId = UUID.randomUUID();
@@ -44,10 +46,9 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         DateTime endDate = new DateTime(2011, 11, 1, 0, 0, 0, 0);
         BigDecimal rate = new BigDecimal("20.00");
 
-        final DateTime expectedCreatedDate = clock.getUTCNow();
         RecurringInvoiceItem item = new RecurringInvoiceItem(invoiceId, subscriptionId, "test plan", "test phase", startDate, endDate,
-                rate, rate, Currency.USD, expectedCreatedDate);
-        recurringInvoiceItemDao.create(item);
+                rate, rate, Currency.USD);
+        recurringInvoiceItemDao.create(item, context);
 
         RecurringInvoiceItem thisItem = (RecurringInvoiceItem) recurringInvoiceItemDao.getById(item.getId().toString());
         assertNotNull(thisItem);
@@ -71,8 +72,8 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         for (int i = 0; i < 3; i++) {
             UUID invoiceId = UUID.randomUUID();
             RecurringInvoiceItem item = new RecurringInvoiceItem(invoiceId, subscriptionId, "test plan", "test phase", startDate.plusMonths(i), startDate.plusMonths(i + 1),
-                    rate, rate, Currency.USD, clock.getUTCNow());
-            recurringInvoiceItemDao.create(item);
+                    rate, rate, Currency.USD);
+            recurringInvoiceItemDao.create(item, context);
         }
 
         List<InvoiceItem> items = recurringInvoiceItemDao.getInvoiceItemsBySubscription(subscriptionId.toString());
@@ -89,8 +90,8 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
             UUID subscriptionId = UUID.randomUUID();
             BigDecimal amount = rate.multiply(new BigDecimal(i + 1));
             RecurringInvoiceItem item = new RecurringInvoiceItem(invoiceId, subscriptionId, "test plan", "test phase", startDate, startDate.plusMonths(1),
-                    amount, amount, Currency.USD, clock.getUTCNow());
-            recurringInvoiceItemDao.create(item);
+                    amount, amount, Currency.USD);
+            recurringInvoiceItemDao.create(item, context);
         }
 
         List<InvoiceItem> items = recurringInvoiceItemDao.getInvoiceItemsByInvoice(invoiceId.toString());
@@ -101,9 +102,9 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
     public void testGetInvoiceItemsByAccountId() {
         UUID accountId = UUID.randomUUID();
         DateTime targetDate = new DateTime(2011, 5, 23, 0, 0, 0, 0);
-        DefaultInvoice invoice = new DefaultInvoice(accountId, targetDate, Currency.USD, clock);
+        DefaultInvoice invoice = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate, Currency.USD);
 
-        invoiceDao.create(invoice);
+        invoiceDao.create(invoice, context);
 
         UUID invoiceId = invoice.getId();
         DateTime startDate = new DateTime(2011, 3, 1, 0, 0, 0, 0);
@@ -111,8 +112,8 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
 
         UUID subscriptionId = UUID.randomUUID();
         RecurringInvoiceItem item = new RecurringInvoiceItem(invoiceId, subscriptionId, "test plan", "test phase", startDate, startDate.plusMonths(1),
-                rate, rate, Currency.USD, clock.getUTCNow());
-        recurringInvoiceItemDao.create(item);
+                rate, rate, Currency.USD);
+        recurringInvoiceItemDao.create(item, context);
 
         List<InvoiceItem> items = recurringInvoiceItemDao.getInvoiceItemsByAccount(accountId.toString());
         assertEquals(items.size(), 1);
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 893912f..742dbf3 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
@@ -24,6 +24,7 @@ import java.util.Map;
 import java.util.UUID;
 
 import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.util.CallContext;
 import com.ning.billing.util.bus.Bus;
 import org.joda.time.DateTime;
 
@@ -43,7 +44,7 @@ public class MockInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public void create(Invoice invoice) {
+    public void create(final Invoice invoice, final CallContext context) {
         synchronized (monitor) {
             invoices.put(invoice.getId(), invoice);
         }
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 fdc5076..c6c881a 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
@@ -21,18 +21,16 @@ import static java.util.concurrent.TimeUnit.MINUTES;
 
 import java.io.IOException;
 import java.sql.SQLException;
-import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.Callable;
 
+import com.ning.billing.invoice.InvoiceDispatcher;
 import org.apache.commons.io.IOUtils;
 import org.joda.time.DateTime;
 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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
@@ -46,16 +44,10 @@ import com.ning.billing.catalog.api.CatalogService;
 import com.ning.billing.config.CatalogConfig;
 import com.ning.billing.config.InvoiceConfig;
 import com.ning.billing.dbi.MysqlTestingHelper;
-import com.ning.billing.entitlement.api.migration.AccountMigrationData;
 import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.SubscriptionBundle;
-import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
-import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.engine.dao.EntitlementSqlDao;
-import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.invoice.InvoiceListener;
-import com.ning.billing.invoice.dao.DefaultInvoiceDao;
 import com.ning.billing.lifecycle.KillbillService.ServiceException;
 import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
@@ -69,24 +61,22 @@ import com.ning.billing.util.notificationq.NotificationQueueService;
 import com.ning.billing.util.notificationq.dao.NotificationSqlDao;
 
 public class TestNextBillingDateNotifier {
-    private static Logger log = LoggerFactory.getLogger(TestNextBillingDateNotifier.class);
 	private Clock clock;
 	private DefaultNextBillingDateNotifier notifier;
 	private DummySqlTest dao;
 	private Bus eventBus;
 	private MysqlTestingHelper helper;
-	private final InvoiceListenerMock listener = new InvoiceListenerMock();
+	private InvoiceListenerMock listener;
 	private NotificationQueueService notificationQueueService;
 
 	private static final class InvoiceListenerMock extends InvoiceListener {
 		int eventCount = 0;
 		UUID latestSubscriptionId = null;
 
-		public InvoiceListenerMock() {
-			super(null);
+		public InvoiceListenerMock(Clock clock, InvoiceDispatcher dispatcher) {
+			super(clock, dispatcher);
 		}
 
-
 		@Override
 		public void handleNextBillingDateEvent(UUID subscriptionId,
 				DateTime eventDateTime) {
@@ -104,7 +94,6 @@ public class TestNextBillingDateNotifier {
 
 	}
 
-
 	@BeforeClass(groups={"setup"})
 	public void setup() throws ServiceException, IOException, ClassNotFoundException, SQLException {
 		//TestApiBase.loadSystemPropertiesFromClasspath("/entitlement.properties");
@@ -133,7 +122,7 @@ public class TestNextBillingDateNotifier {
         eventBus = g.getInstance(Bus.class);
         helper = g.getInstance(MysqlTestingHelper.class);
         notificationQueueService = g.getInstance(NotificationQueueService.class);
-
+        InvoiceDispatcher dispatcher = g.getInstance(InvoiceDispatcher.class);
 
         Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
         EntitlementDao entitlementDao = BrainDeadProxyFactory.createBrainDeadProxyFor(EntitlementDao.class);
@@ -141,6 +130,8 @@ public class TestNextBillingDateNotifier {
 
         notifier = new DefaultNextBillingDateNotifier(notificationQueueService,g.getInstance(InvoiceConfig.class), entitlementDao, listener);
         startMysql();
+
+        listener = new InvoiceListenerMock(clock, dispatcher);
 	}
 
 	private void startMysql() throws IOException, ClassNotFoundException, SQLException {
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 209d889..61b828c 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
@@ -23,6 +23,11 @@ import java.util.SortedSet;
 import java.util.TreeSet;
 import java.util.UUID;
 
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.CallOrigin;
+import com.ning.billing.util.UserType;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.entity.DefaultCallContext;
 import org.apache.commons.io.IOUtils;
 import org.joda.time.DateTime;
 import org.testng.Assert;
@@ -33,12 +38,10 @@ import org.testng.annotations.Test;
 import com.google.inject.Inject;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountUserApi;
-import com.ning.billing.catalog.MockInternationalPrice;
 import com.ning.billing.catalog.MockPlan;
 import com.ning.billing.catalog.MockPlanPhase;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.catalog.api.InternationalPrice;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.dbi.MysqlTestingHelper;
@@ -58,7 +61,6 @@ import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
 import com.ning.billing.util.bus.BusService;
 import com.ning.billing.util.globallocker.GlobalLocker;
-import sun.security.util.BigInt;
 
 @Guice(modules = {MockModule.class})
 public class TestInvoiceDispatcher {
@@ -81,13 +83,14 @@ public class TestInvoiceDispatcher {
     @Inject
     private BusService busService;
 
+    @Inject
+    private Clock clock;
 
+    private final CallContext context = new DefaultCallContext(clock, "Miracle Max", CallOrigin.TEST, UserType.TEST);
 
     @BeforeSuite(alwaysRun = true)
     public void setup() throws IOException
     {
-
-
         final String accountDdl = IOUtils.toString(TestInvoiceDispatcher.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
         final String entitlementDdl = IOUtils.toString(TestInvoiceDispatcher.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
         final String invoiceDdl = IOUtils.toString(TestInvoiceDispatcher.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
@@ -109,7 +112,7 @@ public class TestInvoiceDispatcher {
 
 
 	    @Test(groups={"fast"}, enabled=true)
-	    public void testDryrunInvoice() throws InvoiceApiException {
+	    public void testDryRunInvoice() throws InvoiceApiException {
 	    	UUID accountId = UUID.randomUUID();
 	    	UUID subscriptionId = UUID.randomUUID();
 
@@ -138,21 +141,21 @@ public class TestInvoiceDispatcher {
 
 	    	InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountUserApi, entitlementBillingApi, invoiceDao, locker);
 
-	    	Invoice invoice = dispatcher.processAccount(accountId, target, true);
+	    	Invoice invoice = dispatcher.processAccount(accountId, target, true, context);
 	    	Assert.assertNotNull(invoice);
 
 	    	List<Invoice> invoices = invoiceDao.getInvoicesByAccount(accountId);
 	    	Assert.assertEquals(invoices.size(),0);
 
 	    	// Try it again to double check
-	    	invoice = dispatcher.processAccount(accountId, target, true);
+	    	invoice = dispatcher.processAccount(accountId, target, true, context);
 	    	Assert.assertNotNull(invoice);
 
 	    	invoices = invoiceDao.getInvoicesByAccount(accountId);
 	    	Assert.assertEquals(invoices.size(),0);
 
 	    	// This time no dry run
-	    	invoice = dispatcher.processAccount(accountId, target, false);
+	    	invoice = dispatcher.processAccount(accountId, target, false, context);
 	    	Assert.assertNotNull(invoice);
 
 	    	invoices = invoiceDao.getInvoicesByAccount(accountId);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
index 838375e..6c6f6c1 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
@@ -16,7 +16,6 @@
 
 package com.ning.billing.invoice.tests;
 
-import com.google.inject.Inject;
 import com.ning.billing.catalog.DefaultPrice;
 import com.ning.billing.catalog.MockInternationalPrice;
 import com.ning.billing.catalog.MockPlan;
@@ -27,6 +26,7 @@ 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.config.InvoiceConfig;
 import com.ning.billing.entitlement.api.billing.BillingEvent;
 import com.ning.billing.entitlement.api.billing.BillingModeType;
 import com.ning.billing.entitlement.api.billing.DefaultBillingEvent;
@@ -62,11 +62,38 @@ import static org.testng.Assert.assertNull;
 @Test(groups = {"fast", "invoicing", "invoiceGenerator"})
 public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
     private final Clock clock = new DefaultClock();
+    private final InvoiceConfig invoiceConfig = new InvoiceConfig() {
+        @Override
+        public long getDaoClaimTimeMs() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int getDaoMaxReadyEvents() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public long getNotificationSleepTimeMs() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isEventProcessingOff() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int getNumberOfMonthsInFuture() {
+            return 36;
+        }
+    };
+
     private final InvoiceGenerator generator;
 
     public DefaultInvoiceGeneratorTests() {
         super();
-        this.generator = new DefaultInvoiceGenerator(clock);
+        this.generator = new DefaultInvoiceGenerator(clock, invoiceConfig);
     }
 
     @Test
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 c90bf69..4b47237 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,7 +16,6 @@
 
 package com.ning.billing.invoice.tests;
 
-
 import com.ning.billing.invoice.model.InvoicingConfiguration;
 import org.joda.time.DateTime;
 
@@ -32,7 +31,7 @@ public abstract class InvoicingTestBase {
     protected static final BigDecimal ONE_AND_A_HALF = new BigDecimal("1.5").setScale(NUMBER_OF_DECIMALS);
     protected static final BigDecimal TWO = new BigDecimal("2.0").setScale(NUMBER_OF_DECIMALS);
     protected static final BigDecimal THREE = new BigDecimal("3.0").setScale(NUMBER_OF_DECIMALS);
-    protected static final BigDecimal FOUR = new BigDecimal("4.0").setScale(NUMBER_OF_DECIMALS);
+    //protected static final BigDecimal FOUR = new BigDecimal("4.0").setScale(NUMBER_OF_DECIMALS);
     protected static final BigDecimal FIVE = new BigDecimal("5.0").setScale(NUMBER_OF_DECIMALS);
     protected static final BigDecimal SIX = new BigDecimal("6.0").setScale(NUMBER_OF_DECIMALS);
     protected static final BigDecimal SEVEN = new BigDecimal("7.0").setScale(NUMBER_OF_DECIMALS);
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 69117fb..7ca3727 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
@@ -24,6 +24,8 @@ import java.util.Date;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.entity.BinderBase;
+import com.ning.billing.util.entity.MapperBase;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.skife.jdbi.v2.SQLStatement;
@@ -93,12 +95,7 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Tr
     @SqlUpdate
     void insertPaymentInfo(@Bind(binder = PaymentInfoBinder.class) PaymentInfo paymentInfo);
 
-    public static final class PaymentAttemptBinder implements Binder<Bind, PaymentAttempt> {
-
-        private Date getDate(DateTime dateTime) {
-            return dateTime == null ? null : dateTime.toDate();
-        }
-
+    public static final class PaymentAttemptBinder extends BinderBase implements Binder<Bind, PaymentAttempt> {
         @Override
         public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, PaymentAttempt paymentAttempt) {
             stmt.bind("payment_attempt_id", paymentAttempt.getPaymentAttemptId().toString());
@@ -115,13 +112,7 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Tr
         }
     }
 
-    public static class PaymentAttemptMapper implements ResultSetMapper<PaymentAttempt> {
-
-        private DateTime getDate(ResultSet rs, String fieldName) throws SQLException {
-            final Timestamp resultStamp = rs.getTimestamp(fieldName);
-            return rs.wasNull() ? null : new DateTime(resultStamp).toDateTime(DateTimeZone.UTC);
-        }
-
+    public static class PaymentAttemptMapper extends MapperBase implements ResultSetMapper<PaymentAttempt> {
         @Override
         public PaymentAttempt map(int index, ResultSet rs, StatementContext ctx) throws SQLException {
 
@@ -151,12 +142,7 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Tr
         }
     }
 
-    public static final class PaymentInfoBinder implements Binder<Bind, PaymentInfo> {
-
-        private Date getDate(DateTime dateTime) {
-            return dateTime == null ? null : dateTime.toDate();
-        }
-
+    public static final class PaymentInfoBinder extends BinderBase implements Binder<Bind, PaymentInfo> {
         @Override
         public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, PaymentInfo paymentInfo) {
             stmt.bind("payment_id", paymentInfo.getPaymentId().toString());
@@ -177,13 +163,7 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Tr
         }
     }
 
-    public static class PaymentInfoMapper implements ResultSetMapper<PaymentInfo> {
-
-        private DateTime getDate(ResultSet rs, String fieldName) throws SQLException {
-            final Timestamp resultStamp = rs.getTimestamp(fieldName);
-            return rs.wasNull() ? null : new DateTime(resultStamp).toDateTime(DateTimeZone.UTC);
-        }
-
+    public static class PaymentInfoMapper extends MapperBase implements ResultSetMapper<PaymentInfo> {
         @Override
         public PaymentInfo map(int index, ResultSet rs, StatementContext ctx) throws SQLException {
 
diff --git a/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
index ef492b4..c5a40cf 100644
--- a/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
@@ -78,8 +78,7 @@ public abstract class TestPaymentApi {
                                                        now.plusMonths(1),
                                                        amount,
                                                        new BigDecimal("1.0"),
-                                                       Currency.USD,
-                                                       now));
+                                                       Currency.USD));
 
         List<Either<PaymentError, PaymentInfo>> results = paymentApi.createPayment(account.getExternalKey(), Arrays.asList(invoice.getId().toString()));
 
diff --git a/payment/src/test/java/com/ning/billing/payment/TestHelper.java b/payment/src/test/java/com/ning/billing/payment/TestHelper.java
index d868c9b..08c2a6d 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestHelper.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestHelper.java
@@ -19,6 +19,11 @@ package com.ning.billing.payment;
 import java.math.BigDecimal;
 import java.util.UUID;
 
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.CallOrigin;
+import com.ning.billing.util.UserType;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.entity.DefaultCallContext;
 import com.ning.billing.util.entity.EntityPersistenceException;
 import org.apache.commons.lang.RandomStringUtils;
 import org.joda.time.DateTime;
@@ -38,11 +43,13 @@ import com.ning.billing.invoice.model.RecurringInvoiceItem;
 public class TestHelper {
     protected final AccountDao accountDao;
     protected final InvoiceDao invoiceDao;
+    private final CallContext context;
 
     @Inject
-    public TestHelper(AccountDao accountDao, InvoiceDao invoiceDao) {
+    public TestHelper(Clock clock, AccountDao accountDao, InvoiceDao invoiceDao) {
         this.accountDao = accountDao;
         this.invoiceDao = invoiceDao;
+        context = new DefaultCallContext(clock, "Princess Buttercup", CallOrigin.TEST, UserType.TEST);
     }
 
     // These helper methods can be overridden in a plugin implementation
@@ -57,7 +64,7 @@ public class TestHelper {
                                                                      .currency(Currency.USD)
                                                                      .billingCycleDay(1)
                                                                      .build();
-        accountDao.create(account);
+        accountDao.create(account, context);
         return account;
     }
 
@@ -72,7 +79,7 @@ public class TestHelper {
                                                                      .currency(Currency.USD)
                                                                      .billingCycleDay(1)
                                                                      .build();
-        accountDao.create(account);
+        accountDao.create(account, context);
         return account;
     }
 
@@ -80,7 +87,7 @@ public class TestHelper {
                                      DateTime targetDate,
                                      Currency currency,
                                      InvoiceItem... items) {
-        Invoice invoice = new DefaultInvoice(UUID.randomUUID(), account.getId(), 1, new DateTime(), targetDate, currency);
+        Invoice invoice = new DefaultInvoice(account.getId(), new DateTime(), targetDate, currency);
 
         for (InvoiceItem item : items) {
             if (item instanceof RecurringInvoiceItem) {
@@ -93,11 +100,10 @@ public class TestHelper {
                                                                recurringInvoiceItem.getEndDate(),
                                                                recurringInvoiceItem.getAmount(),
                                                                recurringInvoiceItem.getRate(),
-                                                               recurringInvoiceItem.getCurrency(),
-                                                               recurringInvoiceItem.getCreatedDate()));
+                                                               recurringInvoiceItem.getCurrency()));
             }
         }
-        invoiceDao.create(invoice);
+        invoiceDao.create(invoice, context);
         return invoice;
     }
 
@@ -106,7 +112,7 @@ public class TestHelper {
         final UUID subscriptionId = UUID.randomUUID();
         final BigDecimal amount = new BigDecimal("10.00");
         final InvoiceItem item = new RecurringInvoiceItem(null, subscriptionId, "test plan", "test phase", now, now.plusMonths(1),
-                amount, new BigDecimal("1.0"), Currency.USD, now);
+                amount, new BigDecimal("1.0"), Currency.USD);
 
         return createTestInvoice(account, now, Currency.USD, item);
     }
diff --git a/payment/src/test/java/com/ning/billing/payment/TestRetryService.java b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
index ac33d99..d8ec6e9 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
@@ -29,7 +29,6 @@ import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
 import org.joda.time.DateTime;
 import org.joda.time.Days;
-import org.joda.time.Months;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
@@ -121,8 +120,7 @@ public class TestRetryService {
                                                        endDate,
                                                        amount,
                                                        new BigDecimal("1.0"),
-                                                       Currency.USD,
-                                                       clock.getUTCNow()));
+                                                       Currency.USD));
 
         mockPaymentProviderPlugin.makeNextInvoiceFail();
 
@@ -162,8 +160,7 @@ public class TestRetryService {
                                                        now.plusMonths(1),
                                                        amount,
                                                        new BigDecimal("1.0"),
-                                                       Currency.USD,
-                                                       now));
+                                                       Currency.USD));
 
         int numberOfDays = paymentConfig.getPaymentRetryDays().get(0);
         DateTime nextRetryDate = now.plusDays(numberOfDays);
@@ -172,7 +169,7 @@ public class TestRetryService {
                                                                                       .setPaymentAttemptDate(now)
                                                                                       .build();
 
-        PaymentAttempt attempt = paymentDao.createPaymentAttempt(paymentAttempt);
+        paymentDao.createPaymentAttempt(paymentAttempt);
         retryService.scheduleRetry(paymentAttempt, nextRetryDate);
         ((ClockMock)clock).setDeltaFromReality(Days.days(numberOfDays).toStandardSeconds().getSeconds() * 1000);
         Thread.sleep(2000);
@@ -180,10 +177,10 @@ public class TestRetryService {
         List<Notification> pendingNotifications = mockNotificationQueue.getPendingEvents();
         assertEquals(pendingNotifications.size(), 0);
 
-        List<PaymentInfo> paymentInfos = paymentApi.getPaymentInfo(Arrays.asList(invoice.getId().toString()));
-        assertEquals(paymentInfos.size(), 1);
+        List<PaymentInfo> paymentInfoList = paymentApi.getPaymentInfo(Arrays.asList(invoice.getId().toString()));
+        assertEquals(paymentInfoList.size(), 1);
 
-        PaymentInfo paymentInfo = paymentInfos.get(0);
+        PaymentInfo paymentInfo = paymentInfoList.get(0);
         assertEquals(paymentInfo.getStatus(), PaymentStatus.Processed.toString());
 
         PaymentAttempt updatedAttempt = paymentApi.getPaymentAttemptForInvoiceId(invoice.getId().toString());
diff --git a/util/src/main/java/com/ning/billing/util/customfield/CustomFieldBinder.java b/util/src/main/java/com/ning/billing/util/customfield/CustomFieldBinder.java
new file mode 100644
index 0000000..79fd6ad
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/CustomFieldBinder.java
@@ -0,0 +1,31 @@
+package com.ning.billing.util.customfield;
+
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BinderFactory;
+import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@BindingAnnotation(CustomFieldBinder.CustomFieldBinderFactory.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER})
+public @interface CustomFieldBinder {
+    public static class CustomFieldBinderFactory implements BinderFactory {
+        @Override
+        public Binder build(Annotation annotation) {
+            return new Binder<CustomFieldBinder, CustomField>() {
+                @Override
+                public void bind(SQLStatement q, CustomFieldBinder bind, CustomField customField) {
+                    q.bind("id", customField.getId().toString());
+                    q.bind("fieldName", customField.getName());
+                    q.bind("fieldValue", customField.getValue());
+                }
+            };
+        }
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/customfield/CustomFieldMapper.java b/util/src/main/java/com/ning/billing/util/customfield/CustomFieldMapper.java
new file mode 100644
index 0000000..1a84434
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/CustomFieldMapper.java
@@ -0,0 +1,24 @@
+package com.ning.billing.util.customfield;
+
+import com.ning.billing.util.entity.MapperBase;
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.UUID;
+
+public class CustomFieldMapper extends MapperBase implements ResultSetMapper<CustomField> {
+    @Override
+    public CustomField map(int index, ResultSet result, StatementContext context) throws SQLException {
+        UUID id = UUID.fromString(result.getString("id"));
+        String fieldName = result.getString("field_name");
+        String fieldValue = result.getString("field_value");
+        String createdBy = result.getString("created_by");
+        DateTime createdDate = getDate(result, "created_date");
+        String updatedBy = result.getString("updated_by");
+        DateTime updatedDate = getDate(result, "updated_date");
+        return new StringCustomField(id, createdBy, createdDate, updatedBy, updatedDate, fieldName, fieldValue);
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/customfield/CustomizableEntityBase.java b/util/src/main/java/com/ning/billing/util/customfield/CustomizableEntityBase.java
index ac184be..8be9c43 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/CustomizableEntityBase.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/CustomizableEntityBase.java
@@ -19,12 +19,18 @@ package com.ning.billing.util.customfield;
 import java.util.List;
 import java.util.UUID;
 import com.ning.billing.util.entity.EntityBase;
+import org.joda.time.DateTime;
 
 public abstract class CustomizableEntityBase extends EntityBase implements CustomizableEntity {
     protected final FieldStore fields;
 
-    public CustomizableEntityBase(final UUID id) {
-        super(id);
+    public CustomizableEntityBase() {
+        super();
+        fields = DefaultFieldStore.create(getId(), getObjectName());
+    }
+
+    public CustomizableEntityBase(final UUID id, String createdBy, DateTime createdDate) {
+        super(id, createdBy, createdDate);
         fields = DefaultFieldStore.create(getId(), getObjectName());
     }
 
@@ -44,7 +50,7 @@ public abstract class CustomizableEntityBase extends EntityBase implements Custo
     }
 
     @Override
-    public void addFields(final List<CustomField> fields) {
+    public void setFields(final List<CustomField> fields) {
         if (fields != null) {
             this.fields.add(fields);
         }
diff --git a/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java b/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java
new file mode 100644
index 0000000..ca8061f
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java
@@ -0,0 +1,46 @@
+package com.ning.billing.util.customfield.dao;
+
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.customfield.CustomField;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.UUID;
+
+public class AuditedCustomFieldDao {
+    public void saveFields(Transmogrifier dao, UUID objectId, String objectType, List<CustomField> fields, CallContext context) {
+        CustomFieldSqlDao customFieldSqlDao = dao.become(CustomFieldSqlDao.class);
+
+        // get list of existing fields
+        List<CustomField> existingFields = customFieldSqlDao.load(objectId.toString(), objectType);
+        List<CustomField> fieldsToUpdate = new ArrayList<CustomField>();
+
+        // sort into fields to update (fieldsToUpdate), fields to add (fields), and fields to delete (existingFields)
+        Iterator<CustomField> fieldIterator = fields.iterator();
+        while (fieldIterator.hasNext()) {
+            CustomField field = fieldIterator.next();
+
+            Iterator<CustomField> existingFieldIterator = existingFields.iterator();
+            while (existingFieldIterator.hasNext()) {
+                CustomField existingField = existingFieldIterator.next();
+                if (field.getName().equals(existingField.getName())) {
+                    // if the tags match, remove from both lists
+                    fieldsToUpdate.add(field);
+                    fieldIterator.remove();
+                    existingFieldIterator.remove();
+                }
+            }
+        }
+
+        customFieldSqlDao.batchInsertFromTransaction(objectId.toString(), objectType, fields, context);
+        customFieldSqlDao.batchUpdateFromTransaction(objectId.toString(), objectType, fieldsToUpdate, context);
+        customFieldSqlDao.batchDeleteFromTransaction(objectId.toString(), objectType, existingFields, context);
+
+        CustomFieldAuditSqlDao auditDao = dao.become(CustomFieldAuditSqlDao.class);
+  //      auditDao.batchInsertFromTransaction(objectId.toString(), objectType, fields, context);
+//        auditDao.batchUpdateFromTransaction(objectId.toString(), objectType, fieldsToUpdate, context);
+        auditDao.batchDeleteFromTransaction(objectId.toString(), objectType, existingFields, context);
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldAuditSqlDao.java b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldAuditSqlDao.java
new file mode 100644
index 0000000..23ea555
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldAuditSqlDao.java
@@ -0,0 +1,32 @@
+package com.ning.billing.util.customfield.dao;
+
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.customfield.CustomFieldBinder;
+import com.ning.billing.util.entity.CallContextBinder;
+import com.ning.billing.util.tag.dao.TagBinder;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.SqlBatch;
+import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
+
+import java.util.List;
+
+public interface CustomFieldAuditSqlDao extends Transactional<CustomFieldAuditSqlDao> {
+    @SqlBatch(transactional=false)
+    public void batchInsertFromTransaction(@Bind("objectId") final String objectId,
+                                           @Bind("objectType") final String objectType,
+                                           @CustomFieldBinder final List<CustomField> entities,
+                                           @CallContextBinder final CallContext context);
+
+    @SqlBatch(transactional=false)
+    public void batchUpdateFromTransaction(@Bind("objectId") final String objectId,
+                                           @Bind("objectType") final String objectType,
+                                           @CustomFieldBinder final List<CustomField> entities,
+                                           @CallContextBinder final CallContext context);
+
+    @SqlBatch(transactional=false)
+    public void batchDeleteFromTransaction(@Bind("objectId") final String objectId,
+                                           @Bind("objectType") final String objectType,
+                                           @CustomFieldBinder final List<CustomField> entities,
+                                           @CallContextBinder final CallContext context);
+}
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
new file mode 100644
index 0000000..c6984e7
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.java
@@ -0,0 +1,57 @@
+/*
+ * 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 com.ning.billing.util.CallContext;
+import com.ning.billing.util.customfield.CustomFieldBinder;
+import com.ning.billing.util.customfield.CustomFieldMapper;
+import com.ning.billing.util.entity.CallContextBinder;
+import com.ning.billing.util.entity.UpdatableEntityCollectionDao;
+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 com.ning.billing.util.customfield.CustomField;
+
+@ExternalizedSqlViaStringTemplate3
+@RegisterMapper(CustomFieldMapper.class)
+public interface CustomFieldSqlDao extends UpdatableEntityCollectionDao<CustomField>, Transactional<CustomFieldSqlDao>, Transmogrifier {
+    @Override
+    @SqlBatch(transactional=false)
+    public void batchInsertFromTransaction(@Bind("objectId") final String objectId,
+                                           @Bind("objectType") final String objectType,
+                                           @CustomFieldBinder final List<CustomField> entities,
+                                           @CallContextBinder final CallContext context);
+
+    @Override
+    @SqlBatch(transactional=false)
+    public void batchUpdateFromTransaction(@Bind("objectId") final String objectId,
+                                           @Bind("objectType") final String objectType,
+                                           @CustomFieldBinder final List<CustomField> entities,
+                                           @CallContextBinder final CallContext context);
+
+    @Override
+    @SqlBatch(transactional=false)
+    public void batchDeleteFromTransaction(@Bind("objectId") final String objectId,
+                                           @Bind("objectType") final String objectType,
+                                           @CustomFieldBinder final List<CustomField> entities,
+                                           @CallContextBinder final CallContext context);
+}
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 4e04ff3..f60093c 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
@@ -17,18 +17,22 @@
 package com.ning.billing.util.customfield;
 
 import java.util.UUID;
-import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.UpdatableEntityBase;
+import org.joda.time.DateTime;
 
-public class StringCustomField extends EntityBase<CustomField> implements CustomField {
+public class StringCustomField extends UpdatableEntityBase implements CustomField {
     private String name;
     private String value;
 
     public StringCustomField(String name, String value) {
-        this(UUID.randomUUID(), name, value);
+        super();
+        this.name = name;
+        this.value = value;
     }
 
-    public StringCustomField(UUID id, String name, String value) {
-        super(id);
+    public StringCustomField(UUID id, String createdBy, DateTime createdDate,
+                             String updatedBy, DateTime updatedDate, String name, String value) {
+        super(id, createdBy, createdDate, updatedBy, updatedDate);
         this.name = name;
         this.value = value;
     }
diff --git a/util/src/main/java/com/ning/billing/util/entity/BinderBase.java b/util/src/main/java/com/ning/billing/util/entity/BinderBase.java
new file mode 100644
index 0000000..ec516ed
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/BinderBase.java
@@ -0,0 +1,11 @@
+package com.ning.billing.util.entity;
+
+import org.joda.time.DateTime;
+
+import java.util.Date;
+
+public abstract class BinderBase {
+    protected Date getDate(DateTime dateTime) {
+        return dateTime == null ? null : dateTime.toDate();
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/DefaultCallContext.java b/util/src/main/java/com/ning/billing/util/entity/DefaultCallContext.java
new file mode 100644
index 0000000..fea87c6
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/DefaultCallContext.java
@@ -0,0 +1,57 @@
+/*
+ * 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.entity;
+
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.CallOrigin;
+import com.ning.billing.util.UserType;
+import com.ning.billing.util.clock.Clock;
+import org.joda.time.DateTime;
+
+public class DefaultCallContext implements CallContext {
+    private final Clock clock;
+    private final String userName;
+    private final CallOrigin callOrigin;
+    private final UserType userType;
+
+    public DefaultCallContext(Clock clock, String userName, CallOrigin callOrigin, UserType userType) {
+        this.clock = clock;
+        this.userName = userName;
+        this.callOrigin = callOrigin;
+        this.userType = userType;
+    }
+
+    @Override
+    public String getUserName() {
+        return userName;
+    }
+
+    @Override
+    public CallOrigin getCallOrigin() {
+        return callOrigin;
+    }
+
+    @Override
+    public UserType getUserType() {
+        return userType;
+    }
+
+    @Override
+    public DateTime getUTCNow() {
+        return clock.getUTCNow();
+    }
+}
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 3cb71f8..4de0312 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
@@ -16,21 +16,41 @@
 
 package com.ning.billing.util.entity;
 
+import org.joda.time.DateTime;
+
 import java.util.UUID;
 
-public abstract class EntityBase<T> implements Entity {
+public abstract class EntityBase implements Entity {
     protected final UUID id;
+    protected final String createdBy;
+    protected final DateTime createdDate;
 
-    public EntityBase(UUID id) {
+    // used to hydrate objects
+    public EntityBase(UUID id, String createdBy, DateTime createdDate) {
         this.id = id;
+        this.createdBy = createdBy;
+        this.createdDate = createdDate;
     }
 
+    // used to create new objects
     public EntityBase() {
-        this(UUID.randomUUID());
+        this.id = UUID.randomUUID();
+        this.createdBy = null;
+        this.createdDate = null;
     }
 
     @Override
     public UUID getId() {
         return id;
     }
+
+    @Override
+    public String getCreatedBy() {
+        return createdBy;
+    }
+
+    @Override
+    public DateTime getCreatedDate() {
+        return createdDate;
+    }
 }
\ No newline at end of file
diff --git a/util/src/main/java/com/ning/billing/util/entity/EntityCollectionDao.java b/util/src/main/java/com/ning/billing/util/entity/EntityCollectionDao.java
index 11d149c..ce84966 100644
--- a/util/src/main/java/com/ning/billing/util/entity/EntityCollectionDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/EntityCollectionDao.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.util.entity;
 
+import com.ning.billing.util.CallContext;
 import org.skife.jdbi.v2.sqlobject.*;
 
 import java.util.List;
@@ -26,15 +27,17 @@ import java.util.List;
  * @param <T>
  */
 public interface EntityCollectionDao<T extends Entity> {
-
     @SqlBatch(transactional=false)
-    public void batchSaveFromTransaction(@Bind("objectId") final String objectId,
-                     @Bind("objectType") final String objectType,
-                     @BindBean final List<T> entities);
+    public void batchInsertFromTransaction(@Bind("objectId") final String objectId,
+                                           @Bind("objectType") final String objectType,
+                                           @BindBean final List<T> entities,
+                                           @CallContextBinder final CallContext context);
 
-    @SqlUpdate
-    public void clear(@Bind("objectId") final String objectId,
-                      @Bind("objectType") final String objectType);
+    @SqlBatch(transactional=false)
+    public void batchDeleteFromTransaction(@Bind("objectId") final String objectId,
+                                           @Bind("objectType") final String objectType,
+                                           @BindBean final List<T> entities,
+                                           @CallContextBinder final CallContext context);
 
     @SqlQuery
     public List<T> load(@Bind("objectId") final String objectId,
diff --git a/util/src/main/java/com/ning/billing/util/entity/EntityDao.java b/util/src/main/java/com/ning/billing/util/entity/EntityDao.java
index 1337950..d44e766 100644
--- a/util/src/main/java/com/ning/billing/util/entity/EntityDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/EntityDao.java
@@ -18,16 +18,15 @@ package com.ning.billing.util.entity;
 
 import java.util.List;
 
+import com.ning.billing.util.CallContext;
 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 com.ning.billing.account.api.AccountApiException;
-
 public interface EntityDao<T extends Entity> {
     @SqlUpdate
-    public void create(@BindBean final T entity) throws EntityPersistenceException;
+    public void create(@BindBean final T entity, @CallContextBinder final CallContext context) throws EntityPersistenceException;
 
     @SqlQuery
     public T getById(@Bind("id") final String id);
diff --git a/util/src/main/java/com/ning/billing/util/entity/MapperBase.java b/util/src/main/java/com/ning/billing/util/entity/MapperBase.java
new file mode 100644
index 0000000..ce08d3b
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/MapperBase.java
@@ -0,0 +1,16 @@
+package com.ning.billing.util.entity;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.Date;
+
+public abstract class MapperBase {
+    protected DateTime getDate(ResultSet rs, String fieldName) throws SQLException {
+        final Timestamp resultStamp = rs.getTimestamp(fieldName);
+        return rs.wasNull() ? null : new DateTime(resultStamp).toDateTime(DateTimeZone.UTC);
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/UpdatableEntityBase.java b/util/src/main/java/com/ning/billing/util/entity/UpdatableEntityBase.java
new file mode 100644
index 0000000..3aaffbc
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/UpdatableEntityBase.java
@@ -0,0 +1,48 @@
+/*
+ * 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.entity;
+
+import org.joda.time.DateTime;
+
+import java.util.UUID;
+
+public abstract class UpdatableEntityBase extends EntityBase implements UpdatableEntity {
+    private final String updatedBy;
+    private final DateTime updatedDate;
+
+    public UpdatableEntityBase() {
+        super();
+        this.updatedBy = null;
+        this.updatedDate = null;
+    }
+
+    public UpdatableEntityBase(UUID id, String createdBy, DateTime createdDate, String updatedBy, DateTime updatedDate) {
+        super(id, createdBy, createdDate);
+        this.updatedBy = updatedBy;
+        this.updatedDate = updatedDate;
+    }
+
+    @Override
+    public String getUpdatedBy() {
+        return updatedBy;
+    }
+
+    @Override
+    public DateTime getUpdatedDate() {
+        return updatedDate;
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/UpdatableEntityCollectionDao.java b/util/src/main/java/com/ning/billing/util/entity/UpdatableEntityCollectionDao.java
new file mode 100644
index 0000000..6156b27
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/UpdatableEntityCollectionDao.java
@@ -0,0 +1,17 @@
+package com.ning.billing.util.entity;
+
+import com.ning.billing.util.CallContext;
+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.SqlUpdate;
+
+import java.util.List;
+
+public interface UpdatableEntityCollectionDao<T extends Entity> extends EntityCollectionDao<T> {
+    @SqlBatch(transactional=false)
+    public void batchUpdateFromTransaction(@Bind("objectId") final String objectId,
+                                           @Bind("objectType") final String objectType,
+                                           @BindBean final List<T> entities,
+                                           @CallContextBinder final CallContext context);
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/UpdatableEntityDao.java b/util/src/main/java/com/ning/billing/util/entity/UpdatableEntityDao.java
index eb4adc1..56429a5 100644
--- a/util/src/main/java/com/ning/billing/util/entity/UpdatableEntityDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/UpdatableEntityDao.java
@@ -16,10 +16,12 @@
 
 package com.ning.billing.util.entity;
 
+import com.ning.billing.util.CallContext;
 import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 
 public interface UpdatableEntityDao<T extends UpdatableEntity> extends EntityDao<T> {
     @SqlUpdate
-    public void update(@BindBean final T entity) throws EntityPersistenceException;
+    public void update(@BindBean final T entity, @CallContextBinder final CallContext context)
+            throws EntityPersistenceException;
 }
diff --git a/util/src/main/java/com/ning/billing/util/glue/FieldStoreModule.java b/util/src/main/java/com/ning/billing/util/glue/FieldStoreModule.java
new file mode 100644
index 0000000..4776cbe
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/glue/FieldStoreModule.java
@@ -0,0 +1,10 @@
+package com.ning.billing.util.glue;
+
+import com.google.inject.AbstractModule;
+
+public class FieldStoreModule extends AbstractModule {
+    @Override
+    protected void configure() {
+
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/glue/TagStoreDaoProvider.java b/util/src/main/java/com/ning/billing/util/glue/TagStoreDaoProvider.java
index aa0f080..7b73f06 100644
--- a/util/src/main/java/com/ning/billing/util/glue/TagStoreDaoProvider.java
+++ b/util/src/main/java/com/ning/billing/util/glue/TagStoreDaoProvider.java
@@ -18,10 +18,10 @@ package com.ning.billing.util.glue;
 
 import com.google.inject.Inject;
 import com.google.inject.Provider;
-import com.ning.billing.util.tag.dao.TagStoreSqlDao;
+import com.ning.billing.util.tag.dao.TagSqlDao;
 import org.skife.jdbi.v2.IDBI;
 
-public class TagStoreDaoProvider implements Provider<TagStoreSqlDao>
+public class TagStoreDaoProvider implements Provider<TagSqlDao>
 {
     private final IDBI dbi;
 
@@ -32,8 +32,8 @@ public class TagStoreDaoProvider implements Provider<TagStoreSqlDao>
     }
 
     @Override
-    public TagStoreSqlDao get()
+    public TagSqlDao get()
     {
-        return dbi.onDemand(TagStoreSqlDao.class);
+        return dbi.onDemand(TagSqlDao.class);
     }
 }
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 a598275..6c3856b 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
@@ -22,14 +22,14 @@ import com.ning.billing.util.tag.api.DefaultTagDefinitionUserApi;
 import com.ning.billing.util.tag.dao.DefaultTagDefinitionDao;
 import com.ning.billing.util.tag.dao.TagDefinitionDao;
 import com.ning.billing.util.tag.dao.TagDefinitionSqlDao;
-import com.ning.billing.util.tag.dao.TagStoreSqlDao;
+import com.ning.billing.util.tag.dao.TagSqlDao;
 
 public class TagStoreModule extends AbstractModule
 {
     protected void installDaos() {
         bind(TagDefinitionSqlDao.class).toProvider(TagDescriptionDaoProvider.class).asEagerSingleton();
         bind(TagDefinitionDao.class).to(DefaultTagDefinitionDao.class).asEagerSingleton();
-        bind(TagStoreSqlDao.class).toProvider(TagStoreDaoProvider.class).asEagerSingleton();
+        bind(TagSqlDao.class).toProvider(TagStoreDaoProvider.class).asEagerSingleton();
     }
 
     @Override
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/dao/NotificationSqlDao.java b/util/src/main/java/com/ning/billing/util/notificationq/dao/NotificationSqlDao.java
index 2f511ec..9253e2d 100644
--- a/util/src/main/java/com/ning/billing/util/notificationq/dao/NotificationSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/notificationq/dao/NotificationSqlDao.java
@@ -23,6 +23,8 @@ import java.util.Date;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.entity.BinderBase;
+import com.ning.billing.util.entity.MapperBase;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.skife.jdbi.v2.SQLStatement;
@@ -63,12 +65,7 @@ public interface NotificationSqlDao extends Transactional<NotificationSqlDao>, C
     @SqlUpdate
     public void insertClaimedHistory(@Bind("sequence_id") int sequenceId, @Bind("owner") String owner, @Bind("claimed_dt") Date clainedDate, @Bind("notification_id") String notificationId);
 
-    public static class NotificationSqlDaoBinder implements Binder<Bind, Notification> {
-
-        private Date getDate(DateTime dateTime) {
-            return dateTime == null ? null : dateTime.toDate();
-        }
-
+    public static class NotificationSqlDaoBinder extends BinderBase implements Binder<Bind, Notification> {
         @Override
         public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, Notification evt) {
             stmt.bind("notification_id", evt.getUUID().toString());
@@ -83,13 +80,7 @@ public interface NotificationSqlDao extends Transactional<NotificationSqlDao>, C
     }
 
 
-    public static class NotificationSqlMapper implements ResultSetMapper<Notification> {
-
-        private DateTime getDate(ResultSet r, String fieldName) throws SQLException {
-            final Timestamp resultStamp = r.getTimestamp(fieldName);
-            return r.wasNull() ? null : new DateTime(resultStamp).toDateTime(DateTimeZone.UTC);
-        }
-
+    public static class NotificationSqlMapper extends MapperBase implements ResultSetMapper<Notification> {
         @Override
         public Notification map(int index, ResultSet r, StatementContext ctx)
         throws SQLException {
diff --git a/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagDefinitionUserApi.java b/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagDefinitionUserApi.java
index 452eb5e..42d0f15 100644
--- a/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagDefinitionUserApi.java
+++ b/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagDefinitionUserApi.java
@@ -18,6 +18,7 @@ package com.ning.billing.util.tag.api;
 
 import java.util.List;
 import com.google.inject.Inject;
+import com.ning.billing.util.CallContext;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.api.TagDefinitionUserApi;
 import com.ning.billing.util.tag.TagDefinition;
@@ -37,17 +38,18 @@ public class DefaultTagDefinitionUserApi implements TagDefinitionUserApi {
     }
 
     @Override
-    public TagDefinition create(final String name, final String description, final String createdBy) throws TagDefinitionApiException {
-        return dao.create(name, description, createdBy);
+    public TagDefinition create(final String name, final String description, final CallContext context) throws TagDefinitionApiException {
+        return dao.create(name, description, context);
     }
 
     @Override
-    public void deleteAllTagsForDefinition(final String definitionName) throws TagDefinitionApiException {
-        dao.deleteAllTagsForDefinition(definitionName);
+    public void deleteAllTagsForDefinition(final String definitionName, final CallContext context)
+            throws TagDefinitionApiException {
+        dao.deleteAllTagsForDefinition(definitionName, context);
     }
 
     @Override
-    public void deleteTagDefinition(final String definitionName) throws TagDefinitionApiException {
-        dao.deleteAllTagsForDefinition(definitionName);
+    public void deleteTagDefinition(final String definitionName, final CallContext context) throws TagDefinitionApiException {
+        dao.deleteAllTagsForDefinition(definitionName, context);
     }
 }
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java
new file mode 100644
index 0000000..d81d9db
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java
@@ -0,0 +1,42 @@
+package com.ning.billing.util.tag.dao;
+
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.tag.Tag;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.UUID;
+
+public class AuditedTagDao {
+    public void saveTags(Transmogrifier dao, UUID objectId, String objectType, List<Tag> tags, CallContext context) {
+        TagSqlDao tagSqlDao = dao.become(TagSqlDao.class);
+
+        // get list of existing tags
+        List<Tag> existingTags = tagSqlDao.load(objectId.toString(), objectType);
+
+        // sort into tags to update (tagsToUpdate), tags to add (tags), and tags to delete (existingTags)
+        Iterator<Tag> tagIterator = tags.iterator();
+        while (tagIterator.hasNext()) {
+            Tag tag = tagIterator.next();
+
+            Iterator<Tag> existingTagIterator = existingTags.iterator();
+            while (existingTagIterator.hasNext()) {
+                Tag existingTag = existingTagIterator.next();
+                if (tag.getTagDefinitionName().equals(existingTag.getTagDefinitionName())) {
+                    // if the tags match, remove from both lists
+                    // in the case of tag, this just means the tag remains associated
+                    tagIterator.remove();
+                    existingTagIterator.remove();
+                }
+            }
+        }
+
+        tagSqlDao.batchInsertFromTransaction(objectId.toString(), objectType, tags, context);
+        tagSqlDao.batchDeleteFromTransaction(objectId.toString(), objectType, existingTags, context);
+
+        TagAuditSqlDao auditDao = dao.become(TagAuditSqlDao.class);
+        auditDao.batchInsertFromTransaction(objectId.toString(), objectType, tags, context);
+        auditDao.batchDeleteFromTransaction(objectId.toString(), objectType, existingTags, context);
+    }
+}
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 baaf9cd..74a998f 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
@@ -18,24 +18,22 @@ package com.ning.billing.util.tag.dao;
 
 import java.util.ArrayList;
 import java.util.List;
+
+import com.ning.billing.util.CallContext;
 import org.skife.jdbi.v2.IDBI;
 import com.google.inject.Inject;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.ControlTagType;
 import com.ning.billing.util.api.TagDefinitionApiException;
-import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.tag.DefaultTagDefinition;
-import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.TagDefinition;
 
 public class DefaultTagDefinitionDao implements TagDefinitionDao {
     private final TagDefinitionSqlDao dao;
-    private final Clock clock;
 
     @Inject
-    public DefaultTagDefinitionDao(IDBI dbi, Clock clock) {
+    public DefaultTagDefinitionDao(IDBI dbi) {
         this.dao = dbi.onDemand(TagDefinitionSqlDao.class);
-        this.clock = clock;
     }
 
     @Override
@@ -46,7 +44,7 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
 
         // add control tag definitions
         for (ControlTagType controlTag : ControlTagType.values()) {
-            definitionList.add(new DefaultTagDefinition(controlTag.toString(), controlTag.getDescription(), null));
+            definitionList.add(new DefaultTagDefinition(controlTag.toString(), controlTag.getDescription()));
         }
 
         return definitionList;
@@ -58,7 +56,8 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
     }
 
     @Override
-    public TagDefinition create(final String definitionName, final String description, final String createdBy) throws TagDefinitionApiException {
+    public TagDefinition create(final String definitionName, final String description,
+                                final CallContext context) throws TagDefinitionApiException {
         if (isControlTagName(definitionName)) {
             throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_CONFLICTS_WITH_CONTROL_TAG, definitionName);
         }
@@ -69,8 +68,8 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
             throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_ALREADY_EXISTS, definitionName);
         }
 
-        TagDefinition definition = new DefaultTagDefinition(definitionName, description, createdBy);
-        dao.create(definition);
+        TagDefinition definition = new DefaultTagDefinition(definitionName, description);
+        dao.create(definition, context);
         return definition;
     }
 
@@ -85,17 +84,17 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
     }
 
     @Override
-    public void deleteAllTagsForDefinition(final String definitionName) throws TagDefinitionApiException {
+    public void deleteAllTagsForDefinition(final String definitionName, final CallContext context) throws TagDefinitionApiException {
         TagDefinition existingDefinition = dao.getByName(definitionName);
         if (existingDefinition == null) {
             throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_DOES_NOT_EXIST, definitionName);
         }
 
-        dao.deleteAllTagsForDefinition(definitionName);
+        dao.deleteAllTagsForDefinition(definitionName, context);
     }
 
     @Override
-    public void deleteTagDefinition(final String definitionName) throws TagDefinitionApiException {
+    public void deleteTagDefinition(final String definitionName, final CallContext context) throws TagDefinitionApiException {
         if (dao.tagDefinitionUsageCount(definitionName) > 0) {
             throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_IN_USE, definitionName);
         }
@@ -106,6 +105,6 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
             throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_DOES_NOT_EXIST, definitionName);
         }
 
-        dao.deleteTagDefinition(definitionName);
+        dao.deleteTagDefinition(definitionName, context);
     }
 }
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagAuditSqlDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagAuditSqlDao.java
new file mode 100644
index 0000000..3298768
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagAuditSqlDao.java
@@ -0,0 +1,29 @@
+package com.ning.billing.util.tag.dao;
+
+
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.entity.CallContextBinder;
+import com.ning.billing.util.tag.Tag;
+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.stringtemplate.ExternalizedSqlViaStringTemplate3;
+
+import java.util.List;
+
+@ExternalizedSqlViaStringTemplate3
+@RegisterMapper(TagMapper.class)
+public interface TagAuditSqlDao extends Transactional<TagAuditSqlDao> {
+    @SqlBatch(transactional=false)
+    public void batchInsertFromTransaction(@Bind("objectId") final String objectId,
+                                           @Bind("objectType") final String objectType,
+                                           @TagBinder final List<Tag> entities,
+                                           @CallContextBinder final CallContext context);
+
+    @SqlBatch(transactional=false)
+    public void batchDeleteFromTransaction(@Bind("objectId") final String objectId,
+                                           @Bind("objectType") final String objectType,
+                                           @TagBinder final List<Tag> entities,
+                                           @CallContextBinder final CallContext context);
+}
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagBinder.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagBinder.java
index d56257d..2416b21 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagBinder.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagBinder.java
@@ -21,6 +21,9 @@ import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+
+import com.google.inject.Inject;
+import com.ning.billing.util.clock.Clock;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.sqlobject.Binder;
 import org.skife.jdbi.v2.sqlobject.BinderFactory;
@@ -37,8 +40,6 @@ public @interface TagBinder {
                 public void bind(SQLStatement q, TagBinder bind, Tag tag) {
                     q.bind("id", tag.getId().toString());
                     q.bind("tagDefinitionName", tag.getTagDefinitionName());
-                    q.bind("addedDate", tag.getAddedDate().toDate());
-                    q.bind("addedBy", tag.getAddedBy());
                 }
             };
         }
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
new file mode 100644
index 0000000..549e414
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
@@ -0,0 +1,4 @@
+package com.ning.billing.util.tag.dao;
+
+public interface TagDao {
+}
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 3164518..a148a05 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
@@ -17,6 +17,8 @@
 package com.ning.billing.util.tag.dao;
 
 import java.util.List;
+
+import com.ning.billing.util.CallContext;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.tag.TagDefinition;
 
@@ -25,9 +27,9 @@ public interface TagDefinitionDao {
 
     public TagDefinition getByName(String definitionName);
 
-    public TagDefinition create(String definitionName, String description, String createdBy) throws TagDefinitionApiException;
+    public TagDefinition create(String definitionName, String description, CallContext context) throws TagDefinitionApiException;
 
-    public void deleteAllTagsForDefinition(String definitionName) throws TagDefinitionApiException;
+    public void deleteAllTagsForDefinition(String definitionName, CallContext context) throws TagDefinitionApiException;
 
-    public void deleteTagDefinition(String definitionName) throws TagDefinitionApiException;
+    public void deleteTagDefinition(String definitionName, CallContext context) throws TagDefinitionApiException;
 }
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 86e1b5b..4a4b842 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
@@ -24,8 +24,10 @@ import java.lang.annotation.Target;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.UUID;
+
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.entity.CallContextBinder;
 import com.ning.billing.util.entity.EntityDao;
-import com.ning.billing.util.entity.UpdatableEntityDao;
 import com.ning.billing.util.tag.DefaultTagDefinition;
 import com.ning.billing.util.tag.TagDefinition;
 import org.joda.time.DateTime;
@@ -43,20 +45,16 @@ import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
 @ExternalizedSqlViaStringTemplate3
 @RegisterMapper(TagDefinitionSqlDao.TagDefinitionMapper.class)
-public interface TagDefinitionSqlDao extends UpdatableEntityDao<TagDefinition> {
-    @Override
-    @SqlUpdate
-    public void create(@TagDefinitionBinder final TagDefinition entity);
-
+public interface TagDefinitionSqlDao extends EntityDao<TagDefinition> {
     @Override
     @SqlUpdate
-    public void update(@TagDefinitionBinder final TagDefinition entity);
+    public void create(@TagDefinitionBinder final TagDefinition entity, @CallContextBinder final CallContext context);
 
     @SqlUpdate
-    public void deleteAllTagsForDefinition(@Bind("name") final String definitionName);
+    public void deleteAllTagsForDefinition(@Bind("name") final String definitionName, @CallContextBinder final CallContext context);
 
     @SqlUpdate
-    public void deleteTagDefinition(@Bind("name") final String definitionName);
+    public void deleteTagDefinition(@Bind("name") final String definitionName, @CallContextBinder final CallContext context);
 
     @SqlQuery
     public int tagDefinitionUsageCount(@Bind("name") final String definitionName);
@@ -71,7 +69,8 @@ public interface TagDefinitionSqlDao extends UpdatableEntityDao<TagDefinition> {
             String name = result.getString("name");
             String description = result.getString("description");
             String createdBy = result.getString("created_by");
-            return new DefaultTagDefinition(id, name, description, createdBy);
+            DateTime createdDate = new DateTime(result.getTimestamp("created_date"));
+            return new DefaultTagDefinition(id, createdBy, createdDate, name, description);
         }
     }
 
@@ -85,7 +84,6 @@ public interface TagDefinitionSqlDao extends UpdatableEntityDao<TagDefinition> {
                     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("createdBy", tagDefinition.getCreatedBy());
                         q.bind("description", tagDefinition.getDescription());
                     }
                 };
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java
index fadf98c..1721550 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java
@@ -19,38 +19,30 @@ package com.ning.billing.util.tag.dao;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.UUID;
+
+import com.ning.billing.util.entity.MapperBase;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 import com.ning.billing.account.api.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;
 
-public class TagMapper implements ResultSetMapper<Tag> {
+public class TagMapper extends MapperBase implements ResultSetMapper<Tag> {
     @Override
     public Tag map(final int index, final ResultSet result, final StatementContext context) throws SQLException {
         String name = result.getString("tag_definition_name");
 
-        UUID id = UUID.fromString(result.getString("id"));
-        String addedBy = result.getString("added_by");
-        DateTime addedDate = new DateTime(result.getTimestamp("added_date"));
-
-        Tag tag;
         try {
             ControlTagType controlTagType = ControlTagType.valueOf(name);
-            tag = new DefaultControlTag(id, addedBy, addedDate, controlTagType);
+            return new DefaultControlTag(controlTagType);
         } catch (Throwable t) {
-            String description = result.getString("tag_description");
+            UUID id = UUID.fromString(result.getString("id"));
             String createdBy = result.getString("created_by");
+            DateTime createdDate = new DateTime(result.getTimestamp("created_date"));
 
-            UUID tagDefinitionId = UUID.fromString(result.getString("tag_definition_id"));
-            TagDefinition tagDefinition = new DefaultTagDefinition(tagDefinitionId, name, description, createdBy);
-            tag = new DescriptiveTag(id, tagDefinition, addedBy, addedDate);
+            return new DescriptiveTag(id, createdBy, createdDate, name);
         }
-
-        return tag;
     }
 }
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 707526d..8a2af8f 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
@@ -17,21 +17,22 @@
 package com.ning.billing.util.tag;
 
 import java.util.UUID;
-import org.joda.time.DateTime;
 import com.ning.billing.account.api.ControlTagType;
+import org.joda.time.DateTime;
 
 public class DefaultControlTag extends DescriptiveTag implements ControlTag {
     private final ControlTagType controlTagType;
 
-    public DefaultControlTag(final String addedBy,
-                             final DateTime addedDate, final ControlTagType controlTagType) {
-        this(UUID.randomUUID(), addedBy, addedDate, controlTagType);
+    // use to create new objects
+    public DefaultControlTag(final ControlTagType controlTagType) {
+        super(controlTagType.toString());
+        this.controlTagType = controlTagType;
     }
 
-    public DefaultControlTag(final UUID id, final String addedBy,
-                             final DateTime addedDate, final ControlTagType controlTagType) {
-
-        super(id, controlTagType.toString(), addedBy, addedDate);
+    // use to hydrate objects when loaded from the persistence layer
+    public DefaultControlTag(final UUID id, final String createdBy,
+                             final DateTime createdDate, final ControlTagType controlTagType) {
+        super(id, createdBy, createdDate, controlTagType.toString());
         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 482e56d..0eb0ac9 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
@@ -18,23 +18,22 @@ package com.ning.billing.util.tag;
 
 import java.util.UUID;
 import com.ning.billing.util.entity.EntityBase;
+import org.joda.time.DateTime;
 
 public class DefaultTagDefinition extends EntityBase implements TagDefinition {
     private String name;
     private String description;
-    private String createdBy;
 
-    public DefaultTagDefinition(String name, String description,
-                                String createdBy) {
-        this(UUID.randomUUID(), name, description, createdBy);
+    public DefaultTagDefinition(String name, String description) {
+        super();
+        this.name = name;
+        this.description = description;
     }
 
-    public DefaultTagDefinition(UUID id, String name, String description,
-                                String createdBy) {
-        super(id);
+    public DefaultTagDefinition(UUID id, String createdBy, DateTime createdDate, String name, String description) {
+        super(id, createdBy, createdDate);
         this.name = name;
         this.description = description;
-        this.createdBy = createdBy;
     }
     
     @Override
@@ -43,11 +42,6 @@ public class DefaultTagDefinition extends EntityBase implements TagDefinition {
     }
 
     @Override
-    public String getCreatedBy() {
-        return createdBy;
-    }
-
-    @Override
     public String getDescription() {
         return description;
     }
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 ced1938..1ad70fe 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
@@ -17,41 +17,39 @@
 package com.ning.billing.util.tag;
 
 import java.util.UUID;
-import org.joda.time.DateTime;
+
+import com.google.inject.Inject;
+import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.entity.UpdatableEntityBase;
+import org.joda.time.DateTime;
 
 public class DescriptiveTag extends EntityBase implements Tag {
     private final String tagDefinitionName;
-    private final String addedBy;
-    private final DateTime addedDate;
 
-    public DescriptiveTag(UUID id, String tagDefinitionName, String addedBy, DateTime addedDate) {
-        super(id);
+    @Inject
+    private Clock clock;
+
+    // use to hydrate objects from the persistence layer
+    public DescriptiveTag(UUID id, String createdBy, DateTime createdDate, String tagDefinitionName) {
+        super(id, createdBy, createdDate);
         this.tagDefinitionName = tagDefinitionName;
-        this.addedBy = addedBy;
-        this.addedDate = addedDate;
     }
 
-    public DescriptiveTag(UUID id, TagDefinition tagDefinition, String addedBy, DateTime addedDate) {
-        this(id, tagDefinition.getName(), addedBy, addedDate);
+    // use to create new objects
+    public DescriptiveTag(TagDefinition tagDefinition) {
+        super();
+        this.tagDefinitionName = tagDefinition.getName();
     }
 
-    public DescriptiveTag(TagDefinition tagDefinition, String addedBy, DateTime addedDate) {
-        this(UUID.randomUUID(), tagDefinition.getName(), addedBy, addedDate);
+    // use to create new objects
+    public DescriptiveTag(String tagDefinitionName) {
+        super();
+        this.tagDefinitionName = tagDefinitionName;
     }
 
     @Override
     public String getTagDefinitionName() {
         return tagDefinitionName;
     }
-
-    @Override
-    public String getAddedBy() {
-        return addedBy;
-    }
-
-    @Override
-    public DateTime getAddedDate() {
-        return addedDate;
-    }
 }
diff --git a/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldAuditSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldAuditSqlDao.sql.stg
new file mode 100644
index 0000000..446d9c8
--- /dev/null
+++ b/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldAuditSqlDao.sql.stg
@@ -0,0 +1,27 @@
+group CustomFieldAuditSqlDao;
+
+fields(prefix) ::= <<
+    <prefix>table_name,
+    <prefix>record_id,
+    <prefix>change_type,
+    <prefix>change_date,
+    <prefix>changed_by,
+    <prefix>reason_code,
+    <prefix>comment
+>>
+
+batchInsertFromTransaction() ::= <<
+    INSERT INTO audit_log(<fields()>)
+    VALUES('custom_field', :id, 'INSERT', :date, :userName, NULL, NULL);
+>>
+
+batchDeleteFromTransaction() ::= <<
+    INSERT INTO audit_log(<fields()>)
+    VALUES('custom_field', :id, 'DELETE', :date, :userName, NULL, NULL);
+>>
+
+batchUpdateFromTransaction() ::= <<
+    INSERT INTO audit_log(<fields()>)
+    VALUES('custom_field', :id, 'UPDATE', :date, :userName, NULL, NULL);
+>>
+;
\ No newline at end of file
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 f76795a..a0878d8 100644
--- a/util/src/main/resources/com/ning/billing/util/ddl.sql
+++ b/util/src/main/resources/com/ning/billing/util/ddl.sql
@@ -5,8 +5,10 @@ CREATE TABLE custom_fields (
   object_type varchar(30) NOT NULL,
   field_name varchar(30) NOT NULL,
   field_value varchar(255),
+  created_by varchar(30) NOT NULL,
   created_date datetime NOT NULL,
-  updated_date datetime NOT NULL,
+  updated_by varchar(30) DEFAULT NULL,
+  updated_date datetime DEFAULT NULL,
   PRIMARY KEY(id)
 ) ENGINE=innodb;
 CREATE INDEX custom_fields_object_id_object_type ON custom_fields(object_id, object_type);
@@ -20,34 +22,19 @@ CREATE TABLE custom_field_history (
   field_name varchar(30),
   field_value varchar(255),
   date datetime NOT NULL,
+  updated_by varchar(30) NOT NULL,
   change_type char(6) NOT NULL
 ) ENGINE=innodb;
 CREATE INDEX custom_field_history_object_id_object_type ON custom_fields(object_id, object_type);
 
-CREATE TRIGGER store_custom_field_history_on_insert AFTER INSERT ON custom_fields
-    FOR EACH ROW
-        INSERT INTO custom_field_history (id, object_id, object_type, field_name, field_value, date, change_type)
-        VALUES (NEW.id, NEW.object_id, NEW.object_type, NEW.field_name, NEW.field_value, NOW(), 'CREATE');
-
-CREATE TRIGGER store_custom_field_history_on_update AFTER UPDATE ON custom_fields
-    FOR EACH ROW
-        INSERT INTO custom_field_history (id, object_id, object_type, field_name, field_value, date, change_type)
-        VALUES (NEW.id, NEW.object_id, NEW.object_type, NEW.field_name, NEW.field_value, NOW(), 'UPDATE');
-
-CREATE TRIGGER store_custom_field_history_on_delete BEFORE DELETE ON custom_fields
-    FOR EACH ROW
-        INSERT INTO custom_field_history (id, object_id, object_type, field_name, field_value, date, change_type)
-        VALUES (OLD.id, OLD.object_id, OLD.object_type, NULL, NULL, NOW(), 'DELETE');
-
 DROP TABLE IF EXISTS tag_descriptions;
 DROP TABLE IF EXISTS tag_definitions;
 CREATE TABLE tag_definitions (
   id char(36) NOT NULL,
   name varchar(20) NOT NULL,
-  created_by varchar(50) NOT NULL,
   description varchar(200) NOT NULL,
+  created_by varchar(30) NOT NULL,
   created_date datetime NOT NULL,
-  updated_date datetime NOT NULL,
   PRIMARY KEY(id)
 ) ENGINE=innodb;
 CREATE UNIQUE INDEX tag_definitions_name ON tag_definitions(name);
@@ -58,35 +45,21 @@ CREATE TABLE tag_definition_history (
   name varchar(20) NOT NULL,
   created_by varchar(50),
   description varchar(200),
-  date datetime NOT NULL,
-  change_type char(6) NOT NULL
+  change_type char(6) NOT NULL,
+  updated_by varchar(30) NOT NULL,
+  date datetime NOT NULL
 ) ENGINE=innodb;
 CREATE INDEX tag_definition_history_id ON tag_definition_history(id);
 CREATE INDEX tag_definition_history_name ON tag_definition_history(name);
 
-CREATE TRIGGER tag_definition_history_after_insert AFTER INSERT ON tag_definition_history
-    FOR EACH ROW
-        INSERT INTO tag_definition_history (id, name, created_by, description, date, change_type)
-        VALUES (NEW.id, NEW.name, NEW.created_by, NEW.description, NOW(), 'CREATE');
-
-CREATE TRIGGER tag_definition_history_after_update AFTER UPDATE ON tag_definition_history
-    FOR EACH ROW
-        INSERT INTO tag_definition_history (id, name, created_by, description, date, change_type)
-        VALUES (NEW.id, NEW.name, NEW.created_by, NEW.description, NOW(), 'UPDATE');
-
-CREATE TRIGGER tag_definition_history_before_delete BEFORE DELETE ON tag_definition_history
-    FOR EACH ROW
-        INSERT INTO tag_definition_history (id, name, created_by, description, date, change_type)
-        VALUES (OLD.id, OLD.name, NULL, NULL, NOW(), 'DELETE');
-
 DROP TABLE IF EXISTS tags;
 CREATE TABLE tags (
   id char(36) NOT NULL,
   tag_definition_name varchar(20) NOT NULL,
   object_id char(36) NOT NULL,
   object_type varchar(30) NOT NULL,
-  added_date datetime NOT NULL,
-  added_by varchar(50) NOT NULL,
+  created_by varchar(30) NOT NULL,
+  created_date datetime NOT NULL,
   PRIMARY KEY(id)
 ) ENGINE = innodb;
 CREATE INDEX tags_by_object ON tags(object_id);
@@ -98,26 +71,12 @@ CREATE TABLE tag_history (
   tag_definition_name varchar(20) NOT NULL,
   object_id char(36) NOT NULL,
   object_type varchar(30) NOT NULL,
-  date datetime NOT NULL,
-  change_type char(6) NOT NULL
+  change_type char(6) NOT NULL,
+  updated_by varchar(30) NOT NULL,
+  date datetime NOT NULL
 ) ENGINE = innodb;
 CREATE INDEX tag_history_by_object ON tags(object_id);
 
-CREATE TRIGGER tag_history_after_insert AFTER INSERT ON tag_history
-    FOR EACH ROW
-        INSERT INTO tag_history (id, tag_definition_name, object_id, object_type, date, change_type)
-        VALUES (NEW.id, NEW.tag_definition_name, NEW.object_id, NEW.object_type, NOW(), 'CREATE');
-
-CREATE TRIGGER tag_history_after_update AFTER UPDATE ON tag_history
-    FOR EACH ROW
-        INSERT INTO tag_history (id, tag_definition_name, object_id, object_type, date, change_type)
-        VALUES (NEW.id, NEW.tag_definition_name, NEW.object_id, NEW.object_type, NOW(), 'UPDATE');
-
-CREATE TRIGGER tag_history_before_delete BEFORE DELETE ON tag_history
-    FOR EACH ROW
-        INSERT INTO tag_history (id, tag_definition_name, object_id, object_type, date, change_type)
-        VALUES (OLD.id, OLD.tag_definition_name, OLD.object_id, OLD.object_type, NOW(), 'DELETE');
-
 DROP TABLE IF EXISTS notifications;
 CREATE TABLE notifications (
     id int(11) unsigned NOT NULL AUTO_INCREMENT,
@@ -137,10 +96,23 @@ CREATE INDEX  `idx_get_ready` ON notifications (`effective_dt`,`created_dt`,`id`
 
 DROP TABLE IF EXISTS claimed_notifications;
 CREATE TABLE claimed_notifications (
-    id int(11) unsigned NOT NULL AUTO_INCREMENT,    
-    sequence_id int(11) unsigned NOT NULL,    
+    id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    sequence_id int(11) unsigned NOT NULL,
     owner_id varchar(64) NOT NULL,
     claimed_dt datetime NOT NULL,
     notification_id char(36) NOT NULL,
     PRIMARY KEY(id)
-) ENGINE=innodb;
\ No newline at end of file
+) ENGINE=innodb;
+
+DROP TABLE IF EXISTS audit_log;
+CREATE TABLE audit_log (
+    id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    table_name varchar(50) NOT NULL,
+    record_id char(36) NOT NULL,
+    change_type char(6) NOT NULL,
+    change_date datetime NOT NULL,
+    changed_by varchar(50) NOT NULL,
+    reason_code varchar(20) DEFAULT NULL,
+    comment varchar(255) DEFAULT NULL,
+    PRIMARY KEY(id)
+) ENGINE=innodb;
diff --git a/util/src/main/resources/com/ning/billing/util/tag/dao/TagAuditSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/tag/dao/TagAuditSqlDao.sql.stg
new file mode 100644
index 0000000..af3a384
--- /dev/null
+++ b/util/src/main/resources/com/ning/billing/util/tag/dao/TagAuditSqlDao.sql.stg
@@ -0,0 +1,21 @@
+group TagAuditSqlDao;
+
+fields(prefix) ::= <<
+    <prefix>table_name,
+    <prefix>record_id,
+    <prefix>change_type,
+    <prefix>change_date,
+    <prefix>changed_by,
+    <prefix>reason_code,
+    <prefix>comment
+>>
+
+batchInsertFromTransaction() ::= <<
+    INSERT INTO audit_log(<fields()>)
+    VALUES('tag', :id, 'INSERT', :date, :userName, NULL, NULL);
+>>
+
+batchDeleteFromTransaction() ::= <<
+    INSERT INTO audit_log(<fields()>)
+    VALUES('tag', :id, 'DELETE', :date, :userName, NULL, NULL);
+>>
\ No newline at end of file
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 333da26..c18445d 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
@@ -3,10 +3,9 @@ group TagDefinitionDao;
 fields(prefix) ::= <<
     <prefix>id,
     <prefix>name,
-    <prefix>created_by,
     <prefix>description,
-    <prefix>created_date,
-    <prefix>updated_date
+    <prefix>created_by,
+    <prefix>created_date
 >>
 
 get() ::= <<
@@ -16,13 +15,7 @@ get() ::= <<
 
 create() ::= <<
   INSERT INTO tag_definitions(<fields()>)
-  VALUES(:id, :name, :createdBy, :description, NOW(), NOW());
->>
-
-update() ::= <<
-  UPDATE tag_definitions
-  SET name = :name, created_by = :createdBy, description = :description, updated_date = NOW())
-  WHERE id = :id;
+  VALUES(:id, :name, :description, :userName, :date);
 >>
 
 load() ::= <<
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 9237997..93b62df 100644
--- a/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
+++ b/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
@@ -139,7 +139,7 @@ public class MysqlTestingHelper
 
     public IDBI getDBI()
     {
-        final String dbiString = "jdbc:mysql://localhost:" + port + "/" + DB_NAME + "?createDatabaseIfNotExist=true";
+        final String dbiString = "jdbc:mysql://localhost:" + port + "/" + DB_NAME + "?createDatabaseIfNotExist=true&allowMultiQueries=true";
         return new DBI(dbiString, USERNAME, PASSWORD);
     }
 
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 f7c16e4..d748e1a 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
@@ -17,19 +17,28 @@
 package com.ning.billing.util.customfield;
 
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.UUID;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.ning.billing.util.CallContext;
+import com.ning.billing.util.CallOrigin;
+import com.ning.billing.util.UserType;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.MockClockModule;
+import com.ning.billing.util.customfield.dao.AuditedCustomFieldDao;
+import com.ning.billing.util.entity.DefaultCallContext;
+import com.ning.billing.util.glue.FieldStoreModule;
 import org.apache.commons.io.IOUtils;
 import org.skife.jdbi.v2.IDBI;
-import org.skife.jdbi.v2.Transaction;
-import org.skife.jdbi.v2.TransactionStatus;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 import com.ning.billing.dbi.MysqlTestingHelper;
-import com.ning.billing.util.customfield.dao.FieldStoreDao;
+import com.ning.billing.util.customfield.dao.CustomFieldSqlDao;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.fail;
@@ -38,6 +47,7 @@ import static org.testng.Assert.fail;
 public class TestFieldStore {
     Logger log = LoggerFactory.getLogger(TestFieldStore.class);
     private final MysqlTestingHelper helper = new MysqlTestingHelper();
+    private CallContext context;
     private IDBI dbi;
 
     @BeforeClass(alwaysRun = true)
@@ -50,6 +60,12 @@ public class TestFieldStore {
             helper.initDb(utilDdl);
 
             dbi = helper.getDBI();
+
+            FieldStoreModule module = new FieldStoreModule();
+            final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module, new MockClockModule());
+            Clock clock = injector.getInstance(Clock.class);
+            context = new DefaultCallContext(clock, "Fezzik", CallOrigin.TEST, UserType.TEST);
+
         }
         catch (Throwable t) {
             log.error("Setup failed", t);
@@ -74,38 +90,23 @@ public class TestFieldStore {
         String fieldValue = "Kitty Hawk";
         fieldStore1.setValue(fieldName, fieldValue);
 
-        FieldStoreDao fieldStoreDao = dbi.onDemand(FieldStoreDao.class);
-        fieldStoreDao.inTransaction(new Transaction<Void, FieldStoreDao>() {
-            @Override
-            public Void inTransaction(FieldStoreDao transactional,
-                    TransactionStatus status) throws Exception {
-                transactional.batchSaveFromTransaction(id.toString(), objectType, fieldStore1.getEntityList());
-                return null;
-            }
-        });
-
+        CustomFieldSqlDao customFieldSqlDao = dbi.onDemand(CustomFieldSqlDao.class);
+        AuditedCustomFieldDao auditedDao = new AuditedCustomFieldDao();
+        auditedDao.saveFields(customFieldSqlDao, id, objectType, fieldStore1.getEntityList(), context);
 
         final FieldStore fieldStore2 = DefaultFieldStore.create(id, objectType);
-        fieldStore2.add(fieldStoreDao.load(id.toString(), objectType));
+        fieldStore2.add(customFieldSqlDao.load(id.toString(), objectType));
 
         assertEquals(fieldStore2.getValue(fieldName), fieldValue);
 
         fieldValue = "Cape Canaveral";
         fieldStore2.setValue(fieldName, fieldValue);
         assertEquals(fieldStore2.getValue(fieldName), fieldValue);
-        fieldStoreDao.inTransaction(new Transaction<Void, FieldStoreDao>() {
-            @Override
-            public Void inTransaction(FieldStoreDao transactional,
-                    TransactionStatus status) throws Exception {
-                transactional.batchSaveFromTransaction(id.toString(), objectType, fieldStore2.getEntityList());
-                return null;
-            }
-        });
-
+        auditedDao.saveFields(customFieldSqlDao, id, objectType, fieldStore2.getEntityList(), context);
 
         final FieldStore fieldStore3 = DefaultFieldStore.create(id, objectType);
         assertEquals(fieldStore3.getValue(fieldName), null);
-        fieldStore3.add(fieldStoreDao.load(id.toString(), objectType));
+        fieldStore3.add(customFieldSqlDao.load(id.toString(), objectType));
 
         assertEquals(fieldStore3.getValue(fieldName), fieldValue);
     }
diff --git a/util/src/test/java/com/ning/billing/util/globallocker/TestMysqlGlobalLocker.java b/util/src/test/java/com/ning/billing/util/globallocker/TestMysqlGlobalLocker.java
index 1fd8290..f693103 100644
--- a/util/src/test/java/com/ning/billing/util/globallocker/TestMysqlGlobalLocker.java
+++ b/util/src/test/java/com/ning/billing/util/globallocker/TestMysqlGlobalLocker.java
@@ -71,7 +71,7 @@ public class TestMysqlGlobalLocker {
             @Override
             public Void inTransaction(Handle conn, TransactionStatus status)
                     throws Exception {
-                conn.execute("insert into dummy (dummy_id) values ('" + UUID.randomUUID().toString()  + "')");
+                conn.execute("insert into dummy (dummy_id, value) values ('" + UUID.randomUUID().toString()  + "', 'test')");
                 return null;
             }
         });
@@ -96,9 +96,12 @@ public class TestMysqlGlobalLocker {
             @Override
             public Void inTransaction(Handle h, TransactionStatus status)
                     throws Exception {
+                h.execute("DROP TABLE IF EXISTS dummy;");
+
                 h.execute("create table dummy " +
                         "(id int(11) unsigned NOT NULL AUTO_INCREMENT, " +
                         "dummy_id char(36) NOT NULL, " +
+                        "value varchar(256) NOT NULL," +
                         "PRIMARY KEY(id)" +
                 		") ENGINE=innodb;");
                 return null;
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 425020e..ca07448 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
@@ -19,9 +19,9 @@ package com.ning.billing.util.tag.dao;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 
+import com.ning.billing.util.CallContext;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.tag.DefaultTagDefinition;
 import com.ning.billing.util.tag.TagDefinition;
@@ -35,25 +35,26 @@ public class MockTagDefinitionDao implements TagDefinitionDao {
     }
 
     @Override
-    public TagDefinition getByName(String definitionName) {
+    public TagDefinition getByName(final String definitionName) {
         return tags.get(definitionName);
     }
 
     @Override
-    public TagDefinition create(String definitionName, String description, String createdBy) throws TagDefinitionApiException {
-        TagDefinition tag = new DefaultTagDefinition(UUID.randomUUID(), definitionName, description, createdBy);
+    public TagDefinition create(final String definitionName, final String description,
+                                final CallContext context) throws TagDefinitionApiException {
+        TagDefinition tag = new DefaultTagDefinition(definitionName, description);
 
         tags.put(definitionName, tag);
         return tag;
     }
 
     @Override
-    public void deleteAllTagsForDefinition(String definitionName) throws TagDefinitionApiException {
+    public void deleteAllTagsForDefinition(final String definitionName, final CallContext context) throws TagDefinitionApiException {
         tags.remove(definitionName);
     }
 
     @Override
-    public void deleteTagDefinition(String definitionName) throws TagDefinitionApiException {
+    public void deleteTagDefinition(final String definitionName, final CallContext context) throws TagDefinitionApiException {
         tags.remove(definitionName);
     }
 }
diff --git a/util/src/test/java/com/ning/billing/util/tag/TagStoreModuleMock.java b/util/src/test/java/com/ning/billing/util/tag/TagStoreModuleMock.java
index 7bf9572..a3a8b5a 100644
--- a/util/src/test/java/com/ning/billing/util/tag/TagStoreModuleMock.java
+++ b/util/src/test/java/com/ning/billing/util/tag/TagStoreModuleMock.java
@@ -18,10 +18,12 @@ package com.ning.billing.util.tag;
 
 import java.io.IOException;
 
+import org.skife.jdbi.v2.Handle;
 import org.skife.jdbi.v2.IDBI;
 
 import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.util.glue.TagStoreModule;
+import org.skife.jdbi.v2.tweak.HandleCallback;
 
 public class TagStoreModuleMock extends TagStoreModule {
     private final MysqlTestingHelper helper = new MysqlTestingHelper();
@@ -43,4 +45,14 @@ public class TagStoreModuleMock extends TagStoreModule {
         bind(IDBI.class).toInstance(helper.getDBI());
         super.configure();
     }
+
+    public void execute(final String ddl) {
+        helper.getDBI().withHandle(new HandleCallback<Void>() {
+            @Override
+            public Void withHandle(Handle handle) throws Exception {
+                handle.execute(ddl);
+                return null;
+            }
+        });
+    }
 }
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 c913791..618dc14 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,12 +17,22 @@
 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 com.ning.billing.util.CallContext;
+import com.ning.billing.util.CallOrigin;
+import com.ning.billing.util.UserType;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.entity.DefaultCallContext;
+import com.ning.billing.util.tag.dao.AuditedTagDao;
 import org.apache.commons.io.IOUtils;
+import org.joda.time.DateTime;
+import org.joda.time.Seconds;
+import org.skife.jdbi.v2.Handle;
 import org.skife.jdbi.v2.IDBI;
-import org.skife.jdbi.v2.Transaction;
-import org.skife.jdbi.v2.TransactionStatus;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.AfterClass;
@@ -33,11 +43,9 @@ import com.google.inject.Injector;
 import com.google.inject.Stage;
 import com.ning.billing.account.api.ControlTagType;
 import com.ning.billing.util.api.TagDefinitionApiException;
-import com.ning.billing.util.clock.Clock;
-import com.ning.billing.util.clock.DefaultClock;
 import com.ning.billing.util.clock.MockClockModule;
 import com.ning.billing.util.tag.dao.TagDefinitionDao;
-import com.ning.billing.util.tag.dao.TagStoreSqlDao;
+import com.ning.billing.util.tag.dao.TagSqlDao;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
@@ -48,14 +56,14 @@ import static org.testng.Assert.fail;
 @Test(groups={"util"})
 public class TestTagStore {
     private final static String ACCOUNT_TYPE = "ACCOUNT";
-    private final Clock clock = new DefaultClock();
     private IDBI dbi;
     private TagDefinition tag1;
     private TagDefinition tag2;
     private TagStoreModuleMock module;
-    private TagStoreSqlDao tagStoreSqlDao;
+    private TagSqlDao tagSqlDao;
     private TagDefinitionDao tagDefinitionDao;
     private final Logger log = LoggerFactory.getLogger(TestTagStore.class);
+    private CallContext context;
 
     @BeforeClass(alwaysRun = true)
     protected void setup() throws IOException {
@@ -70,12 +78,17 @@ public class TestTagStore {
             final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module, new MockClockModule());
             dbi = injector.getInstance(IDBI.class);
 
-            tagStoreSqlDao = injector.getInstance(TagStoreSqlDao.class);
-            tagStoreSqlDao.test();
+            tagSqlDao = injector.getInstance(TagSqlDao.class);
+            tagSqlDao.test();
+
+            module.execute("truncate tag_definitions;");
+
+            Clock clock = injector.getInstance(Clock.class);
+            context = new DefaultCallContext(clock, "Inigo Montoya", CallOrigin.TEST, UserType.TEST);
 
             tagDefinitionDao = injector.getInstance(TagDefinitionDao.class);
-            tag1 = tagDefinitionDao.create("tag1", "First tag", "test");
-            tag2 = tagDefinitionDao.create("tag2", "Second tag", "test");
+            tag1 = tagDefinitionDao.create("tag1", "First tag", context);
+            tag2 = tagDefinitionDao.create("tag2", "Second tag", context);
         }
         catch (Throwable t) {
             log.error("Failed to start tag store tests", t);
@@ -89,15 +102,10 @@ public class TestTagStore {
         module.stopDb();
     }
 
-    private void saveTags(final TagStoreSqlDao dao, final String objectType, final String accountId, final List<Tag> tagList)  {
-        dao.inTransaction(new Transaction<Void, TagStoreSqlDao>() {
-            @Override
-            public Void inTransaction(TagStoreSqlDao transactional,
-                    TransactionStatus status) throws Exception {
-                dao.batchSaveFromTransaction(accountId.toString(), objectType, tagList);
-                return null;
-            }
-        });
+    private void saveTags(final TagSqlDao dao, final String objectType, final UUID accountId,
+                          final List<Tag> tagList, final CallContext context)  {
+        AuditedTagDao auditedTagDao = new AuditedTagDao();
+        auditedTagDao.saveTags(dao, accountId, objectType, tagList, context);
     }
 
     @Test
@@ -105,39 +113,36 @@ public class TestTagStore {
         UUID accountId = UUID.randomUUID();
 
         TagStore tagStore = new DefaultTagStore(accountId, ACCOUNT_TYPE);
-        Tag tag = new DescriptiveTag(tag2, "test", clock.getUTCNow());
+        Tag tag = new DescriptiveTag(tag2);
         tagStore.add(tag);
 
-        TagStoreSqlDao dao = dbi.onDemand(TagStoreSqlDao.class);
-        saveTags(dao, ACCOUNT_TYPE, accountId.toString(), tagStore.getEntityList());
+        TagSqlDao dao = dbi.onDemand(TagSqlDao.class);
+        saveTags(dao, ACCOUNT_TYPE, accountId, tagStore.getEntityList(), context);
 
         List<Tag> savedTags = dao.load(accountId.toString(), ACCOUNT_TYPE);
         assertEquals(savedTags.size(), 1);
 
         Tag savedTag = savedTags.get(0);
-        assertEquals(savedTag.getAddedBy(), tag.getAddedBy());
-        assertEquals(savedTag.getAddedDate().compareTo(tag.getAddedDate()), 0);
         assertEquals(savedTag.getTagDefinitionName(), tag.getTagDefinitionName());
         assertEquals(savedTag.getId(), tag.getId());
     }
 
-
     @Test
     public void testControlTagCreation() {
         UUID accountId = UUID.randomUUID();
         TagStore tagStore = new DefaultTagStore(accountId, ACCOUNT_TYPE);
 
-        ControlTag tag = new DefaultControlTag("testUser", clock.getUTCNow(), ControlTagType.AUTO_INVOICING_OFF);
+        ControlTag tag = new DefaultControlTag(ControlTagType.AUTO_INVOICING_OFF);
         tagStore.add(tag);
         assertEquals(tagStore.generateInvoice(), false);
 
         List<Tag> tagList = tagStore.getEntityList();
-        saveTags(tagStoreSqlDao, ACCOUNT_TYPE, accountId.toString(), tagList);
+        saveTags(tagSqlDao, ACCOUNT_TYPE, accountId, tagList, context);
 
         tagStore.clear();
         assertEquals(tagStore.getEntityList().size(), 0);
 
-        tagList = tagStoreSqlDao.load(accountId.toString(), ACCOUNT_TYPE);
+        tagList = tagSqlDao.load(accountId.toString(), ACCOUNT_TYPE);
         tagStore.add(tagList);
         assertEquals(tagList.size(), 1);
 
@@ -152,22 +157,22 @@ public class TestTagStore {
         String definitionName = "SomeTestTag";
         TagDefinition tagDefinition = null;
         try {
-            tagDefinition = tagDefinitionDao.create(definitionName, "Test tag for some test purpose", "testUser");
+            tagDefinition = tagDefinitionDao.create(definitionName, "Test tag for some test purpose", context);
         } catch (TagDefinitionApiException e) {
             fail("Tag definition creation failed.", e);
         }
 
-        DescriptiveTag tag = new DescriptiveTag(tagDefinition, "testUser", clock.getUTCNow());
+        DescriptiveTag tag = new DescriptiveTag(tagDefinition);
         tagStore.add(tag);
         assertEquals(tagStore.generateInvoice(), true);
 
         List<Tag> tagList = tagStore.getEntityList();
-        saveTags(tagStoreSqlDao, ACCOUNT_TYPE, accountId.toString(), tagList);
+        saveTags(tagSqlDao, ACCOUNT_TYPE, accountId, tagList, context);
 
         tagStore.clear();
         assertEquals(tagStore.getEntityList().size(), 0);
 
-        tagList = tagStoreSqlDao.load(accountId.toString(), ACCOUNT_TYPE);
+        tagList = tagSqlDao.load(accountId.toString(), ACCOUNT_TYPE);
         tagStore.add(tagList);
         assertEquals(tagList.size(), 1);
 
@@ -182,26 +187,26 @@ public class TestTagStore {
         String definitionName = "MixedTagTest";
         TagDefinition tagDefinition = null;
         try {
-            tagDefinition = tagDefinitionDao.create(definitionName, "Test tag for some test purpose", "testUser");
+            tagDefinition = tagDefinitionDao.create(definitionName, "Test tag for some test purpose", context);
         } catch (TagDefinitionApiException e) {
             fail("Tag definition creation failed.", e);
         }
 
-        DescriptiveTag descriptiveTag = new DescriptiveTag(tagDefinition, "testUser", clock.getUTCNow());
+        DescriptiveTag descriptiveTag = new DescriptiveTag(tagDefinition);
         tagStore.add(descriptiveTag);
         assertEquals(tagStore.generateInvoice(), true);
 
-        ControlTag controlTag = new DefaultControlTag("testUser", clock.getUTCNow(), ControlTagType.AUTO_INVOICING_OFF);
+        ControlTag controlTag = new DefaultControlTag(ControlTagType.AUTO_INVOICING_OFF);
         tagStore.add(controlTag);
         assertEquals(tagStore.generateInvoice(), false);
 
         List<Tag> tagList = tagStore.getEntityList();
-        saveTags(tagStoreSqlDao, ACCOUNT_TYPE, accountId.toString(), tagList);
+        saveTags(tagSqlDao, ACCOUNT_TYPE, accountId, tagList, context);
 
         tagStore.clear();
         assertEquals(tagStore.getEntityList().size(), 0);
 
-        tagList = tagStoreSqlDao.load(accountId.toString(), ACCOUNT_TYPE);
+        tagList = tagSqlDao.load(accountId.toString(), ACCOUNT_TYPE);
         tagStore.add(tagList);
         assertEquals(tagList.size(), 2);
 
@@ -215,12 +220,12 @@ public class TestTagStore {
         assertEquals(tagStore.generateInvoice(), true);
         assertEquals(tagStore.processPayment(), true);
 
-        ControlTag invoiceTag = new DefaultControlTag("testUser", clock.getUTCNow(), ControlTagType.AUTO_INVOICING_OFF);
+        ControlTag invoiceTag = new DefaultControlTag(ControlTagType.AUTO_INVOICING_OFF);
         tagStore.add(invoiceTag);
         assertEquals(tagStore.generateInvoice(), false);
         assertEquals(tagStore.processPayment(), true);
 
-        ControlTag paymentTag = new DefaultControlTag("testUser", clock.getUTCNow(), ControlTagType.AUTO_BILLING_OFF);
+        ControlTag paymentTag = new DefaultControlTag(ControlTagType.AUTO_BILLING_OFF);
         tagStore.add(paymentTag);
         assertEquals(tagStore.generateInvoice(), false);
         assertEquals(tagStore.processPayment(), false);
@@ -229,18 +234,18 @@ public class TestTagStore {
     @Test(expectedExceptions = TagDefinitionApiException.class)
     public void testTagDefinitionCreationWithControlTagName() throws TagDefinitionApiException {
         String definitionName = ControlTagType.AUTO_BILLING_OFF.toString();
-        tagDefinitionDao.create(definitionName, "This should break", "test");
+        tagDefinitionDao.create(definitionName, "This should break", context);
     }
 
     @Test
     public void testTagDefinitionDeletionForUnusedDefinition() throws TagDefinitionApiException {
         String definitionName = "TestTag1234";
-        tagDefinitionDao.create(definitionName, "Some test tag", "test");
+        tagDefinitionDao.create(definitionName, "Some test tag", context);
 
         TagDefinition tagDefinition = tagDefinitionDao.getByName(definitionName);
         assertNotNull(tagDefinition);
 
-        tagDefinitionDao.deleteTagDefinition(definitionName);
+        tagDefinitionDao.deleteTagDefinition(definitionName, context);
         tagDefinition = tagDefinitionDao.getByName(definitionName);
         assertNull(tagDefinition);
     }
@@ -248,7 +253,7 @@ public class TestTagStore {
     @Test(expectedExceptions = TagDefinitionApiException.class)
     public void testTagDefinitionDeletionForDefinitionInUse() throws TagDefinitionApiException {
         String definitionName = "TestTag12345";
-        tagDefinitionDao.create(definitionName, "Some test tag", "test");
+        tagDefinitionDao.create(definitionName, "Some test tag", context);
 
         TagDefinition tagDefinition = tagDefinitionDao.getByName(definitionName);
         assertNotNull(tagDefinition);
@@ -256,22 +261,22 @@ public class TestTagStore {
         UUID objectId = UUID.randomUUID();
         String objectType = "TestType";
         TagStore tagStore = new DefaultTagStore(objectId, objectType);
-        Tag tag = new DescriptiveTag(tagDefinition, "test", clock.getUTCNow());
+        Tag tag = new DescriptiveTag(tagDefinition);
         tagStore.add(tag);
 
-        saveTags(tagStoreSqlDao, objectType, objectId.toString(), tagStore.getEntityList());
+        saveTags(tagSqlDao, objectType, objectId, tagStore.getEntityList(), context);
 
-        List<Tag> tags = tagStoreSqlDao.load(objectId.toString(), objectType);
+        List<Tag> tags = tagSqlDao.load(objectId.toString(), objectType);
         assertEquals(tags.size(), 1);
 
-        tagDefinitionDao.deleteTagDefinition(definitionName);
+        tagDefinitionDao.deleteTagDefinition(definitionName, context);
     }
 
     @Test
     public void testDeleteAllTagsForDefinitionInUse() {
         String definitionName = "TestTag1234567";
         try {
-            tagDefinitionDao.create(definitionName, "Some test tag", "test");
+            tagDefinitionDao.create(definitionName, "Some test tag", context);
         } catch (TagDefinitionApiException e) {
             fail("Could not create tag definition", e);
         }
@@ -282,22 +287,22 @@ public class TestTagStore {
         UUID objectId = UUID.randomUUID();
         String objectType = "TestType";
         TagStore tagStore = new DefaultTagStore(objectId, objectType);
-        Tag tag = new DescriptiveTag(tagDefinition, "test", clock.getUTCNow());
+        Tag tag = new DescriptiveTag(tagDefinition);
         tagStore.add(tag);
 
-        saveTags(tagStoreSqlDao, objectType, objectId.toString(), tagStore.getEntityList());
+        saveTags(tagSqlDao, objectType, objectId, tagStore.getEntityList(), context);
 
-        List<Tag> tags = tagStoreSqlDao.load(objectId.toString(), objectType);
+        List<Tag> tags = tagSqlDao.load(objectId.toString(), objectType);
         assertEquals(tags.size(), 1);
 
         try {
-            tagDefinitionDao.deleteAllTagsForDefinition(definitionName);
+            tagDefinitionDao.deleteAllTagsForDefinition(definitionName, context);
         } catch (TagDefinitionApiException e) {
             fail("Could not delete tags for tag definition", e);
         }
 
         try {
-            tagDefinitionDao.deleteTagDefinition(definitionName);
+            tagDefinitionDao.deleteTagDefinition(definitionName, context);
         } catch (TagDefinitionApiException e) {
             fail("Could not delete tag definition", e);
         }
@@ -307,7 +312,7 @@ public class TestTagStore {
     public void testDeleteAllTagsForDefinitionNotInUse() {
         String definitionName = "TestTag4321";
         try {
-            tagDefinitionDao.create(definitionName, "Some test tag", "test");
+            tagDefinitionDao.create(definitionName, "Some test tag", context);
         } catch (TagDefinitionApiException e) {
             fail("Could not create tag definition", e);
         }
@@ -316,13 +321,13 @@ public class TestTagStore {
         assertNotNull(tagDefinition);
 
         try {
-            tagDefinitionDao.deleteAllTagsForDefinition(definitionName);
+            tagDefinitionDao.deleteAllTagsForDefinition(definitionName, context);
         } catch (TagDefinitionApiException e) {
             fail("Could not delete tags for tag definition", e);
         }
 
         try {
-            tagDefinitionDao.deleteTagDefinition(definitionName);
+            tagDefinitionDao.deleteTagDefinition(definitionName, context);
         } catch (TagDefinitionApiException e) {
             fail("Could not delete tag definition", e);
         }
@@ -333,7 +338,7 @@ public class TestTagStore {
         String definitionName = "TestTag654321";
         String wrongDefinitionName = "TestTag564321";
         try {
-            tagDefinitionDao.create(definitionName, "Some test tag", "test");
+            tagDefinitionDao.create(definitionName, "Some test tag", context);
         } catch (TagDefinitionApiException e) {
             fail("Could not create tag definition", e);
         }
@@ -341,10 +346,10 @@ public class TestTagStore {
         TagDefinition tagDefinition = tagDefinitionDao.getByName(definitionName);
         assertNotNull(tagDefinition);
 
-        tagDefinitionDao.deleteAllTagsForDefinition(wrongDefinitionName);
+        tagDefinitionDao.deleteAllTagsForDefinition(wrongDefinitionName, context);
 
         try {
-            tagDefinitionDao.deleteTagDefinition(definitionName);
+            tagDefinitionDao.deleteTagDefinition(definitionName, context);
         } catch (TagDefinitionApiException e) {
             fail("Could not delete tag definition", e);
         }
@@ -355,4 +360,80 @@ public class TestTagStore {
         List<TagDefinition> definitionList = tagDefinitionDao.getTagDefinitions();
         assertTrue(definitionList.size() >= ControlTagType.values().length);
     }
+
+    @Test
+    public void testTagInsertAudit() {
+        UUID accountId = UUID.randomUUID();
+
+        TagStore tagStore = new DefaultTagStore(accountId, ACCOUNT_TYPE);
+        Tag tag = new DescriptiveTag(tag2);
+        tagStore.add(tag);
+
+        TagSqlDao dao = dbi.onDemand(TagSqlDao.class);
+        saveTags(dao, ACCOUNT_TYPE, accountId, tagStore.getEntityList(), context);
+
+        List<Tag> savedTags = dao.load(accountId.toString(), ACCOUNT_TYPE);
+        assertEquals(savedTags.size(), 1);
+
+        Tag savedTag = savedTags.get(0);
+        assertEquals(savedTag.getTagDefinitionName(), tag.getTagDefinitionName());
+        assertEquals(savedTag.getId(), tag.getId());
+
+        Handle handle = dbi.open();
+        String query = String.format("select * from audit_log where table_name = 'Tag' and record_id='%s'",
+                                     tag.getId().toString());
+        List<Map<String, Object>> result = handle.select(query);
+        assertNotNull(result);
+        assertEquals(result.size(), 1);
+        assertEquals(result.get(0).get("change_type"), "INSERT");
+        assertNotNull(result.get(0).get("change_date"));
+        DateTime changeDate = new DateTime(result.get(0).get("change_date"));
+        assertTrue(Seconds.secondsBetween(changeDate, context.getUTCNow()).getSeconds() < 2);
+        assertEquals(result.get(0).get("changed_by"), context.getUserName());
+    }
+
+    @Test
+    public void testTagDeleteAudit() {
+        UUID accountId = UUID.randomUUID();
+
+        TagStore tagStore = new DefaultTagStore(accountId, ACCOUNT_TYPE);
+        Tag tag = new DescriptiveTag(tag2);
+        tagStore.add(tag);
+
+        TagSqlDao dao = dbi.onDemand(TagSqlDao.class);
+        saveTags(dao, ACCOUNT_TYPE, accountId, tagStore.getEntityList(), context);
+        saveTags(dao, ACCOUNT_TYPE, accountId, new ArrayList<Tag>(), context);
+
+        List<Tag> savedTags = dao.load(accountId.toString(), ACCOUNT_TYPE);
+        assertEquals(savedTags.size(), 0);
+
+        Handle handle = dbi.open();
+        String query = String.format("select * from audit_log where table_name = 'Tag' and record_id='%s' and change_type='DELETE'",
+                                     tag.getId().toString());
+        List<Map<String, Object>> result = handle.select(query);
+        assertNotNull(result);
+        assertEquals(result.size(), 1);
+        assertNotNull(result.get(0).get("change_date"));
+        DateTime changeDate = new DateTime(result.get(0).get("change_date"));
+        assertTrue(Seconds.secondsBetween(changeDate, context.getUTCNow()).getSeconds() < 2);
+        assertEquals(result.get(0).get("changed_by"), context.getUserName());
+    }
+
+//    @Test
+//    public void testTagDefinitionInsertAudit() {
+//
+//        fail();
+//    }
+//
+//    @Test
+//    public void testTagDefinitionUpdateAudit() {
+//
+//        fail();
+//    }
+//
+//    @Test
+//    public void testTagDefinitionDeleteAudit() {
+//
+//        fail();
+//    }
 }