killbill-aplcache

Changes

account/pom.xml 2(+1 -1)

api/pom.xml 2(+1 -1)

beatrix/pom.xml 2(+1 -1)

catalog/pom.xml 2(+1 -1)

invoice/pom.xml 10(+9 -1)

payment/pom.xml 2(+1 -1)

pom.xml 10(+7 -3)

util/pom.xml 5(+2 -3)

util/src/main/resources/com/ning/billing/util/customfield/dao/FieldStoreDao.sql.stg 25(+0 -25)

util/src/main/resources/com/ning/billing/util/tag/dao/TagStoreSqlDao.sql.stg 30(+0 -30)

Details

account/pom.xml 2(+1 -1)

diff --git a/account/pom.xml b/account/pom.xml
index c48b8b2..de028b7 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.8-SNAPSHOT</version>
+        <version>0.1.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-account</artifactId>
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 022e341..a22491b 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,27 +19,24 @@ package com.ning.billing.account.api;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.entity.ExtendedEntityBase;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 
 import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.util.customfield.CustomizableEntityBase;
-import com.ning.billing.util.tag.DefaultTagStore;
-import com.ning.billing.util.tag.DescriptiveTag;
-import com.ning.billing.util.tag.Tag;
-import com.ning.billing.util.tag.TagDefinition;
- 
-public class DefaultAccount extends CustomizableEntityBase implements Account {
-	//public final static String OBJECT_TYPE = "Account";
-
-	private final String externalKey;
+
+import javax.annotation.Nullable;
+
+public class DefaultAccount extends ExtendedEntityBase implements Account {
+    private final String externalKey;
 	private final String email;
 	private final String name;
 	private final int firstNameLength;
 	private final Currency currency;
 	private final int billCycleDay;
 	private final String paymentProviderName;
-	private final DefaultTagStore tags;
 	private final DateTimeZone timeZone;
 	private final String locale;
 	private final String address1;
@@ -50,57 +47,40 @@ 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, @Nullable final String createdBy, @Nullable final DateTime createdDate,
+                          @Nullable final String updatedBy, @Nullable 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);
 	}
 
 	/**
@@ -126,14 +106,17 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
 	 * @param createdDate
 	 * @param updatedDate
 	 */
-	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,14 +134,28 @@ 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";
+		return ObjectType;
 	}
 
 	@Override
@@ -176,20 +173,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,79 +238,47 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
 		return country;
 	}
 
-	@Override
-	public String getPhone() {
-		return phone;
-	}
-
-	@Override
-	public List<Tag> getTagList() {
-		return tags.getEntityList();
-	}
-
-	@Override
-	public boolean hasTag(String tagName) {
-		return tags.containsTag(tagName);
-	}
-
-	@Override
-	public void addTag(TagDefinition definition, String addedBy, DateTime dateAdded) {
-		Tag tag = new DescriptiveTag(definition, addedBy, dateAdded);
-		tags.add(tag) ;
-	}
-
-	@Override
-	public void addTags(List<Tag> tags) {
-		if (tags != null) {
-			this.tags.add(tags);
-		}
-	}
+    @Override
+    public String getUpdatedBy() {
+        return updatedBy;
+    }
 
-	@Override
-	public void clearTags() {
-		this.tags.clear();
-	}
-
-	@Override
-	public void removeTag(TagDefinition definition) {
-		tags.remove(definition.getName());
-	}
+    @Override
+    public DateTime getUpdatedDate() {
+        return updatedDate;
+    }
 
-	@Override
-	public boolean generateInvoice() {
-		return tags.generateInvoice();
+    @Override
+	public String getPhone() {
+		return phone;
 	}
 
-	@Override
-	public boolean processPayment() {
-		return tags.processPayment();
-	}
-	
-	@Override
+    @Override
 	public MutableAccountData toMutableAccountData() {
 	    return new MutableAccountData(this);
 	}
-
+    
 	@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 +
+                ", fields=" + fields +
+                "]";
 	}
 }
\ 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 7e7c33e..f1c58fb 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
@@ -28,29 +28,32 @@ import com.ning.billing.account.api.DefaultAccount;
 import com.ning.billing.account.api.MigrationAccountData;
 import com.ning.billing.account.api.MutableAccountData;
 import com.ning.billing.account.dao.AccountDao;
-import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextFactory;
 import com.ning.billing.util.customfield.CustomField;
 import com.ning.billing.util.entity.EntityPersistenceException;
 import com.ning.billing.util.tag.Tag;
+import org.joda.time.DateTime;
 
 public class DefaultAccountUserApi implements com.ning.billing.account.api.AccountUserApi {
+    private final CallContextFactory factory;
     private final AccountDao dao;
-	private Clock clock;
 
     @Inject
-    public DefaultAccountUserApi(final AccountDao dao, final Clock clock) {
+    public DefaultAccountUserApi(final CallContextFactory factory, final AccountDao dao) {
+        this.factory = factory;
         this.dao = dao;
-        this.clock = clock;
     }
 
     @Override
-    public Account createAccount(final AccountData data, final List<CustomField> fields, final 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);
         }
@@ -79,21 +82,21 @@ 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 UUID accountId, final AccountData accountData)
+    public void updateAccount(final UUID accountId, final AccountData accountData, final CallContext context)
             throws AccountApiException {
         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);
         }
@@ -101,30 +104,28 @@ public class DefaultAccountUserApi implements com.ning.billing.account.api.Accou
     }
 
     @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);
     	}
-    	updateAccount(accountId, accountData);
-     }
 
-	@Override
-	public void deleteAccountByKey(final String externalKey) throws AccountApiException {
-		dao.deleteByKey(externalKey);
-	}
+    	updateAccount(accountId, accountData, context);
+     }
 
 	@Override
-	public Account migrateAccount(MigrationAccountData data,
-			List<CustomField> fields, List<Tag> tags)
-			throws AccountApiException {
-		
-		Account account = new DefaultAccount(data, data.getCreatedDate(), data.getUpdatedDate());
-        account.addFields(fields);
+	public Account migrateAccount(final MigrationAccountData data, final List<CustomField> fields,
+                                  final List<Tag> tags, final CallContext context)
+            throws AccountApiException {
+        DateTime createdDate = data.getCreatedDate() == null ? context.getCreatedDate() : data.getCreatedDate();
+        DateTime updatedDate = data.getUpdatedDate() == null ? context.getUpdatedDate() : data.getUpdatedDate();
+        CallContext migrationContext = factory.toMigrationCallContext(context, createdDate, updatedDate);
+		Account account = new DefaultAccount(data);
+        account.setFields(fields);
         account.addTags(tags);
 
         try {
-            dao.create(account);
+            dao.create(account, migrationContext);
         } catch (EntityPersistenceException e) {
             throw new AccountApiException(e, ErrorCode.ACCOUNT_CREATION_FAILED);
         }
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountBinder.java b/account/src/main/java/com/ning/billing/account/dao/AccountBinder.java
new file mode 100644
index 0000000..a4a00a8
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountBinder.java
@@ -0,0 +1,67 @@
+/*
+ * 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.account.dao;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.catalog.api.Currency;
+import org.joda.time.DateTimeZone;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BinderFactory;
+import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@BindingAnnotation(AccountBinder.AccountBinderFactory.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER})
+public @interface AccountBinder {
+    public static class AccountBinderFactory implements BinderFactory {
+        @Override
+        public Binder<AccountBinder, Account> build(Annotation annotation) {
+            return new Binder<AccountBinder, Account>() {
+                @Override
+                public void bind(@SuppressWarnings("rawtypes") SQLStatement q, AccountBinder bind, Account account) {
+                    q.bind("id", account.getId().toString());
+                    q.bind("externalKey", account.getExternalKey());
+                    q.bind("email", account.getEmail());
+                    q.bind("name", account.getName());
+                    q.bind("firstNameLength", account.getFirstNameLength());
+                    Currency currency = account.getCurrency();
+                    q.bind("currency", (currency == null) ? null : currency.toString());
+                    q.bind("billingCycleDay", account.getBillCycleDay());
+                    q.bind("paymentProviderName", account.getPaymentProviderName());
+                    DateTimeZone timeZone = account.getTimeZone();
+                    q.bind("timeZone", (timeZone == null) ? null : timeZone.toString());
+                    q.bind("locale", account.getLocale());
+                    q.bind("address1", account.getAddress1());
+                    q.bind("address2", account.getAddress2());
+                    q.bind("companyName", account.getCompanyName());
+                    q.bind("city", account.getCity());
+                    q.bind("stateOrProvince", account.getStateOrProvince());
+                    q.bind("country", account.getCountry());
+                    q.bind("postalCode", account.getPostalCode());
+                    q.bind("phone", account.getPhone());
+                }
+            };
+        }
+    }
+}
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..4be1058 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,7 +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.entity.EntityDao;
+import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.entity.UpdatableEntityDao;
 
 public interface AccountDao extends UpdatableEntityDao<Account> {
@@ -32,6 +32,4 @@ public interface AccountDao extends UpdatableEntityDao<Account> {
      * @throws AccountApiException when externalKey is null
      */
     public UUID getIdFromKey(String externalKey) throws AccountApiException;
-
-	public void deleteByKey(String externalKey) throws AccountApiException;
 }
\ No newline at end of file
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountHistorySqlDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountHistorySqlDao.java
new file mode 100644
index 0000000..8d1a46e
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountHistorySqlDao.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.dao;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+
+@ExternalizedSqlViaStringTemplate3
+public interface AccountHistorySqlDao extends Transmogrifier {
+    @SqlUpdate
+    public void insertAccountHistoryFromTransaction(@AccountBinder final Account account,
+                                                    @Bind("historyId") final String historyId,
+                                                    @Bind("changeType") String changeType,
+                                                    @CallContextBinder CallContext context);
+}
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 dfa9b89..8c09af4 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
@@ -16,25 +16,18 @@
 
 package com.ning.billing.account.dao;
 
-import java.lang.annotation.Annotation;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.sql.Timestamp;
-import java.util.Date;
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.dao.MapperBase;
+import com.ning.billing.util.entity.UpdatableEntityDao;
 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;
-import org.skife.jdbi.v2.sqlobject.Binder;
-import org.skife.jdbi.v2.sqlobject.BinderFactory;
-import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
@@ -47,7 +40,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.UpdatableEntityDao;
 
 @ExternalizedSqlViaStringTemplate3
 @RegisterMapper({UuidMapper.class, AccountSqlDao.AccountMapper.class})
@@ -60,25 +52,15 @@ 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);
-
-    @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 void update(@AccountBinder Account account, @CallContextBinder final CallContext context);
 
+    public static class AccountMapper extends MapperBase implements ResultSetMapper<Account> {
         @Override
         public Account map(int index, ResultSet result, StatementContext context) throws SQLException {
-
             UUID id = UUID.fromString(result.getString("id"));
             String externalKey = result.getString("external_key");
             String email = result.getString("email");
@@ -90,8 +72,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);
@@ -107,6 +87,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)
@@ -117,51 +102,9 @@ 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();
         }
     }
-
-    @BindingAnnotation(AccountBinder.AccountBinderFactory.class)
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target({ElementType.PARAMETER})
-    public @interface AccountBinder {
-        public static class AccountBinderFactory implements BinderFactory {
-            @Override
-            public Binder<AccountBinder, Account> build(Annotation annotation) {
-                return new Binder<AccountBinder, Account>() {
-                    private Date getDate(DateTime dateTime) {
-                        return dateTime == null ? null : dateTime.toDate();
-                    }
-
-                    @Override
-                    public void bind(@SuppressWarnings("rawtypes") SQLStatement q, AccountBinder bind, Account account) {
-                        q.bind("id", account.getId().toString());
-                        q.bind("externalKey", account.getExternalKey());
-                        q.bind("email", account.getEmail());
-                        q.bind("name", account.getName());
-                        q.bind("firstNameLength", account.getFirstNameLength());
-                        Currency currency = account.getCurrency();
-                        q.bind("currency", (currency == null) ? null : currency.toString());
-                        q.bind("billingCycleDay", account.getBillCycleDay());
-                        q.bind("paymentProviderName", account.getPaymentProviderName());
-                        DateTimeZone timeZone = account.getTimeZone();
-                        q.bind("timeZone", (timeZone == null) ? null : timeZone.toString());
-                        q.bind("locale", account.getLocale());
-                        q.bind("address1", account.getAddress1());
-                        q.bind("address2", account.getAddress2());
-                        q.bind("companyName", account.getCompanyName());
-                        q.bind("city", account.getCity());
-                        q.bind("stateOrProvince", account.getStateOrProvince());
-                        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/glue/AccountModule.java b/account/src/main/java/com/ning/billing/account/glue/AccountModule.java
index f6e51f4..60e0a14 100644
--- a/account/src/main/java/com/ning/billing/account/glue/AccountModule.java
+++ b/account/src/main/java/com/ning/billing/account/glue/AccountModule.java
@@ -24,7 +24,7 @@ import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.account.api.DefaultAccountService;
 import com.ning.billing.account.api.user.DefaultAccountUserApi;
 import com.ning.billing.account.dao.AccountDao;
-import com.ning.billing.account.dao.DefaultAccountDao;
+import com.ning.billing.account.dao.AuditedAccountDao;
 
 public class AccountModule extends AbstractModule {
 
@@ -34,7 +34,7 @@ public class AccountModule extends AbstractModule {
     }
 
     protected void installAccountDao() {
-        bind(AccountDao.class).to(DefaultAccountDao.class).asEagerSingleton();
+        bind(AccountDao.class).to(AuditedAccountDao.class).asEagerSingleton();
     }
 
     protected void installAccountUserApi() {
diff --git a/account/src/main/resources/com/ning/billing/account/dao/AccountHistorySqlDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/AccountHistorySqlDao.sql.stg
new file mode 100644
index 0000000..c64c29f
--- /dev/null
+++ b/account/src/main/resources/com/ning/billing/account/dao/AccountHistorySqlDao.sql.stg
@@ -0,0 +1,15 @@
+group AccountHistorySqlDao;
+
+insertAccountHistoryFromTransaction() ::= <<
+    INSERT INTO account_history
+    (history_id, 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, change_type, updated_by, date)
+    VALUES
+    (:historyId, :id, :externalKey, :email, :name, :firstNameLength, :currency,
+     :billingCycleDay, :paymentProviderName, :timeZone, :locale,
+     :address1, :address2, :companyName, :city, :stateOrProvince,
+     :country, :postalCode, :phone, :changeType, :userName, :createdDate);
+>>
+;
\ No newline at end of file
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..eda3949 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
@@ -18,12 +18,13 @@ accountFields(prefix) ::= <<
 	<prefix>state_or_province, 
     <prefix>country, 
     <prefix>postal_code,
-    <prefix>phone,	
-    <prefix>created_dt,
-    <prefix>updated_dt
+    <prefix>phone,
+    <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, :createdDate, :userName, :updatedDate);
 >>
 
 update() ::= <<
@@ -41,16 +42,10 @@ 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 = :updatedDate, updated_by = :userName
     WHERE id = :id;
 >>
 
-deleteByKey() ::= <<
-    DELETE FROM accounts
-    WHERE external_key = :externalKey;
->>
-
-
 getAccountByKey() ::= <<
     select <accountFields()>
     from accounts
@@ -59,19 +54,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..ff9a41c 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,11 @@ 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,
+    migrated bool DEFAULT false,
+    created_date datetime NOT NULL,
+    created_by varchar(50) NOT NULL,
+    updated_date datetime DEFAULT NULL,
+    updated_by varchar(50) DEFAULT NULL,
     PRIMARY KEY(id)
 ) ENGINE=innodb;
 CREATE UNIQUE INDEX accounts_external_key ON accounts(external_key);
@@ -27,6 +30,7 @@ CREATE UNIQUE INDEX accounts_email ON accounts(email);
 
 DROP TABLE IF EXISTS account_history;
 CREATE TABLE account_history (
+    history_id char(36) NOT NULL,
     id char(36) NOT NULL,
     external_key varchar(128) NULL,
     email varchar(50) NOT NULL,
@@ -45,28 +49,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(50) 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 1d49c45..d2fea9e 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.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,38 +102,27 @@ 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 Account migrateAccount(final MigrationAccountData data,
+			final List<CustomField> fields, final List<Tag> tags, final CallContext context)
 			throws AccountApiException {
-		for (Account account : accounts) {
-            if (externalKey.equals(account.getExternalKey())) {
-                accounts.remove(account.getId());
-            }
-        }	
-		
-	}
-
-	@Override
-	public Account migrateAccount(MigrationAccountData data,
-			List<CustomField> fields, List<Tag> tags)
-			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(final String key, final AccountData accountData, final CallContext context)
 			throws AccountApiException {
 		throw new UnsupportedOperationException();
 	}
 
     @Override
-    public void updateAccount(UUID accountId, AccountData accountData)
+    public void updateAccount(final UUID accountId, final 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..3a51bcc 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.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
+import com.ning.billing.util.clock.Clock;
 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 DefaultCallContextFactory(clock).createCallContext("Vizzini", CallOrigin.TEST, UserType.TEST);
+
+
             BusService busService = injector.getInstance(BusService.class);
             ((DefaultBusService) busService).startBus();
         }
@@ -79,20 +90,11 @@ public abstract class AccountDaoTestBase {
             @Override
             public Void inTransaction(Handle h, TransactionStatus status) throws Exception {
                 h.execute("truncate table accounts");
-                h.execute("truncate table entitlement_events");
-                h.execute("truncate table subscriptions");
-                h.execute("truncate table bundles");
                 h.execute("truncate table notifications");
                 h.execute("truncate table claimed_notifications");
-                h.execute("truncate table invoices");
-                h.execute("truncate table fixed_invoice_items");
-                h.execute("truncate table recurring_invoice_items");
                 h.execute("truncate table tag_definitions");
                 h.execute("truncate table tags");
                 h.execute("truncate table custom_fields");
-                h.execute("truncate table invoice_payments");
-                h.execute("truncate table payment_attempts");
-                h.execute("truncate table payments");
                 return null;
             }
         });
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..645124b 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.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);
@@ -96,13 +97,4 @@ public class MockAccountDao implements AccountDao {
             }
         }
     }
-
-	@Override
-	public void deleteByKey(String externalKey) throws AccountApiException {
-		for (Account account : accounts.values()) {
-            if (externalKey.equals(account.getExternalKey())) {
-                accounts.remove(account.getId());
-            }
-        }		
-	}
 }
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 fd99ec5..7feb492 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
@@ -33,46 +33,38 @@ import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
 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;
 import com.ning.billing.util.tag.dao.TagDefinitionSqlDao;
 
-@Test(groups = {"account-dao"})
+@Test(groups = {"slow", "account-dao"})
 public class TestSimpleAccountDao extends AccountDaoTestBase {
-    private AccountBuilder createTestAccountBuilder() {
+    private Account createTestAccount(int billCycleDay) {
+        return createTestAccount(billCycleDay, "123-456-7890");
+    }
+
+    private Account createTestAccount(int billCycleDay, String phone) {
         String thisKey = "test" + UUID.randomUUID().toString();
         String lastName = UUID.randomUUID().toString();
         String thisEmail = "me@me.com" + " " + UUID.randomUUID();
         String firstName = "Bob";
         String name = firstName + " " + lastName;
-        String phone = "123-456-7890";
         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)
-                                   .phone(phone)
-                                   .firstNameLength(firstNameLength)
-                                   .email(thisEmail)
-                                   .currency(Currency.USD)
-                                   .locale(locale)
-                                   .timeZone(timeZone)
-                                   .createdDate(createdDate)
-                                   .updatedDate(updatedDate);
+
+        return new DefaultAccount(UUID.randomUUID(), thisKey, thisEmail, name, firstNameLength, Currency.USD,
+                billCycleDay, null, timeZone, locale,
+                null, null, null, null, null, null, null, // add null address fields
+                phone, "test", DateTime.now(), "test", DateTime.now());
     }
 
     @Test
     public void testBasic() throws EntityPersistenceException {
-        Account a = createTestAccountBuilder().build();
-        accountDao.create(a);
+        Account a = createTestAccount(5);
+        accountDao.create(a, context);
         String key = a.getExternalKey();
 
         Account r = accountDao.getAccountByKey(key);
@@ -91,8 +83,8 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
     // simple test to ensure long phone numbers can be stored
     @Test
     public void testLongPhoneNumber() throws EntityPersistenceException {
-        Account account = createTestAccountBuilder().phone("123456789012345678901234").build();
-        accountDao.create(account);
+        Account account = createTestAccount(1, "123456789012345678901234");
+        accountDao.create(account, context);
 
         Account saved = accountDao.getAccountByKey(account.getExternalKey());
         assertNotNull(saved);
@@ -101,19 +93,19 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
     // simple test to ensure excessively long phone numbers cannot be stored
     @Test(expectedExceptions = {EntityPersistenceException.class})
     public void testOverlyLongPhoneNumber() throws EntityPersistenceException {
-        Account account = createTestAccountBuilder().phone("12345678901234567890123456").build();
-        accountDao.create(account);
+        Account account = createTestAccount(1, "12345678901234567890123456");
+        accountDao.create(account, context);
     }
 
     @Test
     public void testGetById() throws EntityPersistenceException {
-        Account account = createTestAccountBuilder().build();
+        Account account = createTestAccount(1);
         UUID id = account.getId();
         String key = account.getExternalKey();
         String name = account.getName();
         int firstNameLength = account.getFirstNameLength();
 
-        accountDao.create(account);
+        accountDao.create(account, context);
 
         account = accountDao.getById(id.toString());
         assertNotNull(account);
@@ -126,12 +118,12 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
 
     @Test
     public void testCustomFields() throws EntityPersistenceException {
-        Account account = createTestAccountBuilder().build();
+        Account account = createTestAccount(1);
         String fieldName = "testField1";
         String fieldValue = "testField1_value";
         account.setFieldValue(fieldName, fieldValue);
 
-        accountDao.create(account);
+        accountDao.create(account, context);
 
         Account thisAccount = accountDao.getAccountByKey(account.getExternalKey());
         assertNotNull(thisAccount);
@@ -141,30 +133,26 @@ 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");
+        Account account = createTestAccount(1);
+        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);
+        Account account = createTestAccount(1);
+        accountDao.create(account, context);
 
         try {
             UUID accountId = accountDao.getIdFromKey(account.getExternalKey());
@@ -182,8 +170,8 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
 
     @Test
     public void testUpdate() throws Exception {
-        final Account account = createTestAccountBuilder().build();
-        accountDao.create(account);
+        final Account account = createTestAccount(1);
+        accountDao.create(account, context);
 
         AccountData accountData = new AccountData() {
             @Override
@@ -270,8 +258,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 +286,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 +302,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 +328,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,31 +361,14 @@ 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);
-    }
-
-    @Test
-    public void testDelete() throws AccountApiException, EntityPersistenceException {
-
-        Account a = createTestAccountBuilder().build();
-        accountDao.create(a);
-        String key = a.getExternalKey();
-
-        Account r = accountDao.getAccountByKey(key);
-        assertNotNull(r);
-        assertEquals(r.getExternalKey(), a.getExternalKey());
-        
-        accountDao.deleteByKey(key);
-        
-        Account s = accountDao.getAccountByKey(key);
-        assertTrue(s==null);
-
+                                                    null, null, null, null, null, null, null, null, null, null,
+                                                    null, null, null, null);
+        accountDao.update(updatedAccount, context);
     }
-
 }
diff --git a/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithEmbeddedDb.java b/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithEmbeddedDb.java
index 1a894a7..f263971 100644
--- a/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithEmbeddedDb.java
+++ b/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithEmbeddedDb.java
@@ -18,7 +18,11 @@ package com.ning.billing.account.glue;
 
 import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.util.clock.MockClockModule;
+import com.ning.billing.util.customfield.CustomFieldMapper;
 import com.ning.billing.util.glue.BusModule;
+import com.ning.billing.util.glue.CallContextModule;
+import com.ning.billing.util.glue.FieldStoreModule;
+import com.ning.billing.util.glue.TagStoreModule;
 import org.skife.jdbi.v2.IDBI;
 
 import java.io.IOException;
@@ -41,8 +45,11 @@ public class AccountModuleWithEmbeddedDb extends AccountModule {
     @Override
     protected void configure() {
         bind(IDBI.class).toInstance(helper.getDBI());
-        super.configure();
         install(new BusModule());
         install(new MockClockModule());
+        install(new CallContextModule());
+        install(new TagStoreModule());
+        install(new FieldStoreModule());
+        super.configure();
     }
 }
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..74fe321 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
@@ -19,6 +19,9 @@ package com.ning.billing.account.glue;
 import com.ning.billing.account.dao.AccountDao;
 import com.ning.billing.account.dao.MockAccountDao;
 import com.ning.billing.util.clock.MockClockModule;
+import com.ning.billing.util.glue.CallContextModule;
+import com.ning.billing.util.glue.FieldStoreModule;
+import com.ning.billing.util.tag.MockTagStoreModuleMemory;
 
 public class AccountModuleWithMocks extends AccountModule {
     @Override
@@ -27,10 +30,12 @@ public class AccountModuleWithMocks extends AccountModule {
         bind(AccountDao.class).to(MockAccountDao.class);
     }
 
-
     @Override
     protected void configure() {
         super.configure();
         install(new MockClockModule());
+        install(new CallContextModule());
+        install(new MockTagStoreModuleMemory());
+        install(new FieldStoreModule());
     }
 }
diff --git a/analytics/pom.xml b/analytics/pom.xml
index d5fff4c..a92d088 100644
--- a/analytics/pom.xml
+++ b/analytics/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.8-SNAPSHOT</version>
+        <version>0.1.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-analytics</artifactId>
diff --git a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
index 5c7ffae..1aafa7c 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
@@ -18,6 +18,9 @@ package com.ning.billing.analytics;
 
 import com.ning.billing.invoice.glue.InvoiceModule;
 import com.ning.billing.payment.setup.PaymentModule;
+import com.ning.billing.util.glue.CallContextModule;
+import com.ning.billing.util.glue.FieldStoreModule;
+import com.ning.billing.util.tag.dao.TagDefinitionSqlDao;
 import org.skife.jdbi.v2.IDBI;
 import com.ning.billing.account.glue.AccountModule;
 import com.ning.billing.analytics.setup.AnalyticsModule;
@@ -31,6 +34,8 @@ import com.ning.billing.util.glue.ClockModule;
 import com.ning.billing.util.glue.NotificationQueueModule;
 import com.ning.billing.util.glue.TagStoreModule;
 
+import java.lang.reflect.Field;
+
 public class AnalyticsTestModule extends AnalyticsModule
 {
     @Override
@@ -39,13 +44,16 @@ public class AnalyticsTestModule extends AnalyticsModule
         super.configure();
 
         // Need to configure a few more things for the EventBus
+        install(new ClockModule());
+        install(new CallContextModule());
+        install(new FieldStoreModule());
+        install(new TagStoreModule());
         install(new AccountModule());
         install(new CatalogModule());
         install(new BusModule());
         install(new EntitlementModule());
         install(new InvoiceModule());
         install(new PaymentModule());
-        install(new ClockModule());
         install(new TagStoreModule());
         install(new NotificationQueueModule());
 
@@ -54,5 +62,7 @@ public class AnalyticsTestModule extends AnalyticsModule
         bind(MysqlTestingHelper.class).toInstance(helper);
         final IDBI dbi = helper.getDBI();
         bind(IDBI.class).toInstance(dbi);
+
+        bind(TagDefinitionSqlDao.class).toInstance(dbi.onDemand(TagDefinitionSqlDao.class));
     }
 }
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 14654d2..495e985 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,11 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
 import org.apache.commons.io.IOUtils;
 import org.joda.time.DateTime;
 import org.testng.Assert;
@@ -88,13 +93,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 DefaultCallContextFactory(clock).createCallContext("Analytics Test", CallOrigin.TEST, UserType.TEST);
 
     @Inject
     private AccountUserApi accountApi;
@@ -133,31 +139,29 @@ public class TestAnalyticsService {
     private InvoiceCreationNotification invoiceCreationNotification;
     private PaymentInfo paymentInfoNotification;
 
-    @BeforeMethod
+    @BeforeMethod(groups = "slow")
     public void cleanup() throws Exception
     {
         helper.cleanupTable("bst");
         helper.cleanupTable("bac");
-
     }
 
 
-    @BeforeClass(alwaysRun = true)
+    @BeforeClass(groups = "slow")
     public void startMysql() throws IOException, ClassNotFoundException, SQLException, EntitlementUserApiException {
         // 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);
@@ -191,7 +195,7 @@ public class TestAnalyticsService {
     }
 
     private void createSubscriptionTransitionEvent(final Account account) throws EntitlementUserApiException {
-        final SubscriptionBundle bundle = entitlementApi.createBundleForAccount(account.getId(), KEY);
+        final SubscriptionBundle bundle = entitlementApi.createBundleForAccount(account.getId(), KEY, context);
 
         // Verify we correctly initialized the account subsystem
         Assert.assertNotNull(bundle);
@@ -241,16 +245,16 @@ 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()
-        );
+                UUID.randomUUID(), invoice.getId(), account.getId(), UUID.randomUUID(), "somePlan", "somePhase", clock.getUTCNow(), clock.getUTCNow().plusDays(1),
+                INVOICE_AMOUNT, ACCOUNT_CURRENCY, context.getUserName(), clock.getUTCNow());
         invoice.addInvoiceItem(invoiceItem);
 
-        invoiceDao.create(invoice);
-        Assert.assertEquals(invoiceDao.getInvoicesByAccount(account.getId()).size(), 1);
-        Assert.assertEquals(invoiceDao.getInvoicesByAccount(account.getId()).get(0).getInvoiceItems().size(), 1);
+        invoiceDao.create(invoice, context);
+        List<Invoice> invoices = invoiceDao.getInvoicesByAccount(account.getId());
+        Assert.assertEquals(invoices.size(), 1);
+        Assert.assertEquals(invoices.get(0).getInvoiceItems().size(), 1);
 
         // It doesn't really matter what the events contain - the listener will go back to the db
         invoiceCreationNotification = new DefaultInvoiceCreationNotification(invoice.getId(), account.getId(),
@@ -259,12 +263,12 @@ public class TestAnalyticsService {
         paymentInfoNotification = new PaymentInfo.Builder().setPaymentId(UUID.randomUUID().toString()).setPaymentMethod(PAYMENT_METHOD).setCardCountry(CARD_COUNTRY).build();
         final PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice.getId(), account.getId(), BigDecimal.TEN,
                 ACCOUNT_CURRENCY, clock.getUTCNow(), clock.getUTCNow(), paymentInfoNotification.getPaymentId(), 1);
-        paymentDao.createPaymentAttempt(paymentAttempt);
-        paymentDao.savePaymentInfo(paymentInfoNotification);
+        paymentDao.createPaymentAttempt(paymentAttempt, context);
+        paymentDao.savePaymentInfo(paymentInfoNotification, context);
         Assert.assertEquals(paymentDao.getPaymentInfo(Arrays.asList(invoice.getId().toString())).size(), 1);
     }
 
-    @AfterClass(alwaysRun = true)
+    @AfterClass(groups = "slow")
     public void stopMysql() {
         helper.stopMysql();
     }
diff --git a/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java b/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java
index b7613cc..1504b15 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java
@@ -53,7 +53,7 @@ import java.util.UUID;
 public class TestAnalyticsDao
 {
     private static final UUID EVENT_ID = UUID.randomUUID();
-    private static final String EVENT_KEY = "12345";
+    private static final String EVENT_KEY = "23456";
     private static final String ACCOUNT_KEY = "pierre-143343-vcc";
 
     private final MysqlTestingHelper helper = new MysqlTestingHelper();
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 f25ec05..4942118 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.CallContext;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 
@@ -140,88 +140,113 @@ 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 setFields(List<CustomField> fields) {
+        throw new UnsupportedOperationException();
     }
 
     @Override
-    public void addFields(List<CustomField> fields) {
-        throw new NotImplementedException();
+    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();
     }
 
     @Override
     public MutableAccountData toMutableAccountData() {
-        throw new NotImplementedException();
+        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 c410698..315ce7f 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
@@ -28,7 +28,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.CallContext;
 import com.ning.billing.util.customfield.CustomField;
 import com.ning.billing.util.tag.Tag;
 
@@ -36,23 +36,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();
     }
 
@@ -64,7 +62,7 @@ public class MockIAccountUserApi implements AccountUserApi
 
     @Override
     public Account getAccountById(final UUID uid) {
-        return new DefaultAccount(account, clock.getUTCNow());
+        return new DefaultAccount(account);
     }
 
     @Override
@@ -79,25 +77,20 @@ public class MockIAccountUserApi implements AccountUserApi
     }
 
 	@Override
-	public void deleteAccountByKey(String externalKey) {
-		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();
 	}
 
     @Override
-    public void updateAccount(UUID accountId, AccountData accountData)
+    public void updateAccount(UUID accountId, AccountData accountData, CallContext context)
             throws AccountApiException {
         throw new NotImplementedException();
     }
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 357caad..99e435f 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
@@ -24,8 +24,11 @@ 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.CallContext;
 import com.ning.billing.util.customfield.CustomField;
 
+import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.TagDefinition;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 
@@ -50,13 +53,13 @@ public class MockSubscription implements Subscription
     }
 
     @Override
-    public void cancel(DateTime requestedDate, boolean eot)
+    public void cancel(DateTime requestedDate, boolean eot, CallContext context)
     {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public void changePlan(final String productName, final BillingPeriod term, final String planSet, DateTime requestedDate)
+    public void changePlan(final String productName, final BillingPeriod term, final String planSet, DateTime requestedDate, CallContext context)
     {
         throw new UnsupportedOperationException();
     }
@@ -68,6 +71,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;
@@ -99,7 +112,7 @@ public class MockSubscription implements Subscription
 
 
     @Override
-    public void uncancel() throws EntitlementUserApiException
+    public void uncancel(CallContext context) throws EntitlementUserApiException
     {
         throw new UnsupportedOperationException();
     }
@@ -141,7 +154,7 @@ public class MockSubscription implements Subscription
     }
 
     @Override
-    public void recreate(PlanPhaseSpecifier spec, DateTime requestedDate)
+    public void recreate(PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
             throws EntitlementUserApiException {
         throw new UnsupportedOperationException();
     }
@@ -157,12 +170,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();
     }
 
@@ -172,7 +195,52 @@ public class MockSubscription implements Subscription
     }
 
     @Override
+    public void clearPersistedFields(CallContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public String getObjectName() {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public List<Tag> getTagList() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean hasTag(String tagName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void addTag(TagDefinition definition) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void addTags(List<Tag> tags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void clearTags() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removeTag(TagDefinition definition) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean generateInvoice() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean processPayment() {
+        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 0b420f0..7254a5d 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 MockEntitlementUserApi(bundleUUID, KEY), new MockIAccountUserApi(ACCOUNT_KEY, CURRENCY));
         listener = new AnalyticsListener(recorder, null);
     }
 
@@ -118,8 +116,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(

api/pom.xml 2(+1 -1)

diff --git a/api/pom.xml b/api/pom.xml
index ac7f6c9..a9cec84 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.8-SNAPSHOT</version>
+        <version>0.1.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-api</artifactId>
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 ed54ce2..3f1e383 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,17 +17,12 @@
 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.customfield.Customizable;
 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();
-
-    public MutableAccountData toMutableAccountData();
 
+public interface Account extends AccountData, Customizable, UpdatableEntity, Taggable {
+    public static String ObjectType = "account";
+    
+    public MutableAccountData toMutableAccountData();    
 }
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 6ac392d..98d6272 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
@@ -19,17 +19,18 @@ package com.ning.billing.account.api;
 import java.util.List;
 import java.util.UUID;
 
-import org.joda.time.DateTimeZone;
-
-import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.callcontext.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;
 
     /***
      *
@@ -37,12 +38,12 @@ 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, CallContext context) throws AccountApiException;
+
+    public void updateAccount(UUID accountId, AccountData accountData, CallContext context) throws AccountApiException;
 
-    public void updateAccount(String key, AccountData accountData) throws AccountApiException;
-    
-    public void updateAccount(UUID accountId, AccountData accountData) throws AccountApiException;
-    
     public Account getAccountByKey(String key);
 
     public Account getAccountById(UUID accountId);
@@ -50,6 +51,4 @@ public interface AccountUserApi {
     public List<Account> getAccounts();
 
     public UUID getIdFromKey(String externalKey) throws AccountApiException;
-
-	public void deleteAccountByKey(String externalKey) 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/entitlement/api/billing/EntitlementBillingApi.java b/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java
index 6dc3e94..0d87399 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java
@@ -19,6 +19,7 @@ package com.ning.billing.entitlement.api.billing;
 import java.util.SortedSet;
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
@@ -36,8 +37,9 @@ public interface EntitlementBillingApi {
 
     public UUID getAccountIdFromSubscriptionId(UUID subscriptionId);
 
-    public void setChargedThroughDate(UUID subscriptionId, DateTime ctd);
+    public void setChargedThroughDate(UUID subscriptionId, DateTime ctd, CallContext context);
 
-    public void setChargedThroughDateFromTransaction(Transmogrifier transactionalDao, UUID subscriptionId, DateTime ctd);
+    public void setChargedThroughDateFromTransaction(Transmogrifier transactionalDao, UUID subscriptionId,
+                                                     DateTime ctd, CallContext context);
 
 }
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApi.java b/api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApi.java
index 826860b..09ac359 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApi.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApi.java
@@ -18,6 +18,7 @@ package com.ning.billing.entitlement.api.migration;
 
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
@@ -44,10 +45,10 @@ public interface EntitlementMigrationApi {
 
     /**
      *
-     * Each case is either a PHASE or a different PlanSpecifer
+     * Each case is either a PHASE or a different PlanSpecifier
      */
     public interface EntitlementSubscriptionMigrationCase {
-        public PlanPhaseSpecifier getPlanPhaseSpecifer();
+        public PlanPhaseSpecifier getPlanPhaseSpecifier();
         public DateTime getEffectiveDate();
         public DateTime getCancelledDate();
     }
@@ -58,17 +59,9 @@ public interface EntitlementMigrationApi {
      * The semantics is 'all or nothing' (atomic operation)
      *
      * @param toBeMigrated all the bundles and associated subscription that should be migrated for the account
+     * @throws EntitlementMigrationApiException an entitlement api exception
      *
      */
-    public void migrate(EntitlementAccountMigration toBeMigrated)
+    public void migrate(EntitlementAccountMigration toBeMigrated, CallContext context)
         throws EntitlementMigrationApiException;
-
-    /**
-     * Remove all the data pertaining to that acount
-     *
-     * @param accountKey
-     */
-    public void undoMigration(UUID accountKey)
-        throws EntitlementMigrationApiException;
-
 }
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java b/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
index 1867cfe..205f256 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
@@ -18,6 +18,8 @@ package com.ning.billing.entitlement.api.user;
 
 import java.util.List;
 import java.util.UUID;
+
+import com.ning.billing.util.callcontext.CallContext;
 import org.joda.time.DateTime;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 
@@ -36,10 +38,10 @@ public interface EntitlementUserApi {
 
     public List<Subscription> getSubscriptionsForKey(String bundleKey);
 
-    public SubscriptionBundle createBundleForAccount(UUID accountId, String bundleKey)
+    public SubscriptionBundle createBundleForAccount(UUID accountId, String bundleKey, CallContext context)
         throws EntitlementUserApiException;
 
-    public Subscription createSubscription(UUID bundleId, PlanPhaseSpecifier spec, DateTime requestedDate)
+    public Subscription createSubscription(UUID bundleId, PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
         throws EntitlementUserApiException;
 
     public DateTime getNextBillingDate(UUID account);
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java b/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
index 83d0b68..0854e2f 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
@@ -21,26 +21,26 @@ import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.ProductCategory;
-import com.ning.billing.util.customfield.CustomizableEntity;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.entity.ExtendedEntity;
 import org.joda.time.DateTime;
 
-import java.util.List;
 import java.util.UUID;
 
 
-public interface Subscription extends CustomizableEntity {
+public interface Subscription extends ExtendedEntity {
 
-    public void cancel(DateTime requestedDate, boolean eot)
+    public void cancel(DateTime requestedDate, boolean eot, CallContext context)
     throws EntitlementUserApiException;
 
-    public void uncancel()
+    public void uncancel(CallContext context)
     throws EntitlementUserApiException;
 
-    public void changePlan(String productName, BillingPeriod term, String planSet, DateTime requestedDate)
+    public void changePlan(String productName, BillingPeriod term, String planSet, DateTime requestedDate, CallContext context)
         throws EntitlementUserApiException;
 
-    public void recreate(PlanPhaseSpecifier spec, DateTime requestedDate)
+    public void recreate(PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
         throws EntitlementUserApiException;
 
     public enum SubscriptionState {
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index baaa3c4..57ea8d9 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -136,6 +136,13 @@ public enum ErrorCode {
 
    /*
     *
+    * Range 3950: Tags
+    *
+    */
+    TAG_DOES_NOT_EXIST(3950, "The tag does not exist (name: %s)"),
+
+   /*
+    *
     * Range 4000: INVOICE
     *
     */
diff --git a/api/src/main/java/com/ning/billing/invoice/api/Invoice.java b/api/src/main/java/com/ning/billing/invoice/api/Invoice.java
index 406160e..0cf30ab 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/Invoice.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/Invoice.java
@@ -16,15 +16,17 @@
 
 package com.ning.billing.invoice.api;
 
-import com.ning.billing.util.entity.Entity;
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.entity.ExtendedEntity;
 import org.joda.time.DateTime;
 
 import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
-public interface Invoice extends Entity {
+public interface Invoice extends ExtendedEntity {
+    public static String ObjectType = "invoice";
+
     boolean addInvoiceItem(InvoiceItem item);
 
     boolean addInvoiceItems(List<InvoiceItem> items);
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java
index 1abcf83..abbc3f1 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java
@@ -26,6 +26,8 @@ import java.util.UUID;
 public interface InvoiceItem extends Entity, Comparable<InvoiceItem> {
     UUID getInvoiceId();
 
+    UUID getAccountId();
+
     UUID getSubscriptionId();
 
     String getPlanName();
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceItemType.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceItemType.java
new file mode 100644
index 0000000..ce066a4
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceItemType.java
@@ -0,0 +1,28 @@
+/*
+ * 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.invoice.api;
+
+public enum InvoiceItemType {
+    FIXED,
+    RECURRING,
+    REVERSAL,
+    MIGRATION,
+    REFUND,
+    CHARGE_BACK,
+    ADD_CREDIT,
+    USE_CREDIT
+}
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoicePayment.java b/api/src/main/java/com/ning/billing/invoice/api/InvoicePayment.java
index 5cdf0de..29f175b 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoicePayment.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoicePayment.java
@@ -22,7 +22,7 @@ import org.joda.time.DateTime;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.util.entity.Entity;
 
-public interface InvoicePayment {
+public interface InvoicePayment extends Entity {
     UUID getPaymentAttemptId();
 
     UUID getInvoiceId();
@@ -32,6 +32,4 @@ public interface InvoicePayment {
     BigDecimal getAmount();
 
     Currency getCurrency();
-
-    DateTime getCreatedDate();
 }
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java b/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java
index 84292c4..9fb0bb3 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java
@@ -20,6 +20,7 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.Currency;
@@ -38,10 +39,10 @@ public interface InvoicePaymentApi {
 
     public InvoicePayment getInvoicePayment(UUID paymentAttemptId);
 
-    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment);
+    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment, CallContext context);
 
-    public void notifyOfPaymentAttempt(UUID invoiceId, BigDecimal amountOutstanding, Currency currency, UUID paymentAttemptId, DateTime paymentAttemptDate);
+    public void notifyOfPaymentAttempt(UUID invoiceId, BigDecimal amountOutstanding, Currency currency, UUID paymentAttemptId, DateTime paymentAttemptDate, CallContext context);
 
-    public void notifyOfPaymentAttempt(UUID invoiceId, UUID paymentAttemptId, DateTime paymentAttemptDate);
+    public void notifyOfPaymentAttempt(UUID invoiceId, UUID paymentAttemptId, DateTime paymentAttemptDate, CallContext context);
 
 }
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 0ee9cd8..66016df 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.CallContext;
 import org.joda.time.DateTime;
 
 import java.math.BigDecimal;
@@ -32,9 +33,13 @@ public interface InvoiceUserApi {
 
     public Invoice getInvoice(UUID invoiceId);
 
-    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment);
+    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment, CallContext context);
 
     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;
+
+    public void tagInvoiceAsWrittenOff(UUID invoiceId, CallContext context);
+
+    public void tagInvoiceAsNotWrittenOff(UUID invoiceId, CallContext context);
 }
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..6c00934 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.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/payment/api/PaymentApi.java b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
index 3b593fd..9076256 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
@@ -22,32 +22,33 @@ import java.util.UUID;
 import javax.annotation.Nullable;
 
 import com.ning.billing.account.api.Account;
+import com.ning.billing.util.callcontext.CallContext;
 
 public interface PaymentApi {
 
-    Either<PaymentError, Void> updatePaymentGateway(String accountKey);
+    Either<PaymentError, Void> updatePaymentGateway(String accountKey, CallContext context);
 
     Either<PaymentError, PaymentMethodInfo> getPaymentMethod(@Nullable String accountKey, String paymentMethodId);
 
     Either<PaymentError, List<PaymentMethodInfo>> getPaymentMethods(String accountKey);
 
-    Either<PaymentError, String> addPaymentMethod(@Nullable String accountKey, PaymentMethodInfo paymentMethod);
+    Either<PaymentError, String> addPaymentMethod(@Nullable String accountKey, PaymentMethodInfo paymentMethod, CallContext context);
 
-    Either<PaymentError, PaymentMethodInfo> updatePaymentMethod(String accountKey, PaymentMethodInfo paymentMethodInfo);
+    Either<PaymentError, PaymentMethodInfo> updatePaymentMethod(String accountKey, PaymentMethodInfo paymentMethodInfo, CallContext context);
 
-    Either<PaymentError, Void> deletePaymentMethod(String accountKey, String paymentMethodId);
+    Either<PaymentError, Void> deletePaymentMethod(String accountKey, String paymentMethodId, CallContext context);
 
-    List<Either<PaymentError, PaymentInfo>> createPayment(String accountKey, List<String> invoiceIds);
-    List<Either<PaymentError, PaymentInfo>> createPayment(Account account, List<String> invoiceIds);
-    Either<PaymentError, PaymentInfo> createPaymentForPaymentAttempt(UUID paymentAttemptId);
+    List<Either<PaymentError, PaymentInfo>> createPayment(String accountKey, List<String> invoiceIds, CallContext context);
+    List<Either<PaymentError, PaymentInfo>> createPayment(Account account, List<String> invoiceIds, CallContext context);
+    Either<PaymentError, PaymentInfo> createPaymentForPaymentAttempt(UUID paymentAttemptId, CallContext context);
 
-    List<Either<PaymentError, PaymentInfo>> createRefund(Account account, List<String> invoiceIds); //TODO
+    List<Either<PaymentError, PaymentInfo>> createRefund(Account account, List<String> invoiceIds, CallContext context); //TODO
 
     Either<PaymentError, PaymentProviderAccount> getPaymentProviderAccount(String accountKey);
 
-    Either<PaymentError, String> createPaymentProviderAccount(Account account);
+    Either<PaymentError, String> createPaymentProviderAccount(Account account, CallContext context);
 
-    Either<PaymentError, Void> updatePaymentProviderAccountContact(String accountKey);
+    Either<PaymentError, Void> updatePaymentProviderAccountContact(String accountKey, CallContext context);
 
     PaymentAttempt getPaymentAttemptForPaymentId(String id);
 
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java b/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java
index 8085310..15ac4ee 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java
@@ -59,8 +59,8 @@ public class PaymentAttempt {
         this.paymentAttemptDate = paymentAttemptDate == null ? new DateTime(DateTimeZone.UTC) : paymentAttemptDate;
         this.paymentId = paymentId;
         this.retryCount = retryCount == null ? 0 : retryCount;
-        this.createdDate = createdDate == null ? new DateTime(DateTimeZone.UTC) : createdDate;
-        this.updatedDate = updatedDate == null ? new DateTime(DateTimeZone.UTC) : updatedDate;
+        this.createdDate = createdDate;
+        this.updatedDate = updatedDate;
     }
 
     public PaymentAttempt(UUID paymentAttemptId,
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentInfo.java b/api/src/main/java/com/ning/billing/payment/api/PaymentInfo.java
index b0fbca6..e325d9a 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentInfo.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentInfo.java
@@ -38,7 +38,7 @@ public class PaymentInfo implements BusEvent {
     private final String paymentMethodId;
     private final String paymentMethod;
     private final String cardType;
-    private final String cardCoutry;
+    private final String cardCountry;
     private final DateTime effectiveDate;
     private final DateTime createdDate;
     private final DateTime updatedDate;
@@ -70,7 +70,7 @@ public class PaymentInfo implements BusEvent {
         this.paymentMethodId = paymentMethodId;
         this.paymentMethod = paymentMethod;
         this.cardType = cardType;
-        this.cardCoutry = cardCountry;
+        this.cardCountry = cardCountry;
         this.effectiveDate = effectiveDate;
         this.createdDate = createdDate == null ? new DateTime(DateTimeZone.UTC) : createdDate;
         this.updatedDate = updatedDate == null ? new DateTime(DateTimeZone.UTC) : updatedDate;
@@ -88,7 +88,7 @@ public class PaymentInfo implements BusEvent {
              src.paymentMethodId,
              src.paymentMethod,
              src.cardType,
-             src.cardCoutry,
+             src.cardCountry,
              src.effectiveDate,
              src.createdDate,
              src.updatedDate);
@@ -131,7 +131,7 @@ public class PaymentInfo implements BusEvent {
     }
 
     public String getCardCountry() {
-        return cardCoutry;
+        return cardCountry;
     }
 
     public String getReferenceId() {
@@ -191,7 +191,7 @@ public class PaymentInfo implements BusEvent {
             this.paymentMethodId = src.paymentMethodId;
             this.paymentMethod = src.paymentMethod;
             this.cardType = src.cardType;
-            this.cardCountry = src.cardCoutry;
+            this.cardCountry = src.cardCountry;
             this.createdDate = src.createdDate;
             this.updatedDate = src.updatedDate;
         }
@@ -303,7 +303,7 @@ public class PaymentInfo implements BusEvent {
                                 paymentMethodId,
                                 paymentMethod,
                                 cardType,
-                                cardCoutry,
+                cardCountry,
                                 effectiveDate,
                                 createdDate,
                                 updatedDate);
@@ -319,7 +319,7 @@ public class PaymentInfo implements BusEvent {
         if (amount != null ? !(amount.compareTo(that.amount) == 0) : that.amount != null) return false;
         if (bankIdentificationNumber != null ? !bankIdentificationNumber.equals(that.bankIdentificationNumber) : that.bankIdentificationNumber != null)
             return false;
-        if (cardCoutry != null ? !cardCoutry.equals(that.cardCoutry) : that.cardCoutry != null) return false;
+        if (cardCountry != null ? !cardCountry.equals(that.cardCountry) : that.cardCountry != null) return false;
         if (cardType != null ? !cardType.equals(that.cardType) : that.cardType != null) return false;
         if (createdDate != null ? !(getUnixTimestamp(createdDate) == getUnixTimestamp(that.createdDate)) : that.createdDate != null) return false;
         if (effectiveDate != null ? !(getUnixTimestamp(effectiveDate) == getUnixTimestamp(that.effectiveDate)) : that.effectiveDate != null)
@@ -342,7 +342,7 @@ public class PaymentInfo implements BusEvent {
 
     @Override
     public String toString() {
-        return "PaymentInfo [paymentId=" + paymentId + ", amount=" + amount + ", refundAmount=" + refundAmount + ", paymentNumber=" + paymentNumber + ", bankIdentificationNumber=" + bankIdentificationNumber + ", status=" + status + ", type=" + type + ", referenceId=" + referenceId + ", paymentMethodId=" + paymentMethodId + ", paymentMethod=" + paymentMethod + ", cardType=" + cardType + ", cardCountry=" + cardCoutry + ", effectiveDate=" + effectiveDate + ", createdDate=" + createdDate + ", updatedDate=" + updatedDate + "]";
+        return "PaymentInfo [paymentId=" + paymentId + ", amount=" + amount + ", refundAmount=" + refundAmount + ", paymentNumber=" + paymentNumber + ", bankIdentificationNumber=" + bankIdentificationNumber + ", status=" + status + ", type=" + type + ", referenceId=" + referenceId + ", paymentMethodId=" + paymentMethodId + ", paymentMethod=" + paymentMethod + ", cardType=" + cardType + ", cardCountry=" + cardCountry + ", effectiveDate=" + effectiveDate + ", createdDate=" + createdDate + ", updatedDate=" + updatedDate + "]";
     }
 
     private static long getUnixTimestamp(final DateTime dateTime) {
diff --git a/api/src/main/java/com/ning/billing/util/api/TagUserApi.java b/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
index 1c2bd7a..d5cb8f2 100644
--- a/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
+++ b/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
@@ -18,6 +18,7 @@ package com.ning.billing.util.api;
 
 import java.util.List;
 
+import com.ning.billing.util.callcontext.CallContext;
 import org.joda.time.DateTime;
 
 import com.ning.billing.util.tag.Tag;
@@ -26,7 +27,7 @@ import com.ning.billing.util.tag.TagDefinition;
 public interface TagUserApi {
     /***
      *
-     * @return
+     * @return the list of all available tag definitions
      */
     public List<TagDefinition> getTagDefinitions();
 
@@ -34,25 +35,27 @@ public interface TagUserApi {
      *
      * @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;
 
     
 	/**
@@ -64,20 +67,16 @@ public interface TagUserApi {
 	public TagDefinition getTagDefinition(String name) throws TagDefinitionApiException;
 	
 	/**
-	 * @param addedBy
-	 * @param addedDate
-	 * @param controlTagType
-	 * @throws TagApiException 
+	 * @param controlTagName
+	 * @throws TagDefinitionApiException
 	 */
-	public Tag createControlTag(String controlTagName, String addedBy, DateTime addedDate) throws TagDefinitionApiException;
+	public Tag createControlTag(String controlTagName) throws TagDefinitionApiException;
 	
 	
 	/**
 	 * @param tagDefinitionName
-	 * @param addedBy
-	 * @param addedDate
 	 * @return
 	 */
-	public Tag createDescriptiveTag(String tagDefinitionName, String addedBy, DateTime addedDate) throws TagDefinitionApiException;
+	public Tag createDescriptiveTag(String tagDefinitionName) throws TagDefinitionApiException;
 	
 }
diff --git a/api/src/main/java/com/ning/billing/util/callcontext/CallContext.java b/api/src/main/java/com/ning/billing/util/callcontext/CallContext.java
new file mode 100644
index 0000000..da36f10
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/callcontext/CallContext.java
@@ -0,0 +1,27 @@
+/*
+ * 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.callcontext;
+
+import org.joda.time.DateTime;
+
+public interface CallContext {
+    public String getUserName();
+    public CallOrigin getCallOrigin();
+    public UserType getUserType();
+    public DateTime getCreatedDate();
+    public DateTime getUpdatedDate();
+}
diff --git a/api/src/main/java/com/ning/billing/util/callcontext/CallOrigin.java b/api/src/main/java/com/ning/billing/util/callcontext/CallOrigin.java
new file mode 100644
index 0000000..8806647
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/callcontext/CallOrigin.java
@@ -0,0 +1,23 @@
+/*
+ * 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.callcontext;
+
+public enum CallOrigin {
+    INTERNAL,
+    EXTERNAL,
+    TEST
+}
diff --git a/api/src/main/java/com/ning/billing/util/callcontext/UserType.java b/api/src/main/java/com/ning/billing/util/callcontext/UserType.java
new file mode 100644
index 0000000..b10a314
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/callcontext/UserType.java
@@ -0,0 +1,25 @@
+/*
+ * 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.callcontext;
+
+public enum UserType {
+    SYSTEM,
+    ADMIN,
+    CUSTOMER,
+    MIGRATION,
+    TEST
+}
diff --git a/api/src/main/java/com/ning/billing/util/ChangeType.java b/api/src/main/java/com/ning/billing/util/ChangeType.java
new file mode 100644
index 0000000..d6db6d3
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/ChangeType.java
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+public enum ChangeType {
+    INSERT,
+    UPDATE,
+    DELETE
+}
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/ExtendedEntity.java b/api/src/main/java/com/ning/billing/util/entity/ExtendedEntity.java
new file mode 100644
index 0000000..b64e9c3
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/entity/ExtendedEntity.java
@@ -0,0 +1,23 @@
+/*
+ * 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.customfield.Customizable;
+import com.ning.billing.util.tag.Taggable;
+
+public interface ExtendedEntity extends Entity, Taggable, Customizable {
+}
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/ControlTagType.java b/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
index 902f4a1..406ec54 100644
--- a/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
+++ b/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
@@ -18,7 +18,8 @@ package com.ning.billing.util.tag;
 
 public enum ControlTagType {
     AUTO_PAY_OFF("Suspends payments until removed.", true, false),
-    AUTO_INVOICING_OFF("Suspends invoicing until removed.", false, true);
+    AUTO_INVOICING_OFF("Suspends invoicing until removed.", false, true),
+    WRITTEN_OFF("Indicated that an invoice is written off. No billing or payment effect.", false, false);
 
     private final String description;
     private final boolean autoPaymentOff;
@@ -34,11 +35,11 @@ public enum ControlTagType {
         return this.description;
     }
 
-    public boolean autoPaymentOff() {
+    public boolean getAutoPaymentOff() {
         return this.autoPaymentOff;
     }
 
-    public boolean autoInvoicingOff() {
+    public boolean getAutoInvoicingOff() {
         return this.autoInvoicingOff;
     }
 }
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);

beatrix/pom.xml 2(+1 -1)

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 28f478f..64726c8 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.8-SNAPSHOT</version>
+        <version>0.1.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-beatrix</artifactId>
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/MockModule.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/MockModule.java
index 58cc017..304cb0b 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/MockModule.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/MockModule.java
@@ -22,6 +22,9 @@ import java.io.IOException;
 import java.net.URL;
 import java.util.Set;
 
+import com.ning.billing.util.glue.CallContextModule;
+import com.ning.billing.util.glue.FieldStoreModule;
+import com.ning.billing.util.glue.TagStoreModule;
 import org.skife.config.ConfigurationObjectFactory;
 import org.skife.jdbi.v2.IDBI;
 
@@ -79,9 +82,12 @@ public class MockModule extends AbstractModule {
             bind(IDBI.class).toInstance(dbi);
         }
 
+        install(new CallContextModule());
         install(new GlobalLockerModule());
         install(new BusModule());
         install(new NotificationQueueModule());
+        install(new TagStoreModule());
+        install(new FieldStoreModule());
         install(new AccountModule());
         install(new CatalogModule());
         install(new EntitlementModule());
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 9fca2bf..e334966 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
@@ -19,6 +19,7 @@ package com.ning.billing.beatrix.integration;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
 
 import java.io.IOException;
 import java.math.BigDecimal;
@@ -33,6 +34,11 @@ 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.invoice.model.InvoicingConfiguration;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.RandomStringUtils;
 import org.joda.time.DateTime;
@@ -53,7 +59,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;
@@ -79,8 +84,8 @@ import com.ning.billing.util.bus.BusService;
 @Test(groups = "slow")
 @Guice(modules = {MockModule.class})
 public class TestIntegration {
-    private static final int NUMBER_OF_DECIMALS = 4;
-    private static final int ROUNDING_METHOD = BigDecimal.ROUND_HALF_EVEN;
+    private static final int NUMBER_OF_DECIMALS = InvoicingConfiguration.getNumberOfDecimals();
+    private static final int ROUNDING_METHOD = InvoicingConfiguration.getRoundingMode();
 
     private static final BigDecimal ONE = new BigDecimal("1.0000").setScale(NUMBER_OF_DECIMALS);
     private static final BigDecimal TWENTY_NINE = new BigDecimal("29.0000").setScale(NUMBER_OF_DECIMALS);
@@ -96,6 +101,7 @@ public class TestIntegration {
 
     @Inject
     private ClockMock clock;
+    private CallContext context;
 
     @Inject
     private Lifecycle lifecycle;
@@ -125,8 +131,6 @@ public class TestIntegration {
 
     private void setupMySQL() throws IOException
     {
-
-
         final String accountDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
         final String entitlementDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
         final String invoiceDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
@@ -147,6 +151,8 @@ public class TestIntegration {
 
         setupMySQL();
 
+        context = new DefaultCallContextFactory(clock).createCallContext("Integration Test", CallOrigin.TEST, UserType.TEST);
+
         /**
          * Initialize lifecyle for subset of services
          */
@@ -240,7 +246,9 @@ public class TestIntegration {
             }
         }
 
-        assertTrue(wasFound);
+        if (!wasFound) {
+            fail();
+        }
 
         DateTime ctd = subscription.getChargedThroughDate();
         assertNotNull(ctd);
@@ -304,13 +312,13 @@ 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);
 
         // set clock to the initial start date
         clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
-        SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever2");
+        SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever2", context);
 
         String productName = "Shotgun";
         BillingPeriod term = BillingPeriod.MONTHLY;
@@ -322,7 +330,7 @@ public class TestIntegration {
         busHandler.pushExpectedEvent(NextEvent.CREATE);
         busHandler.pushExpectedEvent(NextEvent.INVOICE);
         SubscriptionData subscription = (SubscriptionData) entitlementUserApi.createSubscription(bundle.getId(),
-                new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), null);
+                new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), null, context);
         assertNotNull(subscription);
         assertTrue(busHandler.isCompleted(DELAY));
 
@@ -345,7 +353,7 @@ public class TestIntegration {
         assertTrue(busHandler.isCompleted(DELAY));
 
         subscription = (SubscriptionData) entitlementUserApi.getSubscriptionFromId(subscription.getId());
-        subscription.cancel(clock.getUTCNow(), false);
+        subscription.cancel(clock.getUTCNow(), false, context);
 
         // MOVE AFTER CANCEL DATE AND EXPECT EVENT : NextEvent.CANCEL
         busHandler.pushExpectedEvent(NextEvent.CANCEL);
@@ -361,7 +369,7 @@ public class TestIntegration {
         busHandler.pushExpectedEvent(NextEvent.CREATE);
         busHandler.pushExpectedEvent(NextEvent.INVOICE);
         busHandler.pushExpectedEvent(NextEvent.PAYMENT);
-        subscription.recreate(new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), endDate);
+        subscription.recreate(new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), endDate, context);
         assertTrue(busHandler.isCompleted(DELAY));
 
 
@@ -370,13 +378,13 @@ 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);
 
         // set clock to the initial start date
         clock.setDeltaFromReality(initialCreationDate.getMillis() - clock.getUTCNow().getMillis());
-        SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever");
+        SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", context);
 
         String productName = "Shotgun";
         BillingPeriod term = BillingPeriod.MONTHLY;
@@ -388,7 +396,7 @@ public class TestIntegration {
         busHandler.pushExpectedEvent(NextEvent.CREATE);
         busHandler.pushExpectedEvent(NextEvent.INVOICE);
         SubscriptionData subscription = (SubscriptionData) entitlementUserApi.createSubscription(bundle.getId(),
-                new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), null);
+                new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), null, context);
         assertNotNull(subscription);
 
         assertTrue(busHandler.isCompleted(DELAY));
@@ -411,7 +419,7 @@ public class TestIntegration {
         BillingPeriod newTerm = BillingPeriod.MONTHLY;
         String newPlanSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
         String newProductName = "Assault-Rifle";
-        subscription.changePlan(newProductName, newTerm, newPlanSetName, clock.getUTCNow());
+        subscription.changePlan(newProductName, newTerm, newPlanSetName, clock.getUTCNow(), context);
 
         assertTrue(busHandler.isCompleted(DELAY));
 
@@ -486,7 +494,7 @@ public class TestIntegration {
         newPlanSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
         newProductName = "Pistol";
         subscription = (SubscriptionData) entitlementUserApi.getSubscriptionFromId(subscription.getId());
-        subscription.changePlan(newProductName, newTerm, newPlanSetName, clock.getUTCNow());
+        subscription.changePlan(newProductName, newTerm, newPlanSetName, clock.getUTCNow(), context);
 
         //
         // MOVE TIME AFTER CTD AND EXPECT BOTH EVENTS : NextEvent.CHANGE NextEvent.INVOICE
@@ -534,7 +542,7 @@ public class TestIntegration {
         // FINALLY CANCEL SUBSCRIPTION EOT
         //
         subscription = (SubscriptionData) entitlementUserApi.getSubscriptionFromId(subscription.getId());
-        subscription.cancel(clock.getUTCNow(), false);
+        subscription.cancel(clock.getUTCNow(), false, context);
 
         // MOVE AFTER CANCEL DATE AND EXPECT EVENT : NextEvent.CANCEL
         busHandler.pushExpectedEvent(NextEvent.CANCEL);
@@ -562,10 +570,10 @@ 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");
+        SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", context);
 
         String productName = "Shotgun";
         BillingPeriod term = BillingPeriod.MONTHLY;
@@ -574,7 +582,7 @@ public class TestIntegration {
         busHandler.pushExpectedEvent(NextEvent.CREATE);
         busHandler.pushExpectedEvent(NextEvent.INVOICE);
         SubscriptionData subscription = (SubscriptionData) entitlementUserApi.createSubscription(bundle.getId(),
-                new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), null);
+                new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), null, context);
         assertNotNull(subscription);
 
         assertTrue(busHandler.isCompleted(DELAY));
@@ -584,7 +592,7 @@ public class TestIntegration {
         BillingPeriod newTerm = BillingPeriod.MONTHLY;
         String newPlanSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
         String newProductName = "Assault-Rifle";
-        subscription.changePlan(newProductName, newTerm, newPlanSetName, clock.getUTCNow());
+        subscription.changePlan(newProductName, newTerm, newPlanSetName, clock.getUTCNow(), context);
 
         assertTrue(busHandler.isCompleted(DELAY));
 
@@ -599,7 +607,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";
@@ -607,10 +615,10 @@ public class TestIntegration {
 
         busHandler.pushExpectedEvent(NextEvent.CREATE);
         busHandler.pushExpectedEvent(NextEvent.INVOICE);
-        SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(accountId, "testKey");
+        SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(accountId, "testKey", context);
         SubscriptionData subscription = (SubscriptionData) entitlementUserApi.createSubscription(bundle.getId(),
                                         new PlanPhaseSpecifier(productName, ProductCategory.BASE,
-                                        BillingPeriod.MONTHLY, planSetName, PhaseType.TRIAL), null);
+                                        BillingPeriod.MONTHLY, planSetName, PhaseType.TRIAL), null, context);
         assertTrue(busHandler.isCompleted(DELAY));
         List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId);
         assertNotNull(invoices);
@@ -638,7 +646,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";
@@ -717,6 +725,5 @@ public class TestIntegration {
                 return null;
             }
         };
-        return accountData;
     }
 }

catalog/pom.xml 2(+1 -1)

diff --git a/catalog/pom.xml b/catalog/pom.xml
index dee138d..489aba0 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.8-SNAPSHOT</version>
+        <version>0.1.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-catalog</artifactId>
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index cccf568..426d9f8 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.8-SNAPSHOT</version>
+        <version>0.1.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-entitlement</artifactId>
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java
index b354348..2965b8f 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java
@@ -48,11 +48,11 @@ public class MigrationPlanAligner {
 
         try {
             TimedMigration [] events = null;
-            Plan plan0 = catalogService.getFullCatalog().findPlan(input[0].getPlanPhaseSpecifer().getProductName(),
-                    input[0].getPlanPhaseSpecifer().getBillingPeriod(), input[0].getPlanPhaseSpecifer().getPriceListName(), now);
+            Plan plan0 = catalogService.getFullCatalog().findPlan(input[0].getPlanPhaseSpecifier().getProductName(),
+                    input[0].getPlanPhaseSpecifier().getBillingPeriod(), input[0].getPlanPhaseSpecifier().getPriceListName(), now);
 
-            Plan plan1 = (input.length > 1) ? catalogService.getFullCatalog().findPlan(input[1].getPlanPhaseSpecifer().getProductName(),
-                    input[1].getPlanPhaseSpecifer().getBillingPeriod(), input[1].getPlanPhaseSpecifer().getPriceListName(), now) :
+            Plan plan1 = (input.length > 1) ? catalogService.getFullCatalog().findPlan(input[1].getPlanPhaseSpecifier().getProductName(),
+                    input[1].getPlanPhaseSpecifier().getBillingPeriod(), input[1].getPlanPhaseSpecifier().getPriceListName(), now) :
                         null;
 
             DateTime migrationStartDate = now;
@@ -60,21 +60,21 @@ public class MigrationPlanAligner {
             if (isRegularMigratedSubscription(input)) {
 
                 events = getEventsOnRegularMigration(plan0,
-                        getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
-                        input[0].getPlanPhaseSpecifer().getPriceListName(),
+                        getPlanPhase(plan0, input[0].getPlanPhaseSpecifier().getPhaseType()),
+                        input[0].getPlanPhaseSpecifier().getPriceListName(),
                         now);
 
             } else if (isRegularFutureCancelledMigratedSubscription(input)) {
 
                 events = getEventsOnFuturePlanCancelMigration(plan0,
-                        getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
-                        input[0].getPlanPhaseSpecifer().getPriceListName(),
+                        getPlanPhase(plan0, input[0].getPlanPhaseSpecifier().getPhaseType()),
+                        input[0].getPlanPhaseSpecifier().getPriceListName(),
                         now,
                         input[0].getCancelledDate());
 
             } else if (isPhaseChangeMigratedSubscription(input)) {
 
-                PhaseType curPhaseType = input[0].getPlanPhaseSpecifer().getPhaseType();
+                PhaseType curPhaseType = input[0].getPlanPhaseSpecifier().getPhaseType();
                 Duration curPhaseDuration = null;
                 for (PlanPhase cur : plan0.getAllPhases()) {
                     if (cur.getPhaseType() == curPhaseType) {
@@ -89,18 +89,18 @@ public class MigrationPlanAligner {
 
                 migrationStartDate = DefaultClock.removeDuration(input[1].getEffectiveDate(), curPhaseDuration);
                 events = getEventsOnFuturePhaseChangeMigration(plan0,
-                        getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
-                        input[0].getPlanPhaseSpecifer().getPriceListName(),
+                        getPlanPhase(plan0, input[0].getPlanPhaseSpecifier().getPhaseType()),
+                        input[0].getPlanPhaseSpecifier().getPriceListName(),
                         migrationStartDate,
                         input[1].getEffectiveDate());
 
             } else if (isPlanChangeMigratedSubscription(input)) {
 
                 events = getEventsOnFuturePlanChangeMigration(plan0,
-                        getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
+                        getPlanPhase(plan0, input[0].getPlanPhaseSpecifier().getPhaseType()),
                         plan1,
-                        getPlanPhase(plan1, input[1].getPlanPhaseSpecifer().getPhaseType()),
-                        input[0].getPlanPhaseSpecifer().getPriceListName(),
+                        getPlanPhase(plan1, input[1].getPlanPhaseSpecifier().getPhaseType()),
+                        input[0].getPlanPhaseSpecifier().getPriceListName(),
                         now,
                         input[1].getEffectiveDate());
 
@@ -181,15 +181,15 @@ public class MigrationPlanAligner {
         if (input.length != 2) {
             return false;
         }
-        return (isSamePlan(input[0].getPlanPhaseSpecifer(), input[1].getPlanPhaseSpecifer()) &&
-                !isSamePhase(input[0].getPlanPhaseSpecifer(), input[1].getPlanPhaseSpecifer()));
+        return (isSamePlan(input[0].getPlanPhaseSpecifier(), input[1].getPlanPhaseSpecifier()) &&
+                !isSamePhase(input[0].getPlanPhaseSpecifier(), input[1].getPlanPhaseSpecifier()));
     }
 
     private boolean isPlanChangeMigratedSubscription(EntitlementSubscriptionMigrationCase [] input) {
         if (input.length != 2) {
             return false;
         }
-        return ! isSamePlan(input[0].getPlanPhaseSpecifer(), input[1].getPlanPhaseSpecifer());
+        return ! isSamePlan(input[0].getPlanPhaseSpecifier(), input[1].getPlanPhaseSpecifier());
     }
 
     private boolean isSamePlan(PlanPhaseSpecifier plan0, PlanPhaseSpecifier plan1) {
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 3029fc2..f6feeac 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
@@ -22,6 +22,13 @@ import java.util.SortedSet;
 import java.util.TreeSet;
 import java.util.UUID;
 
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.ChangeType;
+import com.ning.billing.util.audit.dao.AuditSqlDao;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.callcontext.CallContextFactory;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
@@ -46,38 +53,43 @@ import com.ning.billing.catalog.api.Product;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionFactory;
 import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
 import com.ning.billing.entitlement.api.user.SubscriptionTransition;
 import com.ning.billing.entitlement.api.user.SubscriptionTransition.SubscriptionTransitionType;
 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 CallContextFactory factory;
     private final EntitlementDao entitlementDao;
     private final AccountUserApi accountApi;
     private final CatalogService catalogService;
+    private final SubscriptionFactory subscriptionFactory;
+    private static final String SUBSCRIPTION_TABLE_NAME = "subscriptions";
 
     @Inject
-    public DefaultEntitlementBillingApi(final EntitlementDao dao, final AccountUserApi accountApi, final CatalogService catalogService) {
+    public DefaultEntitlementBillingApi(final CallContextFactory factory, final SubscriptionFactory subscriptionFactory, final EntitlementDao dao, final AccountUserApi accountApi, final CatalogService catalogService) {
         super();
+        this.factory = factory;
+        this.subscriptionFactory = subscriptionFactory;
         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();
 
         List<SubscriptionBundle> bundles = entitlementDao.getSubscriptionBundleForAccount(accountId);
         SortedSet<BillingEvent> result = new TreeSet<BillingEvent>();
         for (final SubscriptionBundle bundle: bundles) {
-        	List<Subscription> subscriptions = entitlementDao.getSubscriptions(bundle.getId());
+        	List<Subscription> subscriptions = entitlementDao.getSubscriptions(subscriptionFactory, bundle.getId());
 
         	for (final Subscription subscription: subscriptions) {
         		for (final SubscriptionTransition transition : ((SubscriptionData) subscription).getBillingTransitions()) {
@@ -101,7 +113,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 +137,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 = factory.createCallContext(API_USER_NAME, CallOrigin.INTERNAL, UserType.SYSTEM);
+    				result = calculateBcdFromSubscription(subscription, plan, account, context);
     			}
     		break;
     		case BUNDLE :
@@ -141,7 +156,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,11 +169,11 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
         } catch (CatalogApiException e) {
             log.error("Unexpected catalog error encountered when updating BCD",e);
         }
-        
+
         MutableAccountData modifiedData = account.toMutableAccountData();
         modifiedData.setBillCycleDay(result);
 
-        accountApi.updateAccount(account.getExternalKey(), modifiedData);
+        accountApi.updateAccount(account.getExternalKey(), modifiedData, context);
         return result;
     }
 
@@ -171,18 +187,19 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
 
 
     @Override
-    public void setChargedThroughDate(final UUID subscriptionId, final DateTime ctd) {
-        SubscriptionData subscription = (SubscriptionData) entitlementDao.getSubscriptionFromId(subscriptionId);
+    public void setChargedThroughDate(final UUID subscriptionId, final DateTime ctd, CallContext context) {
+        SubscriptionData subscription = (SubscriptionData) entitlementDao.getSubscriptionFromId(subscriptionFactory, subscriptionId);
 
         SubscriptionBuilder builder = new SubscriptionBuilder(subscription)
             .setChargedThroughDate(ctd)
             .setPaidThroughDate(subscription.getPaidThroughDate());
 
-        entitlementDao.updateSubscription(new SubscriptionData(builder));
+        entitlementDao.updateSubscription(new SubscriptionData(builder), context);
     }
 
     @Override
-    public void setChargedThroughDateFromTransaction(final Transmogrifier transactionalDao, final UUID subscriptionId, final DateTime ctd) {
+    public void setChargedThroughDateFromTransaction(final Transmogrifier transactionalDao, final UUID subscriptionId,
+                                                     final DateTime ctd, final CallContext context) {
         SubscriptionSqlDao subscriptionSqlDao = transactionalDao.become(SubscriptionSqlDao.class);
         SubscriptionData subscription = (SubscriptionData) subscriptionSqlDao.getSubscriptionFromId(subscriptionId.toString());
 
@@ -194,10 +211,12 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
             DateTime chargedThroughDate = subscription.getChargedThroughDate();
             if (chargedThroughDate == null || chargedThroughDate.isBefore(ctd)) {
                 subscriptionSqlDao.updateSubscription(subscriptionId.toString(), subscription.getActiveVersion(),
-                                                      ctd.toDate(), paidThroughDate);
+                                                      ctd.toDate(), paidThroughDate, context);
+                AuditSqlDao auditSqlDao = transactionalDao.become(AuditSqlDao.class);
+                auditSqlDao.insertAuditFromTransaction(SUBSCRIPTION_TABLE_NAME, subscriptionId.toString(), ChangeType.UPDATE, context);
             }
         }
     }
-    
- 
+
+
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
index 812378b..d0fb35b 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
@@ -23,6 +23,7 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
 import org.joda.time.DateTime;
 
 import com.google.common.collect.Lists;
@@ -52,8 +53,6 @@ import com.ning.billing.entitlement.exceptions.EntitlementError;
 import com.ning.billing.util.clock.Clock;
 
 public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
-
-
     private final EntitlementDao dao;
     private final MigrationPlanAligner migrationAligner;
     private final SubscriptionFactory factory;
@@ -71,15 +70,10 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
     }
 
     @Override
-    public void migrate(EntitlementAccountMigration toBeMigrated)
+    public void migrate(EntitlementAccountMigration toBeMigrated, CallContext context)
     throws EntitlementMigrationApiException {
         AccountMigrationData accountMigrationData = createAccountMigrationData(toBeMigrated);
-        dao.migrate(toBeMigrated.getAccountKey(), accountMigrationData);
-    }
-
-    @Override
-    public void undoMigration(UUID accountId) {
-        dao.undoMigration(accountId);
+        dao.migrate(toBeMigrated.getAccountKey(), accountMigrationData, context);
     }
 
     private AccountMigrationData createAccountMigrationData(EntitlementAccountMigration toBeMigrated)
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
index baf45f1..5554c9c 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
@@ -20,6 +20,7 @@ import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.catalog.api.Catalog;
+import com.ning.billing.util.callcontext.CallContext;
 import org.joda.time.DateTime;
 import com.google.inject.Inject;
 import com.ning.billing.ErrorCode;
@@ -44,16 +45,18 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
     private final CatalogService catalogService;
     private final SubscriptionApiService apiService;
     private final AddonUtils addonUtils;
+    private final SubscriptionFactory subscriptionFactory;
 
     @Inject
     public DefaultEntitlementUserApi(Clock clock, EntitlementDao dao, CatalogService catalogService,
-            SubscriptionApiService apiService, AddonUtils addonUtils) {
+            SubscriptionApiService apiService, final SubscriptionFactory subscriptionFactory, AddonUtils addonUtils) {
         super();
         this.clock = clock;
         this.apiService = apiService;
         this.dao = dao;
         this.catalogService = catalogService;
         this.addonUtils = addonUtils;
+        this.subscriptionFactory = subscriptionFactory;
     }
 
     @Override
@@ -63,7 +66,7 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
 
     @Override
     public Subscription getSubscriptionFromId(UUID id) {
-        return dao.getSubscriptionFromId(id);
+        return dao.getSubscriptionFromId(subscriptionFactory, id);
     }
 
     @Override
@@ -78,23 +81,24 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
 
     @Override
     public List<Subscription> getSubscriptionsForKey(String bundleKey) {
-        return dao.getSubscriptionsForKey(bundleKey);
+        return dao.getSubscriptionsForKey(subscriptionFactory, bundleKey);
     }
 
     @Override
     public List<Subscription> getSubscriptionsForBundle(UUID bundleId) {
-        return dao.getSubscriptions(bundleId);
+        return dao.getSubscriptions(subscriptionFactory, bundleId);
     }
 
     @Override
-    public SubscriptionBundle createBundleForAccount(UUID accountId, String bundleName)
+    public SubscriptionBundle createBundleForAccount(UUID accountId, String bundleName, CallContext context)
     throws EntitlementUserApiException {
         SubscriptionBundleData bundle = new SubscriptionBundleData(bundleName, accountId);
-        return dao.createSubscriptionBundle(bundle);
+        return dao.createSubscriptionBundle(bundle, context);
     }
 
     @Override
-    public Subscription createSubscription(UUID bundleId, PlanPhaseSpecifier spec, DateTime requestedDate) throws EntitlementUserApiException {
+    public Subscription createSubscription(UUID bundleId, PlanPhaseSpecifier spec, DateTime requestedDate,
+                                           CallContext context) throws EntitlementUserApiException {
         try {
             String realPriceList = (spec.getPriceListName() == null) ? PriceListSet.DEFAULT_PRICELIST_NAME : spec.getPriceListName();
             DateTime now = clock.getUTCNow();
@@ -119,7 +123,7 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
             }
 
             DateTime bundleStartDate = null;
-            SubscriptionData baseSubscription = (SubscriptionData) dao.getBaseSubscription(bundleId);
+            SubscriptionData baseSubscription = (SubscriptionData) dao.getBaseSubscription(subscriptionFactory, bundleId);
             switch(plan.getProduct().getCategory()) {
             case BASE:
                 if (baseSubscription != null) {
@@ -127,7 +131,7 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
                         throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_BP_EXISTS, bundleId);
                     } else {
                         // If we do create on an existing CANCELLED BP, this is equivalent to call recreate on that Subscription.
-                        baseSubscription.recreate(spec, requestedDate);
+                        baseSubscription.recreate(spec, requestedDate, context);
                         return baseSubscription;
                     }
                 }
@@ -158,7 +162,7 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
                 .setCategory(plan.getProduct().getCategory())
                 .setBundleStartDate(bundleStartDate)
                 .setStartDate(effectiveDate),
-            plan, spec.getPhaseType(), realPriceList, requestedDate, effectiveDate, now);
+            plan, spec.getPhaseType(), realPriceList, requestedDate, effectiveDate, now, context);
 
             return subscription;
         } catch (CatalogApiException e) {
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 e1b1502..39efb09 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,13 +29,13 @@ 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.CallContext;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.DefaultClock;
 import org.joda.time.DateTime;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.UUID;
 
 public class SubscriptionApiService {
 
@@ -52,18 +52,17 @@ public class SubscriptionApiService {
         this.dao = dao;
     }
 
-
-
     public SubscriptionData createPlan(SubscriptionBuilder builder, Plan plan, PhaseType initialPhase,
-            String realPriceList, DateTime requestedDate, DateTime effectiveDate, DateTime processedDate)
+            String realPriceList, DateTime requestedDate, DateTime effectiveDate, DateTime processedDate,
+            CallContext context)
         throws EntitlementUserApiException {
 
         SubscriptionData subscription = new SubscriptionData(builder, this, clock);
-        createFromSubscription(subscription, plan, initialPhase, realPriceList, requestedDate, effectiveDate, processedDate, false);
+        createFromSubscription(subscription, plan, initialPhase, realPriceList, requestedDate, effectiveDate, processedDate, false, context);
         return subscription;
     }
 
-    public void recreatePlan(SubscriptionData subscription, PlanPhaseSpecifier spec, DateTime requestedDate)
+    public void recreatePlan(SubscriptionData subscription, PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
         throws EntitlementUserApiException {
 
         SubscriptionState currentState = subscription.getState();
@@ -86,15 +85,15 @@ public class SubscriptionApiService {
             DateTime effectiveDate = requestedDate;
             DateTime processedDate = now;
 
-            createFromSubscription(subscription, plan, spec.getPhaseType(), realPriceList, requestedDate, effectiveDate, processedDate, true);
+            createFromSubscription(subscription, plan, spec.getPhaseType(), realPriceList, requestedDate, effectiveDate, processedDate, true, context);
         } catch (CatalogApiException e) {
             throw new EntitlementUserApiException(e);
         }
     }
 
-
     private void createFromSubscription(SubscriptionData subscription, Plan plan, PhaseType initialPhase,
-            String realPriceList, DateTime requestedDate, DateTime effectiveDate, DateTime processedDate, boolean reCreate)
+            String realPriceList, DateTime requestedDate, DateTime effectiveDate, DateTime processedDate,
+            boolean reCreate, CallContext context)
         throws EntitlementUserApiException {
 
         try {
@@ -122,9 +121,9 @@ public class SubscriptionApiService {
                 events.add(nextPhaseEvent);
             }
             if (reCreate) {
-                dao.recreateSubscription(subscription.getId(), events);
+                dao.recreateSubscription(subscription.getId(), events, context);
             } else {
-                dao.createSubscription(subscription, events);
+                dao.createSubscription(subscription, events, context);
             }
             subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
         } catch (CatalogApiException e) {
@@ -132,10 +131,7 @@ public class SubscriptionApiService {
         }
     }
 
-
-
-
-    public void cancel(SubscriptionData subscription, DateTime requestedDate, boolean eot)
+    public void cancel(SubscriptionData subscription, DateTime requestedDate, boolean eot, CallContext context)
         throws EntitlementUserApiException {
 
         try {
@@ -167,7 +163,7 @@ public class SubscriptionApiService {
             .setRequestedDate(requestedDate)
             .setFromDisk(true));
 
-            dao.cancelSubscription(subscription.getId(), cancelEvent);
+            dao.cancelSubscription(subscription.getId(), cancelEvent, context);
             subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
         } catch (CatalogApiException e) {
             throw new EntitlementUserApiException(e);
@@ -175,7 +171,7 @@ public class SubscriptionApiService {
     }
 
 
-    public void uncancel(SubscriptionData subscription)
+    public void uncancel(SubscriptionData subscription, CallContext context)
         throws EntitlementUserApiException {
 
         if (!subscription.isSubscriptionFutureCancelled()) {
@@ -201,13 +197,12 @@ public class SubscriptionApiService {
         if (nextPhaseEvent != null) {
             uncancelEvents.add(nextPhaseEvent);
         }
-        dao.uncancelSubscription(subscription.getId(), uncancelEvents);
+        dao.uncancelSubscription(subscription.getId(), uncancelEvents, context);
         subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
     }
 
-
     public void changePlan(SubscriptionData subscription, String productName, BillingPeriod term,
-            String priceList, DateTime requestedDate)
+            String priceList, DateTime requestedDate, CallContext context)
         throws EntitlementUserApiException {
 
         try {
@@ -274,15 +269,15 @@ public class SubscriptionApiService {
                         changeEvents.add(nextPhaseEvent);
                     }
                     changeEvents.add(changeEvent);
-                    dao.changePlan(subscription.getId(), changeEvents);
+                    dao.changePlan(subscription.getId(), changeEvents, context);
                     subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
         } catch (CatalogApiException e) {
             throw new EntitlementUserApiException(e);
         }
     }
 
-    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 ee7bb5a..a67c502 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,26 +31,27 @@ import com.ning.billing.entitlement.api.user.SubscriptionTransitionDataIterator.
 import com.ning.billing.entitlement.api.user.SubscriptionTransitionDataIterator.TimeLimit;
 import com.ning.billing.entitlement.api.user.SubscriptionTransitionDataIterator.Visibility;
 import com.ning.billing.entitlement.events.EntitlementEvent;
-import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
 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.CallContext;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.customfield.CustomField;
-import com.ning.billing.util.customfield.CustomizableEntityBase;
 
+import com.ning.billing.util.entity.ExtendedEntityBase;
 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.LinkedList;
 import java.util.List;
 import java.util.UUID;
 
-public class SubscriptionData extends CustomizableEntityBase implements Subscription {
+public class SubscriptionData extends ExtendedEntityBase implements Subscription {
 
     private final static Logger log = LoggerFactory.getLogger(SubscriptionData.class);
 
@@ -83,8 +84,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();
@@ -101,57 +103,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, @Nullable 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();
@@ -185,25 +164,25 @@ public class SubscriptionData extends CustomizableEntityBase implements Subscrip
 
 
     @Override
-    public void cancel(DateTime requestedDate, boolean eot) throws EntitlementUserApiException  {
-        apiService.cancel(this, requestedDate, eot);
+    public void cancel(DateTime requestedDate, boolean eot, CallContext context) throws EntitlementUserApiException  {
+        apiService.cancel(this, requestedDate, eot, context);
     }
 
     @Override
-    public void uncancel() throws EntitlementUserApiException {
-        apiService.uncancel(this);
+    public void uncancel(CallContext context) throws EntitlementUserApiException {
+        apiService.uncancel(this, context);
     }
 
     @Override
     public void changePlan(String productName, BillingPeriod term,
-            String priceList, DateTime requestedDate) throws EntitlementUserApiException {
-        apiService.changePlan(this, productName, term, priceList, requestedDate);
+            String priceList, DateTime requestedDate, CallContext context) throws EntitlementUserApiException {
+        apiService.changePlan(this, productName, term, priceList, requestedDate, context);
     }
 
     @Override
-    public void recreate(PlanPhaseSpecifier spec, DateTime requestedDate)
+    public void recreate(PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
             throws EntitlementUserApiException {
-        apiService.recreatePlan(this, spec, requestedDate);
+        apiService.recreatePlan(this, spec, requestedDate, context);
     }
 
     public List<SubscriptionTransition> getBillingTransitions() {
@@ -311,7 +290,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/core/Engine.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
index 55ea2cf..32ffb90 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
@@ -23,6 +23,10 @@ import java.util.List;
 import java.util.UUID;
 
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextFactory;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,6 +47,7 @@ import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
 import com.ning.billing.entitlement.api.user.DefaultEntitlementUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionFactory;
 import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.engine.addon.AddonUtils;
@@ -84,7 +89,8 @@ public class Engine implements EventListener, EntitlementService {
 
     private final EntitlementConfig config;
     private final NotificationQueueService notificationQueueService;
-
+    private final CallContextFactory factory;
+    private final SubscriptionFactory subscriptionFactory;
     private NotificationQueue subscriptionEventQueue;
 
     @Inject
@@ -92,7 +98,9 @@ public class Engine implements EventListener, EntitlementService {
             EntitlementConfig config, DefaultEntitlementUserApi userApi,
             DefaultEntitlementBillingApi billingApi,
             DefaultEntitlementMigrationApi migrationApi, AddonUtils addonUtils, Bus eventBus,
-            NotificationQueueService notificationQueueService) {
+            NotificationQueueService notificationQueueService,
+            SubscriptionFactory subscriptionFactory,
+            CallContextFactory factory) {
         super();
         this.clock = clock;
         this.dao = dao;
@@ -104,6 +112,8 @@ public class Engine implements EventListener, EntitlementService {
         this.config = config;
         this.eventBus = eventBus;
         this.notificationQueueService = notificationQueueService;
+        this.subscriptionFactory = subscriptionFactory;
+        this.factory = factory;
     }
 
     @Override
@@ -124,7 +134,8 @@ public class Engine implements EventListener, EntitlementService {
                     if (event == null) {
                         log.warn("Failed to extract event for notification key {}", notificationKey);
                     } else {
-                        processEventReady(event);
+                        final CallContext context = factory.createCallContext("SubscriptionEventQueue", CallOrigin.INTERNAL, UserType.SYSTEM);
+                        processEventReady(event, context);
                     }
                 }
             },
@@ -181,11 +192,11 @@ public class Engine implements EventListener, EntitlementService {
 
 
     @Override
-    public void processEventReady(EntitlementEvent event) {
+    public void processEventReady(EntitlementEvent event, CallContext context) {
         if (!event.isActive()) {
             return;
         }
-        SubscriptionData subscription = (SubscriptionData) dao.getSubscriptionFromId(event.getSubscriptionId());
+        SubscriptionData subscription = (SubscriptionData) dao.getSubscriptionFromId(subscriptionFactory, event.getSubscriptionId());
         if (subscription == null) {
             log.warn("Failed to retrieve subscription for id %s", event.getSubscriptionId());
             return;
@@ -194,10 +205,10 @@ public class Engine implements EventListener, EntitlementService {
         // Do any internal processing on that event before we send the event to the bus
         //
         if (event.getType() == EventType.PHASE) {
-            onPhaseEvent(subscription);
+            onPhaseEvent(subscription, context);
         } else if (event.getType() == EventType.API_USER &&
                 subscription.getCategory() == ProductCategory.BASE) {
-            onBasePlanEvent(subscription, (ApiEvent) event);
+            onBasePlanEvent(subscription, (ApiEvent) event, context);
         }
         try {
             eventBus.post(subscription.getTransitionFromEvent(event));
@@ -207,7 +218,7 @@ public class Engine implements EventListener, EntitlementService {
     }
 
 
-    private void onPhaseEvent(SubscriptionData subscription) {
+    private void onPhaseEvent(SubscriptionData subscription, CallContext context) {
         try {
             DateTime now = clock.getUTCNow();
             TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, now, now);
@@ -215,21 +226,21 @@ public class Engine implements EventListener, EntitlementService {
                     PhaseEventData.createNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
                         null;
             if (nextPhaseEvent != null) {
-                dao.createNextPhaseEvent(subscription.getId(), nextPhaseEvent);
+                dao.createNextPhaseEvent(subscription.getId(), nextPhaseEvent, context);
             }
         } catch (EntitlementError e) {
             log.error(String.format("Failed to insert next phase for subscription %s", subscription.getId()), e);
         }
     }
 
-    private void onBasePlanEvent(SubscriptionData baseSubscription, ApiEvent event) {
+    private void onBasePlanEvent(SubscriptionData baseSubscription, ApiEvent event, CallContext context) {
 
         DateTime now = clock.getUTCNow();
 
         Product baseProduct = (baseSubscription.getState() == SubscriptionState.CANCELLED ) ?
                 null : baseSubscription.getCurrentPlan().getProduct();
 
-        List<Subscription> subscriptions = dao.getSubscriptions(baseSubscription.getBundleId());
+        List<Subscription> subscriptions = dao.getSubscriptions(subscriptionFactory, baseSubscription.getBundleId());
 
         Iterator<Subscription> it = subscriptions.iterator();
         while (it.hasNext()) {
@@ -252,7 +263,7 @@ public class Engine implements EventListener, EntitlementService {
                 .setEffectiveDate(event.getEffectiveDate())
                 .setRequestedDate(now)
                 .setFromDisk(true));
-                dao.cancelSubscription(cur.getId(), cancelEvent);
+                dao.cancelSubscription(cur.getId(), cancelEvent, context);
             }
         }
     }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventListener.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventListener.java
index e9962d8..f6b13f9 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventListener.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventListener.java
@@ -17,11 +17,11 @@
 package com.ning.billing.entitlement.engine.core;
 
 import com.ning.billing.entitlement.events.EntitlementEvent;
-
+import com.ning.billing.util.callcontext.CallContext;
 
 
 public interface EventListener {
 
-    public void processEventReady(EntitlementEvent event);
+    public void processEventReady(EntitlementEvent event, CallContext context);
 
 }
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..bccd559 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,11 @@ 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.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.dao.BinderBase;
+import com.ning.billing.util.dao.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 +38,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;
 
@@ -44,10 +45,8 @@ import java.util.UUID;
 public interface BundleSqlDao extends Transactional<BundleSqlDao>, CloseMe, Transmogrifier {
 
     @SqlUpdate
-    public void insertBundle(@Bind(binder = SubscriptionBundleBinder.class) SubscriptionBundleData bundle);
-
-    @SqlUpdate
-    public void removeBundle(@Bind("id") String id);
+    public void insertBundle(@Bind(binder = SubscriptionBundleBinder.class) SubscriptionBundleData bundle,
+                             @CallContextBinder final CallContext context);
 
     @SqlQuery
     @Mapper(ISubscriptionBundleSqlMapper.class)
@@ -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 b6ee805..d79444a 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,65 +19,64 @@ package com.ning.billing.entitlement.engine.dao;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
+
 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.api.user.SubscriptionFactory;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 
 public interface EntitlementDao {
-
-
     // Bundle apis
-    public List<SubscriptionBundle> getSubscriptionBundleForAccount(UUID accountId);
+    public List<SubscriptionBundle> getSubscriptionBundleForAccount(final UUID accountId);
 
-    public SubscriptionBundle getSubscriptionBundleFromKey(String bundleKey);
+    public SubscriptionBundle getSubscriptionBundleFromKey(final String bundleKey);
 
-    public SubscriptionBundle getSubscriptionBundleFromId(UUID bundleId);
+    public SubscriptionBundle getSubscriptionBundleFromId(final UUID bundleId);
 
-    public SubscriptionBundle createSubscriptionBundle(SubscriptionBundleData bundle);
+    public SubscriptionBundle createSubscriptionBundle(final SubscriptionBundleData bundle, CallContext context);
 
-    public Subscription getSubscriptionFromId(UUID subscriptionId);
+    public Subscription getSubscriptionFromId(final SubscriptionFactory factory, final UUID subscriptionId);
 
     // Account retrieval
-    public UUID getAccountIdFromSubscriptionId(UUID subscriptionId);
+    public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId);
 
     // Subscription retrieval
-    public Subscription getBaseSubscription(UUID bundleId);
+    public Subscription getBaseSubscription(final SubscriptionFactory factory, final UUID bundleId);
 
-    public List<Subscription> getSubscriptions(UUID bundleId);
+    public List<Subscription> getSubscriptions(final SubscriptionFactory factory, final UUID bundleId);
 
-    public List<Subscription> getSubscriptionsForKey(String bundleKey);
+    public List<Subscription> getSubscriptionsForKey(final SubscriptionFactory factory, final String bundleKey);
 
     // Update
-    public void updateSubscription(SubscriptionData subscription);
+    public void updateSubscription(final SubscriptionData subscription, final CallContext context);
 
     // Event apis
-    public void createNextPhaseEvent(UUID subscriptionId, EntitlementEvent nextPhase);
+    public void createNextPhaseEvent(final UUID subscriptionId, final EntitlementEvent nextPhase, final CallContext context);
 
-    public EntitlementEvent getEventById(UUID eventId);
+    public EntitlementEvent getEventById(final UUID eventId);
 
-    public List<EntitlementEvent> getEventsForSubscription(UUID subscriptionId);
+    public List<EntitlementEvent> getEventsForSubscription(final UUID subscriptionId);
 
-    public List<EntitlementEvent> getPendingEventsForSubscription(UUID subscriptionId);
+    public List<EntitlementEvent> getPendingEventsForSubscription(final UUID subscriptionId);
 
     // Subscription creation, cancellation, changePlan apis
-    public void createSubscription(SubscriptionData subscription, List<EntitlementEvent> initialEvents);
-
-    public void recreateSubscription(UUID subscriptionId, List<EntitlementEvent> recreateEvents);
+    public void createSubscription(final SubscriptionData subscription, final List<EntitlementEvent> initialEvents, final CallContext context);
 
-    public void cancelSubscription(UUID subscriptionId, EntitlementEvent cancelEvent);
+    public void recreateSubscription(final UUID subscriptionId, final List<EntitlementEvent> recreateEvents, final CallContext context);
 
-    public void uncancelSubscription(UUID subscriptionId, List<EntitlementEvent> uncancelEvents);
+    public void cancelSubscription(final UUID subscriptionId, final EntitlementEvent cancelEvent, final CallContext context);
 
-    public void changePlan(UUID subscriptionId, List<EntitlementEvent> changeEvents);
+    public void uncancelSubscription(final UUID subscriptionId, final List<EntitlementEvent> uncancelEvents, final CallContext context);
 
-    public void migrate(UUID acountId, AccountMigrationData data);
+    public void changePlan(final UUID subscriptionId, final List<EntitlementEvent> changeEvents, final CallContext context);
 
-    public void undoMigration(UUID accountId);
+    public void migrate(final UUID accountId, final AccountMigrationData data, final CallContext context);
 
     // Custom Fields
-    public void saveCustomFields(SubscriptionData subscription);
+    public void saveCustomFields(final SubscriptionData subscription, final 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 e2e9203..c907bde 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,10 @@ import java.util.Date;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.ChangeType;
+import com.ning.billing.util.audit.dao.AuditSqlDao;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.customfield.dao.CustomFieldDao;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
@@ -59,35 +63,45 @@ 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;
 import com.ning.billing.util.notificationq.NotificationQueueService.NoSuchNotificationQueue;
 
+import javax.annotation.Nullable;
 
 public class EntitlementSqlDao implements EntitlementDao {
-
     private final static Logger log = LoggerFactory.getLogger(EntitlementSqlDao.class);
+    public static final String ENTITLEMENT_EVENTS_TABLE_NAME = "entitlement_events";
+    public static final String BUNDLES_TABLE_NAME = "bundles";
+    public static final String SUBSCRIPTIONS_TABLE_NAME = "subscriptions";
 
     private final Clock clock;
     private final SubscriptionSqlDao subscriptionsDao;
     private final BundleSqlDao bundlesDao;
     private final EventSqlDao eventsDao;
-    private final SubscriptionFactory factory;
     private final NotificationQueueService notificationQueueService;
     private final AddonUtils addonUtils;
-
+    private final CustomFieldDao customFieldDao;
+    
+    //
+    // We are not injecting SubscriptionFactory since that creates circular dependencies--
+    // Guice would still work, but this is playing with fire.
+    //
+    // Instead that factory passed through API top to bottom for the call where is it needed-- where we returned fully rehydrated Subscriptions
+    //
     @Inject
-    public EntitlementSqlDao(final IDBI dbi, final Clock clock, final SubscriptionFactory factory,
-            final AddonUtils addonUtils, final NotificationQueueService notificationQueueService) {
+    public EntitlementSqlDao(final IDBI dbi, final Clock clock,
+                             final AddonUtils addonUtils, final NotificationQueueService notificationQueueService,
+                             final CustomFieldDao customFieldDao) {
         this.clock = clock;
-        this.factory = factory;
         this.subscriptionsDao = dbi.onDemand(SubscriptionSqlDao.class);
         this.eventsDao = dbi.onDemand(EventSqlDao.class);
         this.bundlesDao = dbi.onDemand(BundleSqlDao.class);
         this.notificationQueueService = notificationQueueService;
         this.addonUtils = addonUtils;
+        this.customFieldDao = customFieldDao;
     }
 
     @Override
@@ -107,17 +121,21 @@ public class EntitlementSqlDao implements EntitlementDao {
     }
 
     @Override
-    public SubscriptionBundle createSubscriptionBundle(final SubscriptionBundleData bundle) {
+    public SubscriptionBundle createSubscriptionBundle(final SubscriptionBundleData bundle, final CallContext context) {
         return bundlesDao.inTransaction(new Transaction<SubscriptionBundle, BundleSqlDao>() {
             @Override
             public SubscriptionBundle inTransaction(BundleSqlDao bundlesDao, TransactionStatus status) {
-                bundlesDao.insertBundle(bundle);
+                bundlesDao.insertBundle(bundle, context);
+
+                AuditSqlDao auditSqlDao = bundlesDao.become(AuditSqlDao.class);
+                String bundleId = bundle.getId().toString();
+                auditSqlDao.insertAuditFromTransaction(BUNDLES_TABLE_NAME, bundleId, ChangeType.INSERT, context);
+
                 return bundle;
             }
         });
     }
 
-
     @Override
     public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId) {
         Subscription subscription = subscriptionsDao.getSubscriptionFromId(subscriptionId.toString());
@@ -142,59 +160,60 @@ public class EntitlementSqlDao implements EntitlementDao {
     }
 
     @Override
-    public Subscription getBaseSubscription(final UUID bundleId) {
-        return getBaseSubscription(bundleId, true);
+    public Subscription getBaseSubscription(final SubscriptionFactory factory, final UUID bundleId) {
+        return getBaseSubscription(factory, bundleId, true);
     }
 
-
     @Override
-    public Subscription getSubscriptionFromId(final UUID subscriptionId) {
-        return buildSubscription(subscriptionsDao.getSubscriptionFromId(subscriptionId.toString()));
+    public Subscription getSubscriptionFromId(final SubscriptionFactory factory, final UUID subscriptionId) {
+        return buildSubscription(factory, subscriptionsDao.getSubscriptionFromId(subscriptionId.toString()));
     }
 
     @Override
-    public List<Subscription> getSubscriptions(UUID bundleId) {
-        return buildBundleSubscriptions(subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString()));
+    public List<Subscription> getSubscriptions(final SubscriptionFactory factory, final UUID bundleId) {
+        return buildBundleSubscriptions(factory, subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString()));
     }
 
     @Override
-    public List<Subscription> getSubscriptionsForKey(String bundleKey) {
+    public List<Subscription> getSubscriptionsForKey(final SubscriptionFactory factory, final String bundleKey) {
         SubscriptionBundle bundle =  bundlesDao.getBundleFromKey(bundleKey);
         if (bundle == null) {
             return Collections.emptyList();
         }
-        return getSubscriptions(bundle.getId());
+        return getSubscriptions(factory, bundle.getId());
     }
 
     @Override
-    public void updateSubscription(final SubscriptionData subscription) {
+    public void updateSubscription(final SubscriptionData subscription, final CallContext context) {
 
         final Date ctd = (subscription.getChargedThroughDate() != null)  ? subscription.getChargedThroughDate().toDate() : null;
         final Date ptd = (subscription.getPaidThroughDate() != null)  ? subscription.getPaidThroughDate().toDate() : null;
 
-
         subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
-
             @Override
             public Void inTransaction(SubscriptionSqlDao transactionalDao,
                     TransactionStatus status) throws Exception {
-                transactionalDao.updateSubscription(subscription.getId().toString(), subscription.getActiveVersion(), ctd, ptd);
+                transactionalDao.updateSubscription(subscription.getId().toString(), subscription.getActiveVersion(), ctd, ptd, context);
+
+                AuditSqlDao auditSqlDao = transactionalDao.become(AuditSqlDao.class);
+                String subscriptionId = subscription.getId().toString();
+                auditSqlDao.insertAuditFromTransaction(SUBSCRIPTIONS_TABLE_NAME, subscriptionId, ChangeType.UPDATE, context);
                 return null;
             }
         });
     }
 
-
-
     @Override
-    public void createNextPhaseEvent(final UUID subscriptionId, final EntitlementEvent nextPhase) {
+    public void createNextPhaseEvent(final UUID subscriptionId, final EntitlementEvent nextPhase, final CallContext context) {
         eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
-
             @Override
             public Void inTransaction(EventSqlDao dao,
                     TransactionStatus status) throws Exception {
-                cancelNextPhaseEventFromTransaction(subscriptionId, dao);
-                dao.insertEvent(nextPhase);
+                cancelNextPhaseEventFromTransaction(subscriptionId, dao, context);
+                dao.insertEvent(nextPhase, context);
+                AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+                auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, nextPhase.getId().toString(), ChangeType.INSERT, context);
+
                 recordFutureNotificationFromTransaction(dao,
                         nextPhase.getEffectiveDate(),
                         new NotificationKey() {
@@ -213,24 +232,20 @@ public class EntitlementSqlDao implements EntitlementDao {
         return eventsDao.getEventById(eventId.toString());
     }
 
-
     @Override
     public List<EntitlementEvent> getEventsForSubscription(UUID subscriptionId) {
-        List<EntitlementEvent> events = eventsDao.getEventsForSubscription(subscriptionId.toString());
-        return events;
+        return eventsDao.getEventsForSubscription(subscriptionId.toString());
     }
 
     @Override
     public List<EntitlementEvent> getPendingEventsForSubscription(UUID subscriptionId) {
         Date now = clock.getUTCNow().toDate();
-        List<EntitlementEvent> results = eventsDao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
-        return results;
+        return eventsDao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
     }
 
-
     @Override
     public void createSubscription(final SubscriptionData subscription,
-            final List<EntitlementEvent> initialEvents) {
+            final List<EntitlementEvent> initialEvents, final CallContext context) {
 
         subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
 
@@ -238,11 +253,14 @@ public class EntitlementSqlDao implements EntitlementDao {
             public Void inTransaction(SubscriptionSqlDao dao,
                     TransactionStatus status) throws Exception {
 
-                dao.insertSubscription(subscription);
+                dao.insertSubscription(subscription, context);
                 // STEPH batch as well
                 EventSqlDao eventsDaoFromSameTransaction = dao.become(EventSqlDao.class);
+                List<String> eventIds = new ArrayList<String>();
+
                 for (final EntitlementEvent cur : initialEvents) {
-                    eventsDaoFromSameTransaction.insertEvent(cur);
+                    eventsDaoFromSameTransaction.insertEvent(cur, context);
+                    eventIds.add(cur.getId().toString()); // collect ids for batch audit log insert
                     recordFutureNotificationFromTransaction(dao,
                             cur.getEffectiveDate(),
                             new NotificationKey() {
@@ -252,6 +270,9 @@ public class EntitlementSqlDao implements EntitlementDao {
                                 }
                             });
                 }
+
+                AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+                auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
                 return null;
             }
         });
@@ -259,40 +280,49 @@ public class EntitlementSqlDao implements EntitlementDao {
 
     @Override
     public void recreateSubscription(final UUID subscriptionId,
-            final List<EntitlementEvent> recreateEvents) {
+            final List<EntitlementEvent> recreateEvents, final CallContext context) {
 
         eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
             @Override
             public Void inTransaction(EventSqlDao dao,
                     TransactionStatus status) throws Exception {
 
+                List<String> eventIds = new ArrayList<String>();
                 for (final EntitlementEvent cur : recreateEvents) {
-                    dao.insertEvent(cur);
+                    dao.insertEvent(cur, context);
+                    eventIds.add(cur.getId().toString()); // gather event ids for batch audit insert
                     recordFutureNotificationFromTransaction(dao,
                             cur.getEffectiveDate(),
                             new NotificationKey() {
-                        @Override
-                        public String toString() {
-                            return cur.getId().toString();
-                        }
-                    });
+                                @Override
+                                public String toString() {
+                                    return cur.getId().toString();
+                                }
+                            });
                 }
+
+                AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+                auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
                 return null;
             }
         });
     }
 
     @Override
-    public void cancelSubscription(final UUID subscriptionId, final EntitlementEvent cancelEvent) {
+    public void cancelSubscription(final UUID subscriptionId, final EntitlementEvent cancelEvent, final CallContext context) {
 
         eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
             @Override
             public Void inTransaction(EventSqlDao dao,
                     TransactionStatus status) throws Exception {
-                cancelNextCancelEventFromTransaction(subscriptionId, dao);
-                cancelNextChangeEventFromTransaction(subscriptionId, dao);
-                cancelNextPhaseEventFromTransaction(subscriptionId, dao);
-                dao.insertEvent(cancelEvent);
+                cancelNextCancelEventFromTransaction(subscriptionId, dao, context);
+                cancelNextChangeEventFromTransaction(subscriptionId, dao, context);
+                cancelNextPhaseEventFromTransaction(subscriptionId, dao, context);
+                dao.insertEvent(cancelEvent, context);
+                AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+                String cancelEventId = cancelEvent.getId().toString();
+                auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, cancelEventId, ChangeType.INSERT, context);
+
                 recordFutureNotificationFromTransaction(dao,
                         cancelEvent.getEffectiveDate(),
                         new NotificationKey() {
@@ -307,7 +337,7 @@ public class EntitlementSqlDao implements EntitlementDao {
     }
 
     @Override
-    public void uncancelSubscription(final UUID subscriptionId, final List<EntitlementEvent> uncancelEvents) {
+    public void uncancelSubscription(final UUID subscriptionId, final List<EntitlementEvent> uncancelEvents, final CallContext context) {
 
         eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
 
@@ -329,18 +359,26 @@ public class EntitlementSqlDao implements EntitlementDao {
                 }
 
                 if (existingCancelId != null) {
-                    dao.unactiveEvent(existingCancelId.toString(), now);
+                    dao.unactiveEvent(existingCancelId.toString(), context);
+                    String deactivatedEventId = existingCancelId.toString();
+
+                    List<String> eventIds = new ArrayList<String>();
                     for (final EntitlementEvent cur : uncancelEvents) {
-                        dao.insertEvent(cur);
+                        dao.insertEvent(cur, context);
+                        eventIds.add(cur.getId().toString()); // gather event ids for batch insert into audit log
                         recordFutureNotificationFromTransaction(dao,
                                 cur.getEffectiveDate(),
                                 new NotificationKey() {
-                            @Override
-                            public String toString() {
-                                return cur.getId().toString();
-                            }
-                        });
+                                    @Override
+                                    public String toString() {
+                                        return cur.getId().toString();
+                                    }
+                                });
                     }
+
+                    AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+                    auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, deactivatedEventId, ChangeType.UPDATE, context);
+                    auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
                 }
                 return null;
             }
@@ -348,42 +386,50 @@ public class EntitlementSqlDao implements EntitlementDao {
     }
 
     @Override
-    public void changePlan(final UUID subscriptionId, final List<EntitlementEvent> changeEvents) {
+    public void changePlan(final UUID subscriptionId, final List<EntitlementEvent> changeEvents, final CallContext context) {
         eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
             @Override
-            public Void inTransaction(EventSqlDao dao,
-                    TransactionStatus status) throws Exception {
-                cancelNextChangeEventFromTransaction(subscriptionId, dao);
-                cancelNextPhaseEventFromTransaction(subscriptionId, dao);
+            public Void inTransaction(EventSqlDao dao, TransactionStatus status) throws Exception {
+                cancelNextChangeEventFromTransaction(subscriptionId, dao, context);
+                cancelNextPhaseEventFromTransaction(subscriptionId, dao, context);
+
+                List<String> eventIds = new ArrayList<String>();
                 for (final EntitlementEvent cur : changeEvents) {
-                    dao.insertEvent(cur);
+                    dao.insertEvent(cur, context);
+                    eventIds.add(cur.getId().toString()); // gather event ids for batch audit log insert
+
                     recordFutureNotificationFromTransaction(dao,
                             cur.getEffectiveDate(),
                             new NotificationKey() {
-                        @Override
-                        public String toString() {
-                            return cur.getId().toString();
-                        }
-                    });
+                                @Override
+                                public String toString() {
+                                    return cur.getId().toString();
+                                }
+                            });
                 }
+
+                AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+                auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
                 return null;
             }
         });
     }
 
-    private void cancelNextPhaseEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao) {
-        cancelFutureEventFromTransaction(subscriptionId, dao, EventType.PHASE, null);
+    private void cancelNextPhaseEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao, final CallContext context) {
+        cancelFutureEventFromTransaction(subscriptionId, dao, EventType.PHASE, null, context);
     }
 
-    private void cancelNextChangeEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao) {
-        cancelFutureEventFromTransaction(subscriptionId, dao, EventType.API_USER, ApiEventType.CHANGE);
+    private void cancelNextChangeEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao, final CallContext context) {
+        cancelFutureEventFromTransaction(subscriptionId, dao, EventType.API_USER, ApiEventType.CHANGE, context);
     }
 
-    private void cancelNextCancelEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao) {
-        cancelFutureEventFromTransaction(subscriptionId, dao, EventType.API_USER, ApiEventType.CANCEL);
+    private void cancelNextCancelEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao, final CallContext context) {
+        cancelFutureEventFromTransaction(subscriptionId, dao, EventType.API_USER, ApiEventType.CANCEL, context);
     }
 
-    private void cancelFutureEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao, EventType type, ApiEventType apiType) {
+    private void cancelFutureEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao,
+                                                  final EventType type, @Nullable final ApiEventType apiType,
+                                                  final CallContext context) {
 
         UUID futureEventId = null;
         Date now = clock.getUTCNow().toDate();
@@ -401,38 +447,32 @@ public class EntitlementSqlDao implements EntitlementDao {
         }
 
         if (futureEventId != null) {
-            dao.unactiveEvent(futureEventId.toString(), now);
+            dao.unactiveEvent(futureEventId.toString(), context);
+
+            AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+            auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, futureEventId.toString(), ChangeType.UPDATE, context);
         }
     }
 
-    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) {
+        customFieldDao.saveFields(transactionalDao, subscription.getId(), subscription.getObjectName(), subscription.getFieldList(), context);
     }
 
-    private Subscription buildSubscription(Subscription input) {
+    private Subscription buildSubscription(final SubscriptionFactory factory, final Subscription input) {
         if (input == null) {
             return null;
         }
         List<Subscription> bundleInput = new ArrayList<Subscription>();
-        Subscription baseSubscription = null;
         if (input.getCategory() == ProductCategory.ADD_ON) {
-            baseSubscription = getBaseSubscription(input.getBundleId(), false);
+            Subscription baseSubscription = getBaseSubscription(factory, input.getBundleId(), false);
             bundleInput.add(baseSubscription);
             bundleInput.add(input);
         } else {
             bundleInput.add(input);
         }
-        List<Subscription> reloadedSubscriptions = buildBundleSubscriptions(bundleInput);
+        List<Subscription> reloadedSubscriptions = buildBundleSubscriptions(factory, bundleInput);
         for (Subscription cur : reloadedSubscriptions) {
             if (cur.getId().equals(input.getId())) {
                 return cur;
@@ -441,10 +481,7 @@ public class EntitlementSqlDao implements EntitlementDao {
          throw new EntitlementError(String.format("Unexpected code path in buildSubscription"));
     }
 
-
-
-    private List<Subscription> buildBundleSubscriptions(List<Subscription> input) {
-
+    private List<Subscription> buildBundleSubscriptions(final SubscriptionFactory factory, final List<Subscription> input) {
         // Make sure BasePlan -- if exists-- is first
         Collections.sort(input, new Comparator<Subscription>() {
             @Override
@@ -515,7 +552,7 @@ public class EntitlementSqlDao implements EntitlementDao {
     }
 
     @Override
-    public void migrate(final UUID accountId, final AccountMigrationData accountData) {
+    public void migrate(final UUID accountId, final AccountMigrationData accountData, final CallContext context) {
 
         eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
 
@@ -526,15 +563,20 @@ public class EntitlementSqlDao implements EntitlementDao {
                 SubscriptionSqlDao transSubDao = transEventDao.become(SubscriptionSqlDao.class);
                 BundleSqlDao transBundleDao = transEventDao.become(BundleSqlDao.class);
 
-                // First get rid of any data from account
-                undoMigrationFromTransaction(accountId, transEventDao, transBundleDao, transSubDao);
+                List<String> bundleIds = new ArrayList<String>();
+                List<String> subscriptionIds = new ArrayList<String>();
+                List<String> eventIds = new ArrayList<String>();
 
                 for (BundleMigrationData curBundle : accountData.getData()) {
                     SubscriptionBundleData bundleData = curBundle.getData();
+
                     for (SubscriptionMigrationData curSubscription : curBundle.getSubscriptions()) {
+
                         SubscriptionData subData = curSubscription.getData();
                         for (final EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
-                            transEventDao.insertEvent(curEvent);
+                            transEventDao.insertEvent(curEvent, context);
+                            eventIds.add(curEvent.getId().toString()); // gather event ids for batch audit
+
                             recordFutureNotificationFromTransaction(transEventDao,
                                     curEvent.getEffectiveDate(),
                                     new NotificationKey() {
@@ -544,55 +586,35 @@ public class EntitlementSqlDao implements EntitlementDao {
                                 }
                             });
                         }
-                        transSubDao.insertSubscription(subData);
+                        transSubDao.insertSubscription(subData, context);
+                        subscriptionIds.add(subData.getId().toString()); // gather subscription ids for batch audit
                     }
-                    transBundleDao.insertBundle(bundleData);
+                    transBundleDao.insertBundle(bundleData, context);
+                    bundleIds.add(bundleData.getId().toString()); // gather bundle ids for batch audit
                 }
+
+                // add audit records for bundles, subscriptions, and events
+                AuditSqlDao auditSqlDao = transBundleDao.become(AuditSqlDao.class);
+                auditSqlDao.insertAuditFromTransaction(SUBSCRIPTIONS_TABLE_NAME, subscriptionIds, ChangeType.INSERT, context);
+                auditSqlDao.insertAuditFromTransaction(BUNDLES_TABLE_NAME, bundleIds, ChangeType.INSERT, context);
+                auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
+
                 return null;
             }
         });
     }
 
 
-    public Subscription getBaseSubscription(final UUID bundleId, boolean rebuildSubscription) {
+    private Subscription getBaseSubscription(final SubscriptionFactory factory, final UUID bundleId, boolean rebuildSubscription) {
         List<Subscription> subscriptions = subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString());
         for (Subscription cur : subscriptions) {
-            if (((SubscriptionData)cur).getCategory() == ProductCategory.BASE) {
-                return  rebuildSubscription ? buildSubscription(cur) : cur;
+            if (cur.getCategory() == ProductCategory.BASE) {
+                return  rebuildSubscription ? buildSubscription(factory, cur) : cur;
             }
         }
         return null;
     }
 
-    @Override
-    public void undoMigration(final UUID accountId) {
-
-        eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
-
-            @Override
-            public Void inTransaction(EventSqlDao transEventDao,
-                    TransactionStatus status) throws Exception {
-
-                SubscriptionSqlDao transSubDao = transEventDao.become(SubscriptionSqlDao.class);
-                BundleSqlDao transBundleDao = transEventDao.become(BundleSqlDao.class);
-                undoMigrationFromTransaction(accountId, transEventDao, transBundleDao, transSubDao);
-                return null;
-            }
-        });
-    }
-
-    private void undoMigrationFromTransaction(final UUID accountId, EventSqlDao transEventDao, BundleSqlDao transBundleDao, SubscriptionSqlDao transSubDao) {
-        final List<SubscriptionBundle> bundles = transBundleDao.getBundleFromAccount(accountId.toString());
-        for (SubscriptionBundle curBundle : bundles) {
-            List<Subscription> subscriptions = transSubDao.getSubscriptionsFromBundleId(curBundle.getId().toString());
-            for (Subscription cur : subscriptions) {
-                eventsDao.removeEvents(cur.getId().toString());
-                transSubDao.removeSubscription(cur.getId().toString());
-            }
-            transBundleDao.removeBundle(curBundle.getId().toString());
-        }
-    }
-
     private void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao, final DateTime effectiveDate, final NotificationKey notificationKey) {
         try {
             NotificationQueue subscriptionEventQueue = notificationQueueService.getNotificationQueue(Engine.ENTITLEMENT_SERVICE_NAME,
@@ -604,23 +626,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 a931e9a..0c2ba04 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,11 @@ 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.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.dao.BinderBase;
+import com.ning.billing.util.dao.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 +43,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;
@@ -53,16 +55,16 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
     public EntitlementEvent getEventById(@Bind("event_id") String eventId);
 
     @SqlUpdate
-    public void insertEvent(@Bind(binder = EventSqlDaoBinder.class) EntitlementEvent evt);
+    public void insertEvent(@Bind(binder = EventSqlDaoBinder.class) EntitlementEvent evt,
+                            @CallContextBinder final CallContext context);
 
     @SqlUpdate
-    public void removeEvents(@Bind("subscription_id") String subscriptionId);
+    public void unactiveEvent(@Bind("event_id")String eventId,
+                              @CallContextBinder final CallContext context);
 
     @SqlUpdate
-    public void unactiveEvent(@Bind("event_id")String eventId, @Bind("now") Date now);
-
-    @SqlUpdate
-    public void reactiveEvent(@Bind("event_id")String eventId, @Bind("now") Date now);
+    public void reactiveEvent(@Bind("event_id")String eventId,
+                              @CallContextBinder final CallContext context);
 
     @SqlQuery
     @Mapper(EventSqlMapper.class)
@@ -72,19 +74,12 @@ 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());
             stmt.bind("event_type", evt.getType().toString());
             stmt.bind("user_type", (evt.getType() == EventType.API_USER) ? ((ApiEvent) evt).getEventType().toString() : null);
-            stmt.bind("created_dt", getDate(evt.getProcessedDate()));
-            stmt.bind("updated_dt", getDate(evt.getProcessedDate()));
             stmt.bind("requested_dt", getDate(evt.getRequestedDate()));
             stmt.bind("effective_dt", getDate(evt.getEffectiveDate()));
             stmt.bind("subscription_id", evt.getSubscriptionId().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 {
@@ -111,7 +100,7 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
             UUID id = UUID.fromString(r.getString("event_id"));
             EventType eventType = EventType.valueOf(r.getString("event_type"));
             ApiEventType userType = (eventType == EventType.API_USER) ? ApiEventType.valueOf(r.getString("user_type")) : null;
-            DateTime createdDate = getDate(r, "created_dt");
+            DateTime createdDate = getDate(r, "created_date");
             DateTime requestedDate = getDate(r, "requested_dt");
             DateTime effectiveDate = getDate(r, "effective_dt");
             UUID subscriptionId = UUID.fromString(r.getString("subscription_id"));
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 59fca2c..04f2965 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,8 +20,11 @@ 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.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.dao.BinderBase;
+import com.ning.billing.util.dao.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;
@@ -37,39 +40,30 @@ 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;
 
-
 @ExternalizedSqlViaStringTemplate3()
 public interface SubscriptionSqlDao extends Transactional<SubscriptionSqlDao>, CloseMe, Transmogrifier {
-
-
 	@SqlUpdate
-    public void insertSubscription(@Bind(binder = ISubscriptionDaoBinder.class) SubscriptionData sub);
-
-    @SqlUpdate
-    public void removeSubscription(@Bind("id") String id);
+    public void insertSubscription(@Bind(binder = SubscriptionBinder.class) SubscriptionData sub,
+                                   @CallContextBinder final CallContext context);
 
     @SqlQuery
-    @Mapper(ISubscriptionDaoSqlMapper.class)
+    @Mapper(SubscriptionMapper.class)
     public Subscription getSubscriptionFromId(@Bind("id") String id);
 
     @SqlQuery
-    @Mapper(ISubscriptionDaoSqlMapper.class)
+    @Mapper(SubscriptionMapper.class)
     public List<Subscription> getSubscriptionsFromBundleId(@Bind("bundle_id") String bundleId);
 
     @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 void updateSubscription(@Bind("id") String id, @Bind("active_version") long activeVersion,
+                                   @Bind("ctd_dt") Date ctd, @Bind("ptd_dt") Date ptd,
+                                   @CallContextBinder final CallContext context);
+   
+    public static class SubscriptionBinder 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 +77,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 SubscriptionMapper extends MapperBase implements ResultSetMapper<SubscriptionData> {
         @Override
         public SubscriptionData map(int arg0, ResultSet r, StatementContext ctx)
                 throws SQLException {
@@ -103,7 +91,7 @@ public interface SubscriptionSqlDao extends Transactional<SubscriptionSqlDao>, C
             DateTime ptd = getDate(r, "ptd_dt");
             long activeVersion = r.getLong("active_version");
 
-            SubscriptionData subscription = new SubscriptionData(new SubscriptionBuilder()
+            return new SubscriptionData(new SubscriptionBuilder()
             .setId(id)
             .setBundleId(bundleId)
             .setCategory(category)
@@ -112,7 +100,6 @@ public interface SubscriptionSqlDao extends Transactional<SubscriptionSqlDao>, C
             .setActiveVersion(activeVersion)
             .setChargedThroughDate(ctd)
             .setPaidThroughDate(ptd));
-            return subscription;
         }
     }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java b/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
index 704b765..e167807 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
@@ -30,27 +30,24 @@ import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
 import com.ning.billing.entitlement.api.user.DefaultEntitlementUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.SubscriptionApiService;
+import com.ning.billing.entitlement.api.user.SubscriptionFactory;
 import com.ning.billing.entitlement.engine.addon.AddonUtils;
 import com.ning.billing.entitlement.engine.core.Engine;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.engine.dao.EntitlementSqlDao;
 
-
-
 public class EntitlementModule extends AbstractModule {
-
-
     protected void installConfig() {
         final EntitlementConfig config = new ConfigurationObjectFactory(System.getProperties()).build(EntitlementConfig.class);
         bind(EntitlementConfig.class).toInstance(config);
     }
 
-
     protected void installEntitlementDao() {
         bind(EntitlementDao.class).to(EntitlementSqlDao.class).asEagerSingleton();
     }
 
     protected void installEntitlementCore() {
+    	bind(SubscriptionFactory.class).asEagerSingleton();
         bind(SubscriptionApiService.class).asEagerSingleton();
         bind(EntitlementService.class).to(Engine.class).asEagerSingleton();
         bind(Engine.class).asEagerSingleton();
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql b/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
index c39d001..bef27aa 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
@@ -5,8 +5,6 @@ CREATE TABLE entitlement_events (
     event_id char(36) NOT NULL,
     event_type varchar(9) NOT NULL,
     user_type varchar(25) DEFAULT NULL,
-    created_dt datetime NOT NULL,
-    updated_dt datetime NOT NULL,
     requested_dt datetime NOT NULL,
     effective_dt datetime NOT NULL,
     subscription_id char(36) NOT NULL,
@@ -15,11 +13,14 @@ CREATE TABLE entitlement_events (
     plist_name varchar(64) DEFAULT NULL,
     current_version int(11) DEFAULT 1,
     is_active bool DEFAULT 1,
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
+    updated_by varchar(50) NOT NULL,
+    updated_date datetime NOT NULL,
     PRIMARY KEY(id)
 ) ENGINE=innodb;
 CREATE INDEX idx_ent_1 ON entitlement_events(subscription_id,is_active,effective_dt);
-CREATE INDEX idx_ent_2 ON entitlement_events(subscription_id,effective_dt,created_dt,requested_dt,id);
-
+CREATE INDEX idx_ent_2 ON entitlement_events(subscription_id,effective_dt,created_date,requested_dt,id);
 
 DROP TABLE IF EXISTS subscriptions;
 CREATE TABLE subscriptions (
@@ -31,6 +32,10 @@ CREATE TABLE subscriptions (
     active_version int(11) DEFAULT 1,
     ctd_dt datetime DEFAULT NULL,
     ptd_dt datetime DEFAULT NULL,
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
+    updated_by varchar(50) NOT NULL,
+    updated_date datetime NOT NULL,
     PRIMARY KEY(id)
 ) ENGINE=innodb;
 
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
index d9f26c2..4f0e4db 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
@@ -14,13 +14,6 @@ insertBundle() ::= <<
     );
 >>
 
-removeBundle(id) ::= <<
-    delete from bundles
-    where
-      id = :id
-    ;
->>
-
 getBundleFromId(id) ::= <<
     select
       id
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
index b639dce..9179943 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
@@ -1,21 +1,25 @@
 group EventSqlDao;
 
-getEventById(event_id) ::= <<
-  select
-      id
-      , event_id
-      , event_type
-      , user_type
-      , created_dt
-      , updated_dt
-      , requested_dt
-      , effective_dt
-      , subscription_id
-      , plan_name
-      , phase_name
-      , plist_name
-      , current_version
-      , is_active  
+fields(prefix) ::= <<
+    <prefix>event_id,
+    <prefix>event_type,
+    <prefix>user_type,
+    <prefix>requested_dt,
+    <prefix>effective_dt,
+    <prefix>subscription_id,
+    <prefix>plan_name,
+    <prefix>phase_name,
+    <prefix>plist_name,
+    <prefix>current_version,
+    <prefix>is_active,
+    <prefix>created_by,
+    <prefix>created_date,
+    <prefix>updated_by,
+    <prefix>updated_date
+>>
+
+getEventById() ::= <<
+  select id, <fields()>
   from entitlement_events
   where
       event_id = :event_id
@@ -23,26 +27,11 @@ getEventById(event_id) ::= <<
 >>
 
 insertEvent() ::= <<
-    insert into entitlement_events (
-      event_id
-      , event_type
-      , user_type
-      , created_dt
-      , updated_dt
-      , requested_dt
-      , effective_dt
-      , subscription_id
-      , plan_name
-      , phase_name
-      , plist_name
-      , current_version
-      , is_active
-    ) values (
+    insert into entitlement_events (<fields()>)
+    values (
       :event_id
       , :event_type
       , :user_type
-      , :created_dt
-      , :updated_dt
       , :requested_dt
       , :effective_dt
       , :subscription_id
@@ -51,52 +40,37 @@ insertEvent() ::= <<
       , :plist_name
       , :current_version
       , :is_active
+      , :userName
+      , :createdDate
+      , :userName
+      , :updatedDate
     );   
 >>
 
-removeEvents(subscription_id) ::= <<
-    delete from entitlement_events
-      where
-    subscription_id = :subscription_id
-    ;
->>
-
-unactiveEvent(event_id, now) ::= <<
+unactiveEvent() ::= <<
     update entitlement_events
     set
       is_active = 0
-      , updated_dt = :now
+      , updated_by = :userName
+      , updated_date = :updatedDate
     where
       event_id = :event_id
     ;
 >>
 
-reactiveEvent(event_id, now) ::= <<
+reactiveEvent() ::= <<
     update entitlement_events
     set
       is_active = 1
-      , updated_dt = :now
+      , updated_by = :userName
+      , updated_date = :updatedDate
     where
       event_id = :event_id
     ;
 >>
 
-getFutureActiveEventForSubscription(subscription_id, now) ::= <<
-    select 
-      id
-      , event_id
-      , event_type
-      , user_type
-      , created_dt
-      , updated_dt
-      , requested_dt
-      , effective_dt
-      , subscription_id
-      , plan_name
-      , phase_name
-      , plist_name
-      , current_version
-      , is_active
+getFutureActiveEventForSubscription() ::= <<
+    select id, <fields()>
     from entitlement_events
     where
       subscription_id = :subscription_id
@@ -104,34 +78,20 @@ getFutureActiveEventForSubscription(subscription_id, now) ::= <<
       and effective_dt > :now
     order by
       effective_dt asc
-      , created_dt asc
+      , created_date asc
       , requested_dt asc
       , id asc
     ;
 >> 
 
 getEventsForSubscription(subscription_id) ::= <<
-    select
-       id
-      , event_id
-      , event_type
-      , user_type
-      , created_dt
-      , updated_dt
-      , requested_dt
-      , effective_dt
-      , subscription_id
-      , plan_name
-      , phase_name
-      , plist_name
-      , current_version
-      , is_active
+    select id, <fields()>
     from entitlement_events
     where
       subscription_id = :subscription_id
     order by
       effective_dt asc
-      , created_dt asc
+      , created_date asc
       , requested_dt asc
       , id asc
     ;      
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
index bb7400b..780c06a 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
@@ -10,6 +10,10 @@ insertSubscription() ::= <<
       , active_version
       , ctd_dt
       , ptd_dt
+      , created_by
+      , created_date
+      , updated_by
+      , updated_date
     ) values (
         :id
       , :bundle_id
@@ -18,16 +22,14 @@ insertSubscription() ::= <<
       , :bundle_start_dt
       , :active_version
       , :ctd_dt
-      , :ptd_dt 
+      , :ptd_dt
+      , :userName
+      , :createdDate
+      , :userName
+      , :updatedDate
     );
 >>
 
-removeSubscription(id) ::= <<
-    delete from subscriptions
-     where id = :id
-    ;    
->>
-
 getSubscriptionFromId(id) ::= <<
     select
         id
@@ -64,6 +66,8 @@ updateSubscription(id, active_version, ctd_dt, ptd_dt) ::= <<
       active_version = :active_version
       , ctd_dt = :ctd_dt
       , ptd_dt = :ptd_dt
+      , updated_by = :userName
+      , updated_date = :updatedDate
     where id = :id
     ;
 >>
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 080c816..e940cd8 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
@@ -25,10 +25,12 @@ import java.util.List;
 import java.util.SortedSet;
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContextFactory;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
+import com.ning.billing.util.glue.CallContextModule;
 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;
@@ -77,16 +79,17 @@ public class TestDefaultEntitlementBillingApi {
 
 	private Clock clock;
 	private SubscriptionData subscription;
+    private CallContextFactory factory;
 	private DateTime subscriptionStartDate;
 
 	@BeforeSuite(alwaysRun=true)
 	public void setup() throws ServiceException {
 		TestApiBase.loadSystemPropertiesFromClasspath("/entitlement.properties");
-        final Injector g = Guice.createInjector(Stage.PRODUCTION, new CatalogModule(), new ClockModule());
-
+        final Injector g = Guice.createInjector(Stage.PRODUCTION, new CatalogModule(), new ClockModule(), new CallContextModule());
 
         catalogService = g.getInstance(CatalogService.class);
         clock = g.getInstance(Clock.class);
+        factory = g.getInstance(CallContextFactory.class);
 
         ((DefaultCatalogService)catalogService).loadCatalog();
 	}
@@ -135,7 +138,8 @@ public class TestDefaultEntitlementBillingApi {
 		AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
         ((ZombieControl) accountApi).addResult("getAccountById", account);
 
-		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao, accountApi, catalogService);
+        CallContextFactory factory = new DefaultCallContextFactory(clock);
+		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(factory, null, dao, accountApi, catalogService);
 		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
 		Assert.assertEquals(events.size(), 0);
 	}
@@ -157,7 +161,7 @@ public class TestDefaultEntitlementBillingApi {
         ((ZombieControl)account).addResult("getCurrency", Currency.USD);
         ((ZombieControl)accountApi).addResult("getAccountById", account);
 		       
-		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
+		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(factory, null, dao, accountApi, catalogService);
 		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
 		checkFirstEvent(events, nextPlan, 32, oneId, now, nextPhase, ApiEventType.CREATE.toString());
 	}
@@ -181,7 +185,8 @@ public class TestDefaultEntitlementBillingApi {
 		AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
 		((ZombieControl)accountApi).addResult("getAccountById", account);
 
-		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
+        CallContextFactory factory = new DefaultCallContextFactory(clock);
+		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(factory, null, dao, accountApi, catalogService);
 		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
 		checkFirstEvent(events, nextPlan, subscription.getStartDate().getDayOfMonth(), oneId, now, nextPhase, ApiEventType.CREATE.toString());
 	}
@@ -203,7 +208,7 @@ public class TestDefaultEntitlementBillingApi {
         ((ZombieControl)account).addResult("getCurrency", Currency.USD);
         ((ZombieControl)accountApi).addResult("getAccountById", account);
 
-        DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
+        DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(factory, null, dao,accountApi,catalogService);
 		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
 		checkFirstEvent(events, nextPlan, 32, oneId, now, nextPhase, ApiEventType.CREATE.toString());
 	}
@@ -226,7 +231,8 @@ public class TestDefaultEntitlementBillingApi {
 		AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
 		((ZombieControl)accountApi).addResult("getAccountById", account);
 
-		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
+        CallContextFactory factory = new DefaultCallContextFactory(clock);
+		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(factory, null, 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/migration/TestMigration.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
index 4868bf7..1d679a9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
@@ -29,7 +29,6 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.testng.Assert;
 
-import com.google.common.collect.Lists;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Duration;
 import com.ning.billing.catalog.api.PhaseType;
@@ -45,10 +44,10 @@ import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.Entitl
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import org.testng.annotations.Test;
 
+@Test(groups = {"slow"})
 public abstract class TestMigration extends TestApiBase {
-
-
     public void testSingleBasePlan() {
 
         try {
@@ -58,7 +57,7 @@ public abstract class TestMigration extends TestApiBase {
             DateTime afterMigration = clock.getUTCNow();
 
             testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
-            migrationApi.migrate(toBeMigrated);
+            migrationApi.migrate(toBeMigrated, context);
             assertTrue(testListener.isCompleted(5000));
 
             List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
@@ -80,7 +79,6 @@ public abstract class TestMigration extends TestApiBase {
         }
     }
 
-
     public void testPlanWithAddOn() {
         try {
             DateTime beforeMigration = clock.getUTCNow();
@@ -91,7 +89,7 @@ public abstract class TestMigration extends TestApiBase {
 
             testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
             testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
-            migrationApi.migrate(toBeMigrated);
+            migrationApi.migrate(toBeMigrated, context);
             assertTrue(testListener.isCompleted(5000));
 
             List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
@@ -126,7 +124,6 @@ public abstract class TestMigration extends TestApiBase {
         }
     }
 
-
     public void testSingleBasePlanFutureCancelled() {
 
         try {
@@ -137,7 +134,7 @@ public abstract class TestMigration extends TestApiBase {
             DateTime afterMigration = clock.getUTCNow();
 
             testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
-            migrationApi.migrate(toBeMigrated);
+            migrationApi.migrate(toBeMigrated, context);
             assertTrue(testListener.isCompleted(5000));
 
             List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
@@ -177,13 +174,11 @@ public abstract class TestMigration extends TestApiBase {
     public void testSingleBasePlanWithPendingPhase() {
 
         try {
-            DateTime beforeMigration = clock.getUTCNow();
             final DateTime trialDate = clock.getUTCNow().minusDays(10);
             EntitlementAccountMigration toBeMigrated = createAccountFuturePendingPhase(trialDate);
-            DateTime afterMigration = clock.getUTCNow();
 
             testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
-            migrationApi.migrate(toBeMigrated);
+            migrationApi.migrate(toBeMigrated, context);
             assertTrue(testListener.isCompleted(5000));
 
             List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
@@ -221,7 +216,6 @@ public abstract class TestMigration extends TestApiBase {
         }
     }
 
-
     public void testSingleBasePlanWithPendingChange() {
 
         try {
@@ -230,7 +224,7 @@ public abstract class TestMigration extends TestApiBase {
             DateTime afterMigration = clock.getUTCNow();
 
             testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
-            migrationApi.migrate(toBeMigrated);
+            migrationApi.migrate(toBeMigrated, context);
             assertTrue(testListener.isCompleted(5000));
 
             List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
@@ -292,7 +286,7 @@ public abstract class TestMigration extends TestApiBase {
                                 }
                                 @Override
                                 public ProductCategory getCategory() {
-                                    return curCases.get(0).getPlanPhaseSpecifer().getProductCategory();
+                                    return curCases.get(0).getPlanPhaseSpecifier().getProductCategory();
                                 }
                                 @Override
                                 public DateTime getChargedThroughDate() {
@@ -428,7 +422,7 @@ public abstract class TestMigration extends TestApiBase {
         }
 
         @Override
-        public PlanPhaseSpecifier getPlanPhaseSpecifer() {
+        public PlanPhaseSpecifier getPlanPhaseSpecifier() {
             return pps;
         }
 
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 6a5520d..8379269 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
@@ -26,6 +26,8 @@ import java.net.URL;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TestCallContext;
 import org.apache.commons.io.IOUtils;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
@@ -75,9 +77,10 @@ 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 {
 
+public abstract class TestApiBase {
     protected static final Logger log = LoggerFactory.getLogger(TestApiBase.class);
 
     protected static final long DAY_IN_MS = (24 * 3600 * 1000);
@@ -100,6 +103,7 @@ public abstract class TestApiBase {
     protected SubscriptionBundle bundle;
 
     private MysqlTestingHelper helper;
+    protected CallContext context = new TestCallContext("Api Test");
 
     public static void loadSystemPropertiesFromClasspath(final String resource) {
         final URL url = TestApiBase.class.getResource(resource);
@@ -186,14 +190,17 @@ public abstract class TestApiBase {
 
         log.warn("RESET TEST FRAMEWORK\n\n");
 
-        testListener.reset();
+        if (testListener != null) {
+            testListener.reset();
+        }
 
         clock.resetDeltaFromReality();
         ((MockEntitlementDao) dao).reset();
+
         try {
             busService.getBus().register(testListener);
             UUID accountId = UUID.randomUUID();
-            bundle = entitlementApi.createBundleForAccount(accountId, "myDefaultBundle");
+            bundle = entitlementApi.createBundleForAccount(accountId, "myDefaultBundle", context);
         } catch (Exception e) {
             Assert.fail(e.getMessage());
         }
@@ -217,12 +224,12 @@ public abstract class TestApiBase {
         return createSubscriptionWithBundle(bundle.getId(), productName, term, planSet);
     }
 
-
     protected SubscriptionData createSubscriptionWithBundle(final UUID bundleId, final String productName, final BillingPeriod term, final String planSet) throws EntitlementUserApiException {
         testListener.pushExpectedEvent(NextEvent.CREATE);
+
         SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundleId,
                 new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSet, null),
-                clock.getUTCNow());
+                clock.getUTCNow(), context);
         assertNotNull(subscription);
         assertTrue(testListener.isCompleted(5000));
         return subscription;
@@ -408,7 +415,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/TestUserApiAddOn.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
index 7db938e..927ea08 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
@@ -41,7 +41,6 @@ import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.TestApiBase;
 import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
 import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
-import com.ning.billing.entitlement.api.user.SubscriptionTransition.SubscriptionTransitionType;
 import com.ning.billing.entitlement.glue.MockEngineModuleSql;
 import com.ning.billing.util.clock.DefaultClock;
 
@@ -52,7 +51,6 @@ public class TestUserApiAddOn extends TestApiBase {
         return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
     }
 
-
     @Test(enabled=true, groups={"slow"})
     public void testCreateCancelAddon() {
 
@@ -61,7 +59,7 @@ public class TestUserApiAddOn extends TestApiBase {
             BillingPeriod baseTerm = BillingPeriod.MONTHLY;
             String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
 
-            SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+            createSubscription(baseProduct, baseTerm, basePriceList);
 
             String aoProduct = "Telescopic-Scope";
             BillingPeriod aoTerm = BillingPeriod.MONTHLY;
@@ -71,7 +69,7 @@ public class TestUserApiAddOn extends TestApiBase {
             assertEquals(aoSubscription.getState(), SubscriptionState.ACTIVE);
 
             DateTime now = clock.getUTCNow();
-            aoSubscription.cancel(now, false);
+            aoSubscription.cancel(now, false, context);
 
             testListener.reset();
             testListener.pushExpectedEvent(NextEvent.CANCEL);
@@ -114,11 +112,11 @@ public class TestUserApiAddOn extends TestApiBase {
             DateTime now = clock.getUTCNow();
             Duration ctd = getDurationMonth(1);
             DateTime newChargedThroughDate = DefaultClock.addDuration(now, ctd);
-            billingApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate);
+            billingApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate, context);
             baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
 
             // FUTURE CANCELLATION
-            baseSubscription.cancel(now, false);
+            baseSubscription.cancel(now, false, context);
 
             // REFETCH AO SUBSCRIPTION AND CHECK THIS IS ACTIVE
             aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
@@ -131,7 +129,6 @@ public class TestUserApiAddOn extends TestApiBase {
             testListener.pushExpectedEvent(NextEvent.CANCEL);
             testListener.pushExpectedEvent(NextEvent.CANCEL);
             clock.addDeltaFromReality(ctd);
-            now = clock.getUTCNow();
             assertTrue(testListener.isCompleted(5000));
 
             // REFETCH AO SUBSCRIPTION AND CHECK THIS IS CANCELLED
@@ -174,7 +171,7 @@ public class TestUserApiAddOn extends TestApiBase {
             DateTime now = clock.getUTCNow();
             Duration ctd = getDurationMonth(1);
             DateTime newChargedThroughDate = DefaultClock.addDuration(now, ctd);
-            billingApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate);
+            billingApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate, context);
             baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
 
             // CHANGE IMMEDIATELY WITH TO BP WITH NON INCLUDED ADDON
@@ -185,7 +182,7 @@ public class TestUserApiAddOn extends TestApiBase {
             testListener.reset();
             testListener.pushExpectedEvent(NextEvent.CHANGE);
             testListener.pushExpectedEvent(NextEvent.CANCEL);
-            baseSubscription.changePlan(newBaseProduct, newBaseTerm, newBasePriceList, now);
+            baseSubscription.changePlan(newBaseProduct, newBaseTerm, newBasePriceList, now, context);
             assertTrue(testListener.isCompleted(5000));
 
             // REFETCH AO SUBSCRIPTION AND CHECK THIS CANCELLED
@@ -227,7 +224,7 @@ public class TestUserApiAddOn extends TestApiBase {
             DateTime now = clock.getUTCNow();
             Duration ctd = getDurationMonth(1);
             DateTime newChargedThroughDate = DefaultClock.addDuration(now, ctd);
-            billingApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate);
+            billingApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate, context);
             baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
 
             // CHANGE IMMEDIATELY WITH TO BP WITH NON AVAILABLE ADDON
@@ -235,7 +232,7 @@ public class TestUserApiAddOn extends TestApiBase {
             BillingPeriod newBaseTerm = BillingPeriod.MONTHLY;
             String newBasePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
 
-            baseSubscription.changePlan(newBaseProduct, newBaseTerm, newBasePriceList, now);
+            baseSubscription.changePlan(newBaseProduct, newBaseTerm, newBasePriceList, now, context);
 
 
             // REFETCH AO SUBSCRIPTION AND CHECK THIS IS ACTIVE
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java
index 88c3825..55ad5e8 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java
@@ -64,7 +64,7 @@ public abstract class TestUserApiCancel extends TestApiBase {
             testListener.pushExpectedEvent(NextEvent.CANCEL);
 
             // CANCEL in trial period to get IMM policy
-            subscription.cancel(clock.getUTCNow(), false);
+            subscription.cancel(clock.getUTCNow(), false, context);
             currentPhase = subscription.getCurrentPhase();
             testListener.isCompleted(1000);
 
@@ -105,13 +105,13 @@ public abstract class TestUserApiCancel extends TestApiBase {
             // SET CTD + RE READ SUBSCRIPTION + CHANGE PLAN
             Duration ctd = getDurationMonth(1);
             DateTime newChargedThroughDate = DefaultClock.addDuration(expectedPhaseTrialChange, ctd);
-            billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate);
+            billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, context);
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
 
             testListener.pushExpectedEvent(NextEvent.CANCEL);
 
             // CANCEL
-            subscription.cancel(clock.getUTCNow(), false);
+            subscription.cancel(clock.getUTCNow(), false, context);
             assertFalse(testListener.isCompleted(2000));
 
             // MOVE TO EOT + RECHECK
@@ -158,7 +158,7 @@ public abstract class TestUserApiCancel extends TestApiBase {
             testListener.pushExpectedEvent(NextEvent.CANCEL);
 
             // CANCEL
-            subscription.cancel(clock.getUTCNow(), false);
+            subscription.cancel(clock.getUTCNow(), false, context);
             assertTrue(testListener.isCompleted(2000));
 
             PlanPhase currentPhase = subscription.getCurrentPhase();
@@ -202,16 +202,16 @@ public abstract class TestUserApiCancel extends TestApiBase {
             // SET CTD + RE READ SUBSCRIPTION + CHANGE PLAN
             Duration ctd = getDurationMonth(1);
             DateTime newChargedThroughDate = DefaultClock.addDuration(expectedPhaseTrialChange, ctd);
-            billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate);
+            billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, context);
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
 
             testListener.pushExpectedEvent(NextEvent.CANCEL);
 
             // CANCEL
-            subscription.cancel(clock.getUTCNow(), false);
+            subscription.cancel(clock.getUTCNow(), false, context);
             assertFalse(testListener.isCompleted(2000));
 
-            subscription.uncancel();
+            subscription.uncancel(context);
 
             // MOVE TO EOT + RECHECK
             clock.addDeltaFromReality(ctd);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelMemory.java
index fec3550..57e34e9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelMemory.java
@@ -32,25 +32,25 @@ public class TestUserApiCancelMemory extends TestUserApiCancel {
     }
 
     @Override
-    @Test(enabled=false, groups={"fast"})
+    @Test(enabled=true, groups={"fast-disabled"})
     public void testCancelSubscriptionIMM() {
         super.testCancelSubscriptionIMM();
     }
 
     @Override
-    @Test(enabled=false, groups={"fast"})
+    @Test(enabled=true, groups={"fast-disabled"})
     public void testCancelSubscriptionEOTWithChargeThroughDate() throws EntitlementBillingApiException {
         super.testCancelSubscriptionEOTWithChargeThroughDate();
     }
 
     @Override
-    @Test(enabled=false, groups={"fast"})
+    @Test(enabled=true, groups={"fast-disabled"})
     public void testCancelSubscriptionEOTWithNoChargeThroughDate() {
         super.testCancelSubscriptionEOTWithNoChargeThroughDate();
     }
 
     @Override
-    @Test(enabled=false, groups={"fast"})
+    @Test(enabled=true, groups={"fast-disabled"})
     public void testUncancel() throws EntitlementBillingApiException {
         super.testUncancel();
     }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
index 4505ef0..f7f8c41 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
@@ -88,7 +88,7 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
 
             // CHANGE PLAN
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow());
+            subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow(), context);
             assertTrue(testListener.isCompleted(2000));
 
             // CHECK CHANGE PLAN
@@ -129,12 +129,12 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
             // SET CTD
             Duration ctd = getDurationMonth(1);
             DateTime newChargedThroughDate = DefaultClock.addDuration(expectedPhaseTrialChange, ctd);
-            billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate);
+            billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, context);
 
             // RE READ SUBSCRIPTION + CHANGE PLAN
             testListener.pushExpectedEvent(NextEvent.CHANGE);
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
-            subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow());
+            subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow(), context);
             assertFalse(testListener.isCompleted(2000));
             testListener.reset();
 
@@ -186,7 +186,7 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
             clock.setDeltaFromReality(moveALittleInTime, 0);
 
             // CHANGE PLAN IMM
-            subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow());
+            subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow(), context);
             checkChangePlan(subscription, toProd, ProductCategory.BASE, toTerm, PhaseType.TRIAL);
 
             assertTrue(testListener.isCompleted(2000));
@@ -245,7 +245,7 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
             // SET CTD
             Duration ctd = getDurationMonth(1);
             DateTime newChargedThroughDate = DefaultClock.addDuration(expectedPhaseTrialChange, ctd);
-            billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate);
+            billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, context);
 
             // RE READ SUBSCRIPTION + CHECK CURRENT PHASE
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
@@ -257,7 +257,7 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
             currentTime = clock.getUTCNow();
 
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow());
+            subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow(), context);
 
             checkChangePlan(subscription, fromProd, ProductCategory.BASE, fromTerm, PhaseType.EVERGREEN);
 
@@ -317,17 +317,17 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
             DateTime startDiscountPhase = DefaultClock.addDuration(subscription.getStartDate(), durationList);
             Duration ctd = getDurationMonth(1);
             DateTime newChargedThroughDate = DefaultClock.addDuration(startDiscountPhase, ctd);
-            billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate);
+            billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, context);
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
 
             // CHANGE EOT
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan("Pistol", BillingPeriod.MONTHLY, "gunclubDiscount", clock.getUTCNow());
+            subscription.changePlan("Pistol", BillingPeriod.MONTHLY, "gunclubDiscount", clock.getUTCNow(), context);
             assertFalse(testListener.isCompleted(2000));
 
             // CHANGE
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow());
+            subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
             assertFalse(testListener.isCompleted(2000));
 
             Plan currentPlan = subscription.getCurrentPlan();
@@ -363,18 +363,18 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
             DateTime startDiscountPhase = DefaultClock.addDuration(subscription.getStartDate(), durationList);
             Duration ctd = getDurationMonth(1);
             DateTime newChargedThroughDate = DefaultClock.addDuration(startDiscountPhase, ctd);
-            billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate);
+            billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, context);
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
 
             // CHANGE EOT
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan("Shotgun", BillingPeriod.MONTHLY, "gunclubDiscount", clock.getUTCNow());
+            subscription.changePlan("Shotgun", BillingPeriod.MONTHLY, "gunclubDiscount", clock.getUTCNow(), context);
             assertFalse(testListener.isCompleted(2000));
             testListener.reset();
 
             // CHANGE EOT
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan("Pistol", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow());
+            subscription.changePlan("Pistol", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
             assertFalse(testListener.isCompleted(2000));
             testListener.reset();
 
@@ -442,7 +442,7 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
             // CHANGE IMMEDIATE TO A 3 PHASES PLAN
             testListener.reset();
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow());
+            subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
             assertTrue(testListener.isCompleted(3000));
             testListener.reset();
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
index ab76734..0351ea1 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
@@ -32,44 +32,46 @@ public class TestUserApiChangePlanMemory extends TestUserApiChangePlan {
 
 
     @Override
-    @Test(enabled=false, groups={"fast"})
+    @Test(enabled=true, groups={"fast-disabled"})
     public void testChangePlanBundleAlignEOTWithNoChargeThroughDate() {
          super.testChangePlanBundleAlignEOTWithNoChargeThroughDate();
     }
 
     @Override
-    @Test(enabled=false, groups={"fast"})
+    @Test(enabled=true, groups={"fast-disabled"})
     public void testChangePlanBundleAlignEOTWithChargeThroughDate() throws EntitlementBillingApiException {
         super.testChangePlanBundleAlignEOTWithChargeThroughDate();
     }
 
     @Override
-    @Test(enabled=false, groups={"fast"})
+    @Test(enabled=true, groups={"fast-disabled"})
     public void testChangePlanBundleAlignIMM() {
         super.testChangePlanBundleAlignIMM();
     }
 
     @Override
-    @Test(enabled=false, groups={"fast"})
+    @Test(enabled=true, groups={"fast-disabled"})
     public void testMultipleChangeLastIMM() throws EntitlementBillingApiException {
         super.testMultipleChangeLastIMM();
     }
 
     @Override
-    @Test(enabled=false, groups={"fast"})
+    @Test(enabled=true, groups={"fast-disabled"})
     public void testMultipleChangeLastEOT() throws EntitlementBillingApiException {
         super.testMultipleChangeLastEOT();
     }
 
+    /*
     // Set to false until we implement rescue example.
     @Override
     @Test(enabled=false, groups={"fast"})
     public void testChangePlanChangePlanAlignEOTWithChargeThroughDate() throws EntitlementBillingApiException {
         super.testChangePlanChangePlanAlignEOTWithChargeThroughDate();
     }
+    */
 
     @Override
-    @Test(enabled=true, groups={"fast"})
+    @Test(enabled=true, groups={"fast-disabled"})
     public void testCorrectPhaseAlignmentOnChange() {
         super.testCorrectPhaseAlignmentOnChange();
     }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
index a56f13b..a8d6e0c 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
@@ -60,7 +60,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
 
 
             SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
-                    getProductSpecifier(productName, planSetName, term, null), requestedDate);
+                    getProductSpecifier(productName, planSetName, term, null), requestedDate, context);
             assertNotNull(subscription);
 
             assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
@@ -91,7 +91,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
             testListener.pushExpectedEvent(NextEvent.CREATE);
 
             SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
-                    getProductSpecifier(productName, planSetName, term, PhaseType.EVERGREEN), clock.getUTCNow());
+                    getProductSpecifier(productName, planSetName, term, PhaseType.EVERGREEN), clock.getUTCNow(), context);
             assertNotNull(subscription);
 
             assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
@@ -130,7 +130,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
 
             SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
                     getProductSpecifier(productName, planSetName, term, null),
-                    clock.getUTCNow());
+                    clock.getUTCNow(), context);
             assertNotNull(subscription);
 
             assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
@@ -179,8 +179,6 @@ public abstract class TestUserApiCreate extends TestApiBase {
         log.info("Starting testSimpleSubscriptionThroughPhases");
         try {
 
-            DateTime curTime = clock.getUTCNow();
-
             String productName = "Pistol";
             BillingPeriod term = BillingPeriod.ANNUAL;
             String planSetName = "gunclubDiscount";
@@ -189,7 +187,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
 
             // CREATE SUBSCRIPTION
             SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
-                    getProductSpecifier(productName, planSetName, term, null), clock.getUTCNow());
+                    getProductSpecifier(productName, planSetName, term, null), clock.getUTCNow(), context);
             assertNotNull(subscription);
 
             PlanPhase currentPhase = subscription.getCurrentPhase();
@@ -197,12 +195,9 @@ public abstract class TestUserApiCreate extends TestApiBase {
             assertEquals(currentPhase.getPhaseType(), PhaseType.TRIAL);
             assertTrue(testListener.isCompleted(5000));
 
-
-
             // MOVE TO DISCOUNT PHASE
             testListener.pushExpectedEvent(NextEvent.PHASE);
             clock.setDeltaFromReality(currentPhase.getDuration(), DAY_IN_MS);
-            curTime = clock.getUTCNow();
             currentPhase = subscription.getCurrentPhase();
             assertNotNull(currentPhase);
             assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
@@ -214,7 +209,6 @@ public abstract class TestUserApiCreate extends TestApiBase {
             assertTrue(testListener.isCompleted(2000));
 
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
-            curTime = clock.getUTCNow();
             currentPhase = subscription.getCurrentPhase();
             assertNotNull(currentPhase);
             assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
@@ -237,7 +231,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
             testListener.pushExpectedEvent(NextEvent.CREATE);
 
             SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
-                    getProductSpecifier(productName, planSetName, term, null), clock.getUTCNow());
+                    getProductSpecifier(productName, planSetName, term, null), clock.getUTCNow(), context);
             assertNotNull(subscription);
 
         } catch (EntitlementUserApiException e) {
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateMemory.java
index 047ca67..b674e34 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateMemory.java
@@ -31,31 +31,31 @@ public class TestUserApiCreateMemory extends TestUserApiCreate {
     }
 
     @Override
-    @Test(enabled=false, groups={"fast"})
+    @Test(enabled=true, groups={"fast-disabled"})
     public void testCreateWithRequestedDate() {
         super.testCreateWithRequestedDate();
     }
 
     @Override
-    @Test(enabled=false, groups={"fast"})
+    @Test(enabled=true, groups={"fast-disabled"})
     public void testCreateWithInitialPhase() {
         super.testSimpleSubscriptionThroughPhases();
     }
 
     @Override
-    @Test(enabled=false, groups={"fast"})
+    @Test(enabled=true, groups={"fast-disabled"})
     public void testSimpleCreateSubscription() {
         super.testSimpleCreateSubscription();
     }
 
     @Override
-    @Test(enabled=false, groups={"fast"})
+    @Test(enabled=true, groups={"fast-disabled"})
     protected void testSimpleSubscriptionThroughPhases() {
         super.testSimpleSubscriptionThroughPhases();
     }
 
     @Override
-    @Test(enabled=false, groups={"fast"})
+    @Test(enabled=true, groups={"fast-disabled"})
     protected void testSubscriptionWithAddOn() {
         super.testSubscriptionWithAddOn();
     }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java
index 7fceab9..74fbef7 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java
@@ -79,7 +79,7 @@ public class TestUserApiDemos extends TestApiBase {
 
             /* STEP 2. CHANGE PLAN WHILE IN TRIAL */
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow());
+            subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
             assertTrue(testListener.isCompleted(3000));
 
             displayState(subscription.getId(), "STEP 2. CHANGED PLAN WHILE IN TRIAL");
@@ -98,11 +98,11 @@ public class TestUserApiDemos extends TestApiBase {
 
             Duration ctd = getDurationMonth(1);
             DateTime newChargedThroughDate = DefaultClock.addDuration(startDiscountPhase, ctd);
-            billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate);
+            billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, context);
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
 
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan("Shotgun", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow());
+            subscription.changePlan("Shotgun", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
             assertFalse(testListener.isCompleted(2000));
             testListener.reset();
 
@@ -110,7 +110,7 @@ public class TestUserApiDemos extends TestApiBase {
 
             /* STEP 5. CHANGE AGAIN */
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan("Pistol", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow());
+            subscription.changePlan("Pistol", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
             assertFalse(testListener.isCompleted(2000));
             testListener.reset();
 
@@ -136,7 +136,7 @@ public class TestUserApiDemos extends TestApiBase {
             /* STEP 7.  MOVE TO NEXT PHASE */
             testListener.pushExpectedEvent(NextEvent.PHASE);
             clock.addDeltaFromReality(currentPhase.getDuration());
-            assertTrue(testListener.isCompleted(3000));
+            assertTrue(testListener.isCompleted(5000));
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
 
             currentPlan = subscription.getCurrentPlan();
@@ -153,7 +153,7 @@ public class TestUserApiDemos extends TestApiBase {
 
             /* STEP 8. CANCEL IMM (NO CTD) */
             testListener.pushExpectedEvent(NextEvent.CANCEL);
-            subscription.cancel(clock.getUTCNow(), false);
+            subscription.cancel(clock.getUTCNow(), false, context);
 
             displayState(subscription.getId(), "STEP 8.  CANCELLATION");
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
index 7442298..11103ba 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
@@ -32,6 +32,7 @@ import org.joda.time.DateTime;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import javax.annotation.Nullable;
 import java.util.UUID;
 
 import static org.testng.Assert.assertEquals;
@@ -49,7 +50,7 @@ public class TestUserApiError extends TestApiBase {
 
     @Test(enabled=true, groups={"fast"})
     public void testCreateSubscriptionBadCatalog() {
-        // WRONG PRODUTCS
+        // WRONG PRODUCTS
         tCreateSubscriptionInternal(bundle.getId(), null, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.CAT_NULL_PRODUCT_NAME);
         tCreateSubscriptionInternal(bundle.getId(), "Whatever", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.CAT_NO_SUCH_PRODUCT);
 
@@ -89,7 +90,7 @@ public class TestUserApiError extends TestApiBase {
         try {
             SubscriptionData subscription = createSubscription("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME);
             try {
-                subscription.recreate(getProductSpecifier("Pistol", PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, null), clock.getUTCNow());
+                subscription.recreate(getProductSpecifier("Pistol", PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, null), clock.getUTCNow(), context);
                 Assert.assertFalse(true);
             } catch (EntitlementUserApiException e) {
                 assertEquals(e.getCode(), ErrorCode.ENT_RECREATE_BAD_STATE.getCode());
@@ -104,7 +105,7 @@ public class TestUserApiError extends TestApiBase {
     public void testCreateSubscriptionAddOnNotAvailable() {
         try {
             UUID accountId = UUID.randomUUID();
-            SubscriptionBundle aoBundle = entitlementApi.createBundleForAccount(accountId, "myAOBundle");
+            SubscriptionBundle aoBundle = entitlementApi.createBundleForAccount(accountId, "myAOBundle", context);
             createSubscriptionWithBundle(aoBundle.getId(), "Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
             tCreateSubscriptionInternal(aoBundle.getId(), "Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.ENT_CREATE_AO_NOT_AVAILABLE);
         } catch (Exception e) {
@@ -117,7 +118,7 @@ public class TestUserApiError extends TestApiBase {
     public void testCreateSubscriptionAddOnIncluded() {
         try {
             UUID accountId = UUID.randomUUID();
-            SubscriptionBundle aoBundle = entitlementApi.createBundleForAccount(accountId, "myAOBundle");
+            SubscriptionBundle aoBundle = entitlementApi.createBundleForAccount(accountId, "myAOBundle", context);
             createSubscriptionWithBundle(aoBundle.getId(), "Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
             tCreateSubscriptionInternal(aoBundle.getId(), "Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.ENT_CREATE_AO_ALREADY_INCLUDED);
         } catch (Exception e) {
@@ -127,12 +128,12 @@ public class TestUserApiError extends TestApiBase {
     }
 
 
-    private void tCreateSubscriptionInternal(UUID bundleId, String productName,
-            BillingPeriod term, String planSet, ErrorCode expected)  {
+    private void tCreateSubscriptionInternal(@Nullable UUID bundleId, @Nullable String productName,
+            @Nullable BillingPeriod term, String planSet, ErrorCode expected)  {
         try {
             entitlementApi.createSubscription(bundleId,
                     getProductSpecifier(productName, planSet, term, null),
-                    clock.getUTCNow());
+                    clock.getUTCNow(), context);
             Assert.fail("Exception expected, error code: " + expected);
         } catch (EntitlementUserApiException e) {
             assertEquals(e.getCode(), expected.getCode());
@@ -151,9 +152,9 @@ public class TestUserApiError extends TestApiBase {
             Subscription subscription = createSubscription("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME);
 
             testListener.pushExpectedEvent(NextEvent.CANCEL);
-            subscription.cancel(clock.getUTCNow(), false);
+            subscription.cancel(clock.getUTCNow(), false, context);
             try {
-                subscription.changePlan("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow());
+                subscription.changePlan("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow(), context);
             } catch (EntitlementUserApiException e) {
                 assertEquals(e.getCode(), ErrorCode.ENT_CHANGE_NON_ACTIVE.getCode());
                 try {
@@ -186,13 +187,13 @@ public class TestUserApiError extends TestApiBase {
             DateTime expectedPhaseTrialChange = DefaultClock.addDuration(subscription.getStartDate(), trialPhase.getDuration());
             Duration ctd = getDurationMonth(1);
             DateTime newChargedThroughDate = DefaultClock.addDuration(expectedPhaseTrialChange, ctd);
-            billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate);
+            billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, context);
 
             subscription = entitlementApi.getSubscriptionFromId(subscription.getId());
 
-            subscription.cancel(clock.getUTCNow(), false);
+            subscription.cancel(clock.getUTCNow(), false, context);
             try {
-                subscription.changePlan("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow());
+                subscription.changePlan("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow(), context);
             } catch (EntitlementUserApiException e) {
                 assertEquals(e.getCode(), ErrorCode.ENT_CHANGE_FUTURE_CANCELLED.getCode());
                 try {
@@ -218,7 +219,7 @@ public class TestUserApiError extends TestApiBase {
             Subscription subscription = createSubscription("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
 
             try {
-                subscription.uncancel();
+                subscription.uncancel(context);
             } catch (EntitlementUserApiException e) {
                 assertEquals(e.getCode(), ErrorCode.ENT_UNCANCEL_BAD_STATE.getCode());
                 try {
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java
index cff7e91..7dbc802 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java
@@ -69,7 +69,7 @@ public abstract class TestUserApiRecreate extends TestApiBase {
         testListener.pushExpectedEvent(NextEvent.PHASE);
         testListener.pushExpectedEvent(NextEvent.CREATE);
         SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
-                getProductSpecifier(productName, planSetName, term, null), requestedDate);
+                getProductSpecifier(productName, planSetName, term, null), requestedDate, context);
         assertNotNull(subscription);
         assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
         assertEquals(subscription.getBundleId(), bundle.getId());
@@ -86,9 +86,9 @@ public abstract class TestUserApiRecreate extends TestApiBase {
 
             if (fromUserAPi) {
                 subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
-                        getProductSpecifier(productName, planSetName, term, null), requestedDate);
+                        getProductSpecifier(productName, planSetName, term, null), requestedDate, context);
             } else {
-                subscription.recreate(getProductSpecifier(productName, planSetName, term, null), requestedDate);
+                subscription.recreate(getProductSpecifier(productName, planSetName, term, null), requestedDate, context);
             }
             Assert.fail("Expected Create API to fail since BP already exists");
         } catch (EntitlementUserApiException e) {
@@ -97,12 +97,12 @@ public abstract class TestUserApiRecreate extends TestApiBase {
 
         // NOW CANCEL ADN THIS SHOULD WORK
         testListener.pushExpectedEvent(NextEvent.CANCEL);
-        subscription.cancel(null, false);
+        subscription.cancel(null, false, context);
 
         testListener.pushExpectedEvent(NextEvent.PHASE);
         testListener.pushExpectedEvent(NextEvent.RE_CREATE);
 
-        // Avoid ordering issue for events at excat same date; this is actually a real good test, we
+        // Avoid ordering issue for events at exact same date; this is actually a real good test, we
         // we test it at Beatrix level. At this level that would work for sql tests but not for in memory.
         try {
             Thread.sleep(1000);
@@ -112,9 +112,9 @@ public abstract class TestUserApiRecreate extends TestApiBase {
 
         if (fromUserAPi) {
             subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
-                    getProductSpecifier(productName, planSetName, term, null), requestedDate);
+                    getProductSpecifier(productName, planSetName, term, null), requestedDate, context);
         } else {
-            subscription.recreate(getProductSpecifier(productName, planSetName, term, null), clock.getUTCNow());
+            subscription.recreate(getProductSpecifier(productName, planSetName, term, null), clock.getUTCNow(), context);
         }
         assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
         assertEquals(subscription.getBundleId(), bundle.getId());
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java
index f69b36a..1ece406 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java
@@ -53,7 +53,7 @@ public class TestUserApiScenarios extends TestApiBase {
             assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
 
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan("Pistol", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow());
+            subscription.changePlan("Pistol", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
             testListener.isCompleted(3000);
 
             // MOVE TO NEXT PHASE
@@ -65,21 +65,21 @@ public class TestUserApiScenarios extends TestApiBase {
             Duration ctd = getDurationMonth(1);
             DateTime expectedPhaseTrialChange = DefaultClock.addDuration(subscription.getStartDate(), trialPhase.getDuration());
             DateTime newChargedThroughDate = DefaultClock.addDuration(expectedPhaseTrialChange, ctd);
-            billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate);
+            billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, context);
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
 
             // CANCEL EOT
             testListener.pushExpectedEvent(NextEvent.CANCEL);
-            subscription.cancel(clock.getUTCNow(), false);
+            subscription.cancel(clock.getUTCNow(), false, context);
             assertFalse(testListener.isCompleted(2000));
             testListener.reset();
 
             // UNCANCEL
-            subscription.uncancel();
+            subscription.uncancel(context);
 
             // CHANGE EOT
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan("Pistol", BillingPeriod.MONTHLY, "gunclubDiscount", clock.getUTCNow());
+            subscription.changePlan("Pistol", BillingPeriod.MONTHLY, "gunclubDiscount", clock.getUTCNow(), context);
             assertFalse(testListener.isCompleted(2000));
 
             clock.addDeltaFromReality(ctd);
@@ -90,10 +90,4 @@ public class TestUserApiScenarios extends TestApiBase {
             Assert.fail(e.getMessage());
         }
     }
-
-    @Test(enabled=false)
-    private void testChangeEOTCancelUncancelChangeIMM() {
-
-    }
-
 }
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..bbfd42a 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.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
+import com.ning.billing.util.clock.DefaultClock;
 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 DefaultCallContextFactory(new DefaultClock()).createCallContext(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();
@@ -74,17 +79,17 @@ public class TestUserCustomFieldsSql extends TestApiBase {
             testListener.pushExpectedEvent(NextEvent.PHASE);
             testListener.pushExpectedEvent(NextEvent.CREATE);
             SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
-                    getProductSpecifier(productName, planSetName, term, null), requestedDate);
+                    getProductSpecifier(productName, planSetName, term, null), requestedDate, context);
             assertNotNull(subscription);
 
             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());
@@ -133,11 +135,11 @@ public class TestUserCustomFieldsSql extends TestApiBase {
             testListener.pushExpectedEvent(NextEvent.PHASE);
             testListener.pushExpectedEvent(NextEvent.CREATE);
             SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
-                    getProductSpecifier(productName, planSetName, term, null), requestedDate);
+                    getProductSpecifier(productName, planSetName, term, null), requestedDate, context);
             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 31879e0..94836a8 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.CallContext;
 import org.apache.commons.lang.NotImplementedException;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
@@ -31,6 +32,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
+import com.ning.billing.catalog.api.CatalogService;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.catalog.api.TimeUnit;
 import com.ning.billing.config.EntitlementConfig;
@@ -63,17 +65,17 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     private final TreeSet<EntitlementEvent> events;
     private final Clock clock;
     private final EntitlementConfig config;
-    private final SubscriptionFactory factory;
     private final NotificationQueueService notificationQueueService;
+    private final CatalogService catalogService;
 
     @Inject
     public MockEntitlementDaoMemory(final Clock clock, final EntitlementConfig config,
-                                    final SubscriptionFactory factory,
-                                    final NotificationQueueService notificationQueueService) {
+                                    final NotificationQueueService notificationQueueService,
+                                    final CatalogService catalogService) {
         super();
         this.clock = clock;
         this.config = config;
-        this.factory = factory;
+        this.catalogService = catalogService;
         this.notificationQueueService = notificationQueueService;
         this.bundles = new ArrayList<SubscriptionBundle>();
         this.subscriptions = new ArrayList<Subscription>();
@@ -118,18 +120,17 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
         return null;
     }
 
-
     @Override
-    public SubscriptionBundle createSubscriptionBundle(final SubscriptionBundleData bundle) {
+    public SubscriptionBundle createSubscriptionBundle(final SubscriptionBundleData bundle, final CallContext context) {
         bundles.add(bundle);
         return getSubscriptionBundleFromId(bundle.getId());
     }
 
     @Override
-    public Subscription getSubscriptionFromId(final UUID subscriptionId) {
+    public Subscription getSubscriptionFromId(final SubscriptionFactory factory, final UUID subscriptionId) {
         for (final Subscription cur : subscriptions) {
             if (cur.getId().equals(subscriptionId)) {
-                return buildSubscription((SubscriptionData) cur);
+                return buildSubscription(factory, (SubscriptionData) cur);
             }
         }
         return null;
@@ -141,11 +142,11 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public List<Subscription> getSubscriptionsForKey(final String bundleKey) {
+    public List<Subscription> getSubscriptionsForKey(final SubscriptionFactory factory, final String bundleKey) {
 
         for (final SubscriptionBundle cur : bundles) {
             if (cur.getKey().equals(bundleKey)) {
-                return getSubscriptions(cur.getId());
+                return getSubscriptions(factory, cur.getId());
             }
         }
         return Collections.emptyList();
@@ -153,7 +154,8 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
 
 
     @Override
-    public void createSubscription(final SubscriptionData subscription, final List<EntitlementEvent> initialEvents) {
+    public void createSubscription(final SubscriptionData subscription, final List<EntitlementEvent> initialEvents,
+                                   final CallContext context) {
 
         synchronized(events) {
             events.addAll(initialEvents);
@@ -166,13 +168,13 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
                 });
             }
         }
-        Subscription updatedSubscription = buildSubscription(subscription);
+        Subscription updatedSubscription = buildSubscription(null, subscription);
         subscriptions.add(updatedSubscription);
     }
 
     @Override
     public void recreateSubscription(final UUID subscriptionId,
-            final List<EntitlementEvent> recreateEvents) {
+            final List<EntitlementEvent> recreateEvents, final CallContext context) {
 
         synchronized(events) {
             events.addAll(recreateEvents);
@@ -188,12 +190,12 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public List<Subscription> getSubscriptions(final UUID bundleId) {
+    public List<Subscription> getSubscriptions(final SubscriptionFactory factory, final UUID bundleId) {
 
         List<Subscription> results = new ArrayList<Subscription>();
         for (final Subscription cur : subscriptions) {
             if (cur.getBundleId().equals(bundleId)) {
-                results.add(buildSubscription((SubscriptionData) cur));
+                results.add(buildSubscription(factory, (SubscriptionData) cur));
             }
         }
         return results;
@@ -229,30 +231,39 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
 
 
     @Override
-    public Subscription getBaseSubscription(final UUID bundleId) {
+    public Subscription getBaseSubscription(final SubscriptionFactory factory, final UUID bundleId) {
         for (final Subscription cur : subscriptions) {
             if (cur.getBundleId().equals(bundleId) &&
                     cur.getCurrentPlan().getProduct().getCategory() == ProductCategory.BASE) {
-                return buildSubscription((SubscriptionData) cur);
+                return buildSubscription(factory, (SubscriptionData) cur);
             }
         }
         return null;
     }
 
     @Override
-    public void createNextPhaseEvent(final UUID subscriptionId, final EntitlementEvent nextPhase) {
+    public void createNextPhaseEvent(final UUID subscriptionId, final EntitlementEvent nextPhase,
+                                     final CallContext context) {
         cancelNextPhaseEvent(subscriptionId);
         insertEvent(nextPhase);
     }
 
 
 
-    private Subscription buildSubscription(final SubscriptionData in) {
-        return factory.createSubscription(new SubscriptionBuilder(in), getEventsForSubscription(in.getId()));
+    private Subscription buildSubscription(final SubscriptionFactory factory, final SubscriptionData in) {
+    	if (factory != null) {
+    		return factory.createSubscription(new SubscriptionBuilder(in), getEventsForSubscription(in.getId()));
+    	} else {
+    		SubscriptionData subscription = new SubscriptionData(new SubscriptionBuilder(in), null, clock);
+            if (events.size() > 0) {
+                subscription.rebuildTransitions(getEventsForSubscription(in.getId()), catalogService.getFullCatalog());
+            }
+            return subscription;
+    	}
     }
 
     @Override
-    public void updateSubscription(final SubscriptionData subscription) {
+    public void updateSubscription(final SubscriptionData subscription, final CallContext context) {
 
         boolean found = false;
         Iterator<Subscription> it = subscriptions.iterator();
@@ -270,7 +281,8 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public void cancelSubscription(final UUID subscriptionId, final EntitlementEvent cancelEvent) {
+    public void cancelSubscription(final UUID subscriptionId, final EntitlementEvent cancelEvent,
+                                   final CallContext context) {
         synchronized (cancelEvent) {
             cancelNextPhaseEvent(subscriptionId);
             insertEvent(cancelEvent);
@@ -278,7 +290,8 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public void changePlan(final UUID subscriptionId, final List<EntitlementEvent> changeEvents) {
+    public void changePlan(final UUID subscriptionId, final List<EntitlementEvent> changeEvents,
+                           final CallContext context) {
         synchronized(events) {
             cancelNextChangeEvent(subscriptionId);
             cancelNextPhaseEvent(subscriptionId);
@@ -306,9 +319,10 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
         }
     }
 
+    
     private void cancelNextPhaseEvent(final UUID subscriptionId) {
 
-        Subscription curSubscription = getSubscriptionFromId(subscriptionId);
+        Subscription curSubscription = getSubscriptionFromId(null, subscriptionId);
         if (curSubscription.getCurrentPhase() == null ||
                 curSubscription.getCurrentPhase().getDuration().getUnit() == TimeUnit.UNLIMITED) {
             return;
@@ -354,7 +368,8 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public void uncancelSubscription(final UUID subscriptionId, final List<EntitlementEvent> uncancelEvents) {
+    public void uncancelSubscription(final UUID subscriptionId, final List<EntitlementEvent> uncancelEvents,
+                                     final CallContext context) {
 
         synchronized (events) {
             boolean foundCancel = false;
@@ -381,11 +396,9 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
 
 
     @Override
-    public void migrate(final UUID accountId, final AccountMigrationData accountData) {
+    public void migrate(final UUID accountId, final AccountMigrationData accountData, final CallContext context) {
         synchronized(events) {
 
-            undoMigration(accountId);
-
             for (final BundleMigrationData curBundle : accountData.getData()) {
                 SubscriptionBundleData bundleData = curBundle.getData();
                 for (final SubscriptionMigrationData curSubscription : curBundle.getSubscriptions()) {
@@ -408,26 +421,6 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public void undoMigration(final UUID accountId) {
-        synchronized(events) {
-
-            List<SubscriptionBundle> allBundles = getSubscriptionBundleForAccount(accountId);
-            for (final SubscriptionBundle bundle : allBundles) {
-                List<Subscription> allSubscriptions = getSubscriptions(bundle.getId());
-                for (final Subscription subscription : allSubscriptions) {
-                    List<EntitlementEvent> allEvents = getEventsForSubscription(subscription.getId());
-                    for (final EntitlementEvent event : allEvents) {
-                        events.remove(event);
-                    }
-                    subscriptions.remove(subscription);
-                }
-                bundles.remove(bundle);
-            }
-        }
-
-    }
-
-    @Override
     public EntitlementEvent getEventById(final UUID eventId) {
         synchronized(events) {
             for (final EntitlementEvent cur : events) {
@@ -441,17 +434,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/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java
index cb87dc0..4733f4a 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.entitlement.engine.dao;
 
+import com.ning.billing.util.customfield.dao.CustomFieldDao;
+import com.ning.billing.util.tag.dao.TagDao;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
@@ -34,8 +36,9 @@ public class MockEntitlementDaoSql extends EntitlementSqlDao implements MockEnti
     private final ResetSqlDao resetDao;
 
     @Inject
-    public MockEntitlementDaoSql(IDBI dbi, Clock clock, SubscriptionFactory factory, AddonUtils addonUtils, NotificationQueueService notificationQueueService) {
-        super(dbi, clock, factory, addonUtils, notificationQueueService);
+    public MockEntitlementDaoSql(IDBI dbi, Clock clock, AddonUtils addonUtils, NotificationQueueService notificationQueueService,
+                                 CustomFieldDao customFieldDao) {
+        super(dbi, clock, addonUtils, notificationQueueService, customFieldDao);
         this.resetDao = dbi.onDemand(ResetSqlDao.class);
     }
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java
index 5c1d6b5..acb1a2a 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java
@@ -21,6 +21,7 @@ import com.ning.billing.catalog.glue.CatalogModule;
 import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.util.clock.MockClockModule;
 import com.ning.billing.util.glue.BusModule;
+import com.ning.billing.util.glue.CallContextModule;
 
 public class MockEngineModule extends EntitlementModule {
 
@@ -31,5 +32,6 @@ public class MockEngineModule extends EntitlementModule {
         install(new CatalogModule());
         bind(AccountUserApi.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class));
         install(new MockClockModule());
+        install(new CallContextModule());
     }
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java
index 786f1e3..8b78c2d 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java
@@ -29,10 +29,10 @@ public class MockEngineModuleMemory extends MockEngineModule {
         bind(EntitlementDao.class).to(MockEntitlementDaoMemory.class).asEagerSingleton();
     }
 
-
     private void installNotificationQueue() {
         bind(NotificationQueueService.class).to(MockNotificationQueueService.class).asEagerSingleton();
     }
+
     @Override
     protected void configure() {
         super.configure();
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
index c452c3b..f774e58 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
@@ -23,6 +23,7 @@ import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.engine.dao.MockEntitlementDaoSql;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.glue.FieldStoreModule;
 import com.ning.billing.util.glue.NotificationQueueModule;
 
 import org.skife.config.ConfigurationObjectFactory;
@@ -53,6 +54,7 @@ public class MockEngineModuleSql extends MockEngineModule {
     protected void configure() {
         installDBI();
         install(new NotificationQueueModule());
+        install(new FieldStoreModule());
         super.configure();
     }
 }

invoice/pom.xml 10(+9 -1)

diff --git a/invoice/pom.xml b/invoice/pom.xml
index ccef93e..8db3258 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.8-SNAPSHOT</version>
+        <version>0.1.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-invoice</artifactId>
@@ -39,13 +39,21 @@
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
+            <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-entitlement</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>com.ning.billing</groupId>
             <artifactId>killbill-entitlement</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>com.ning.billing</groupId>
             <artifactId>killbill-catalog</artifactId>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>com.ning.billing</groupId>
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java
index 3a04c3f..11712d2 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java
@@ -21,6 +21,7 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
 import org.joda.time.DateTime;
 
 import com.google.inject.Inject;
@@ -40,8 +41,8 @@ public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
      }
 
     @Override
-    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment) {
-        dao.notifyOfPaymentAttempt(invoicePayment);
+    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment, CallContext context) {
+        dao.notifyOfPaymentAttempt(invoicePayment, context);
     }
 
 	@Override
@@ -66,15 +67,15 @@ public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
     }
 
     @Override
-    public void notifyOfPaymentAttempt(UUID invoiceId, BigDecimal amount, Currency currency, UUID paymentAttemptId, DateTime paymentAttemptDate) {
-        InvoicePayment invoicePayment = new DefaultInvoicePayment(paymentAttemptId, invoiceId, paymentAttemptDate, amount, currency, null);
-        dao.notifyOfPaymentAttempt(invoicePayment);
+    public void notifyOfPaymentAttempt(UUID invoiceId, BigDecimal amount, Currency currency, UUID paymentAttemptId, DateTime paymentAttemptDate, CallContext context) {
+        InvoicePayment invoicePayment = new DefaultInvoicePayment(paymentAttemptId, invoiceId, paymentAttemptDate, amount, currency);
+        dao.notifyOfPaymentAttempt(invoicePayment, context);
     }
 
     @Override
-    public void notifyOfPaymentAttempt(UUID invoiceId, UUID paymentAttemptId, DateTime paymentAttemptDate) {
+    public void notifyOfPaymentAttempt(UUID invoiceId, UUID paymentAttemptId, DateTime paymentAttemptDate, CallContext context) {
         InvoicePayment invoicePayment = new DefaultInvoicePayment(paymentAttemptId, invoiceId, paymentAttemptDate);
-        dao.notifyOfPaymentAttempt(invoicePayment);
+        dao.notifyOfPaymentAttempt(invoicePayment, context);
     }
 
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java
index 6852b69..bea87a7 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java
@@ -19,6 +19,10 @@ package com.ning.billing.invoice.api.migration;
 import java.math.BigDecimal;
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
 import org.joda.time.DateTime;
 
 import com.google.inject.Inject;
@@ -27,7 +31,6 @@ import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoiceMigrationApi;
 import com.ning.billing.invoice.dao.DefaultInvoiceDao;
-import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.invoice.model.MigrationInvoiceItem;
 import com.ning.billing.util.clock.Clock;
 
@@ -44,10 +47,11 @@ public class DefaultInvoiceMigrationApi implements InvoiceMigrationApi {
 
 	@Override
 	public UUID createMigrationInvoice(UUID accountId, DateTime targetDate, BigDecimal balance, Currency currency) {
-		Invoice migrationInvoice = new DefaultInvoice(accountId, targetDate, currency, clock, true);
-		InvoiceItem migrationInvoiceItem = new MigrationInvoiceItem(migrationInvoice.getId(), targetDate, balance, currency, clock);
+        CallContext context = new DefaultCallContextFactory(clock).createMigrationCallContext("Migration", CallOrigin.INTERNAL, UserType.MIGRATION, clock.getUTCNow(), clock.getUTCNow());
+		Invoice migrationInvoice = new MigrationInvoice(accountId, clock.getUTCNow(), targetDate, currency);
+		InvoiceItem migrationInvoiceItem = new MigrationInvoiceItem(migrationInvoice.getId(), accountId, targetDate, balance, currency );
 		migrationInvoice.addInvoiceItem(migrationInvoiceItem);
-		dao.create(migrationInvoice);
+		dao.create(migrationInvoice, context);
 		return migrationInvoice.getId();
 	}
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/migration/MigrationInvoice.java b/invoice/src/main/java/com/ning/billing/invoice/api/migration/MigrationInvoice.java
new file mode 100644
index 0000000..84877eb
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/migration/MigrationInvoice.java
@@ -0,0 +1,29 @@
+/*
+ * 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.invoice.api.migration;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.model.DefaultInvoice;
+import org.joda.time.DateTime;
+
+import java.util.UUID;
+
+public class MigrationInvoice extends DefaultInvoice {
+    public MigrationInvoice(UUID accountId, DateTime invoiceDate, DateTime targetDate, Currency currency) {
+        super(UUID.randomUUID(), accountId, null, invoiceDate, targetDate, currency, true, null, null);
+    }
+}
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..ec0ca88 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.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 8e34ec9..f556778 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,8 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.tag.ControlTagType;
 import org.joda.time.DateTime;
 
 import com.google.inject.Inject;
@@ -51,8 +53,8 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
     }
 
     @Override
-    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment) {
-        dao.notifyOfPaymentAttempt(invoicePayment);
+    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment, CallContext context) {
+        dao.notifyOfPaymentAttempt(invoicePayment, context);
     }
 
     @Override
@@ -72,8 +74,19 @@ 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);
 	}
+
+    @Override
+    public void tagInvoiceAsWrittenOff(final UUID invoiceId, final CallContext context) {
+        dao.addControlTag(ControlTagType.WRITTEN_OFF, invoiceId, context);
+    }
+
+    @Override
+    public void tagInvoiceAsNotWrittenOff(final UUID invoiceId, final CallContext context) {
+        dao.removeControlTag(ControlTagType.WRITTEN_OFF, invoiceId, 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 03970d0..cf46d15 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,16 +17,26 @@
 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.ChangeType;
+import com.ning.billing.util.audit.dao.AuditSqlDao;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.customfield.dao.CustomFieldSqlDao;
+import com.ning.billing.util.tag.ControlTagType;
+import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.dao.TagDao;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -48,20 +58,23 @@ public class DefaultInvoiceDao implements InvoiceDao {
     private final InvoiceSqlDao invoiceSqlDao;
     private final InvoicePaymentSqlDao invoicePaymentSqlDao;
     private final EntitlementBillingApi entitlementBillingApi;
+    private final TagDao tagDao;
 
     private final Bus eventBus;
 
-	private NextBillingDatePoster nextBillingDatePoster;
+	private final NextBillingDatePoster nextBillingDatePoster;
 
     @Inject
     public DefaultInvoiceDao(final IDBI dbi, final Bus eventBus,
                              final EntitlementBillingApi entitlementBillingApi,
-                             NextBillingDatePoster nextBillingDatePoster) {
+                             final NextBillingDatePoster nextBillingDatePoster,
+                             final TagDao tagDao) {
         this.invoiceSqlDao = dbi.onDemand(InvoiceSqlDao.class);
         this.invoicePaymentSqlDao = dbi.onDemand(InvoicePaymentSqlDao.class);
         this.eventBus = eventBus;
         this.entitlementBillingApi = entitlementBillingApi;
         this.nextBillingDatePoster = nextBillingDatePoster;
+        this.tagDao = tagDao;
     }
 
     @Override
@@ -71,8 +84,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
             public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
                 List<Invoice> invoices = invoiceDao.getInvoicesByAccount(accountId.toString());
 
-                getInvoiceItemsWithinTransaction(invoices, invoiceDao);
-                getInvoicePaymentsWithinTransaction(invoices, invoiceDao);
+                populateChildren(invoices, invoiceDao);
 
                 return invoices;
             }
@@ -86,8 +98,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
     		public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
     			List<Invoice> invoices = invoiceDao.getAllInvoicesByAccount(accountId.toString());
 
-    			getInvoiceItemsWithinTransaction(invoices, invoiceDao);
-    			getInvoicePaymentsWithinTransaction(invoices, invoiceDao);
+                populateChildren(invoices, invoiceDao);
 
     			return invoices;
     		}
@@ -101,8 +112,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
             public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
                 List<Invoice> invoices = invoiceDao.getInvoicesByAccountAfterDate(accountId.toString(), fromDate.toDate());
 
-                getInvoiceItemsWithinTransaction(invoices, invoiceDao);
-                getInvoicePaymentsWithinTransaction(invoices, invoiceDao);
+                populateChildren(invoices, invoiceDao);
 
                 return invoices;
             }
@@ -116,8 +126,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
              public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
                  List<Invoice> invoices = invoiceDao.get();
 
-                 getInvoiceItemsWithinTransaction(invoices, invoiceDao);
-                 getInvoicePaymentsWithinTransaction(invoices, invoiceDao);
+                 populateChildren(invoices, invoiceDao);
 
                  return invoices;
              }
@@ -132,8 +141,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
                  Invoice invoice = invoiceDao.getById(invoiceId.toString());
 
                  if (invoice != null) {
-                     getInvoiceItemsWithinTransaction(invoice, invoiceDao);
-                     getInvoicePaymentsWithinTransaction(invoice, invoiceDao);
+                     populateChildren(invoice, invoiceDao);
                  }
 
                  return invoice;
@@ -142,7 +150,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 {
@@ -151,24 +159,31 @@ 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);
-                    recurringInvoiceItemDao.batchCreateFromTransaction(recurringInvoiceItems);
+                    recurringInvoiceItemDao.batchCreateFromTransaction(recurringInvoiceItems, context);
 
                     notifyOfFutureBillingEvents(invoiceSqlDao, recurringInvoiceItems);
 
                     List<InvoiceItem> fixedPriceInvoiceItems = invoice.getInvoiceItems(FixedPriceInvoiceItem.class);
                     FixedPriceInvoiceItemSqlDao fixedPriceInvoiceItemDao = invoiceDao.become(FixedPriceInvoiceItemSqlDao.class);
-                    fixedPriceInvoiceItemDao.batchCreateFromTransaction(fixedPriceInvoiceItems);
+                    fixedPriceInvoiceItemDao.batchCreateFromTransaction(fixedPriceInvoiceItems, context);
 
-                    setChargedThroughDates(invoiceSqlDao, fixedPriceInvoiceItems, recurringInvoiceItems);
+                    setChargedThroughDates(invoiceSqlDao, fixedPriceInvoiceItems, recurringInvoiceItems, context);
 
                     // STEPH Why do we need that? Are the payments not always null at this point?
                     List<InvoicePayment> invoicePayments = invoice.getPayments();
                     InvoicePaymentSqlDao invoicePaymentSqlDao = invoiceDao.become(InvoicePaymentSqlDao.class);
-                    invoicePaymentSqlDao.batchCreateFromTransaction(invoicePayments);
+                    invoicePaymentSqlDao.batchCreateFromTransaction(invoicePayments, context);
+
+                    AuditSqlDao auditSqlDao = invoiceDao.become(AuditSqlDao.class);
+                    auditSqlDao.insertAuditFromTransaction("invoices", invoice.getId().toString(), ChangeType.INSERT, context);
+                    auditSqlDao.insertAuditFromTransaction("recurring_invoice_items", getIdsFromInvoiceItems(recurringInvoiceItems), ChangeType.INSERT, context);
+                    auditSqlDao.insertAuditFromTransaction("fixed_invoice_items", getIdsFromInvoiceItems(fixedPriceInvoiceItems), ChangeType.INSERT, context);
+                    auditSqlDao.insertAuditFromTransaction("invoice_payments", getIdsFromInvoicePayments(invoicePayments), ChangeType.INSERT, context);
+
                 }
 
                 return null;
@@ -187,6 +202,26 @@ public class DefaultInvoiceDao implements InvoiceDao {
         }
     }
 
+    private List<String> getIdsFromInvoiceItems(List<InvoiceItem> invoiceItems) {
+        List<String> ids = new ArrayList<String>();
+
+        for (InvoiceItem item : invoiceItems) {
+            ids.add(item.getId().toString());
+        }
+
+        return ids;
+    }
+
+    private List<String> getIdsFromInvoicePayments(List<InvoicePayment> invoicePayments) {
+        List<String> ids = new ArrayList<String>();
+
+        for (InvoicePayment payment : invoicePayments) {
+            ids.add(payment.getId().toString());
+        }
+
+        return ids;
+    }
+
     @Override
     public List<Invoice> getInvoicesBySubscription(final UUID subscriptionId) {
         return invoiceSqlDao.inTransaction(new Transaction<List<Invoice>, InvoiceSqlDao>() {
@@ -194,8 +229,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
             public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
                 List<Invoice> invoices = invoiceDao.getInvoicesBySubscription(subscriptionId.toString());
 
-                getInvoiceItemsWithinTransaction(invoices, invoiceDao);
-                getInvoicePaymentsWithinTransaction(invoices, invoiceDao);
+                populateChildren(invoices, invoiceDao);
 
                 return invoices;
             }
@@ -208,8 +242,19 @@ public class DefaultInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment) {
-        invoicePaymentSqlDao.notifyOfPaymentAttempt(invoicePayment);
+    public void notifyOfPaymentAttempt(final InvoicePayment invoicePayment, final CallContext context) {
+        invoicePaymentSqlDao.inTransaction(new Transaction<Void, InvoicePaymentSqlDao>() {
+            @Override
+            public Void inTransaction(InvoicePaymentSqlDao transactional, TransactionStatus status) throws Exception {
+                transactional.notifyOfPaymentAttempt(invoicePayment, context);
+
+                AuditSqlDao auditSqlDao = transactional.become(AuditSqlDao.class);
+                String invoicePaymentId = invoicePayment.getId().toString();
+                auditSqlDao.insertAuditFromTransaction("invoice_payments", invoicePaymentId, ChangeType.INSERT, context);
+
+                return null;
+            }
+        });
     }
 
     @Override
@@ -219,8 +264,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
             public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
                 List<Invoice> invoices = invoiceSqlDao.getUnpaidInvoicesByAccountId(accountId.toString(), upToDate.toDate());
 
-                getInvoiceItemsWithinTransaction(invoices, invoiceDao);
-                getInvoicePaymentsWithinTransaction(invoices, invoiceDao);
+                populateChildren(invoices, invoiceDao);
 
                 return invoices;
             }
@@ -238,10 +282,34 @@ public class DefaultInvoiceDao implements InvoiceDao {
     }
 
     @Override
+    public void addControlTag(ControlTagType controlTagType, UUID invoiceId, CallContext context) {
+        tagDao.addTag(controlTagType.toString(), invoiceId, Invoice.ObjectType, context);
+    }
+
+    @Override
+    public void removeControlTag(ControlTagType controlTagType, UUID invoiceId, CallContext context) {
+        tagDao.removeTag(controlTagType.toString(), invoiceId, Invoice.ObjectType, context);
+    }
+
+    @Override
     public void test() {
         invoiceSqlDao.test();
     }
 
+    private void populateChildren(final Invoice invoice, InvoiceSqlDao invoiceSqlDao) {
+        getInvoiceItemsWithinTransaction(invoice, invoiceSqlDao);
+        getInvoicePaymentsWithinTransaction(invoice, invoiceSqlDao);
+        getTagsWithinTransaction(invoice, invoiceSqlDao);
+        getFieldsWithinTransaction(invoice, invoiceSqlDao);
+    }
+
+    private void populateChildren(List<Invoice> invoices, InvoiceSqlDao invoiceSqlDao) {
+        getInvoiceItemsWithinTransaction(invoices, invoiceSqlDao);
+        getInvoicePaymentsWithinTransaction(invoices, invoiceSqlDao);
+        getTagsWithinTransaction(invoices, invoiceSqlDao);
+        getFieldsWithinTransaction(invoices, invoiceSqlDao);
+    }
+
     private void getInvoiceItemsWithinTransaction(final List<Invoice> invoices, final InvoiceSqlDao invoiceDao) {
         for (final Invoice invoice : invoices) {
             getInvoiceItemsWithinTransaction(invoice, invoiceDao);
@@ -264,13 +332,37 @@ public class DefaultInvoiceDao implements InvoiceDao {
         }
     }
 
-    private void getInvoicePaymentsWithinTransaction(final Invoice invoice, final InvoiceSqlDao invoiceDao) {
-        InvoicePaymentSqlDao invoicePaymentSqlDao = invoiceDao.become(InvoicePaymentSqlDao.class);
+    private void getInvoicePaymentsWithinTransaction(final Invoice invoice, final InvoiceSqlDao invoiceSqlDao) {
+        InvoicePaymentSqlDao invoicePaymentSqlDao = invoiceSqlDao.become(InvoicePaymentSqlDao.class);
         String invoiceId = invoice.getId().toString();
         List<InvoicePayment> invoicePayments = invoicePaymentSqlDao.getPaymentsForInvoice(invoiceId);
         invoice.addPayments(invoicePayments);
     }
 
+    private void getTagsWithinTransaction(final List<Invoice> invoices, final InvoiceSqlDao invoiceSqlDao) {
+        for (final Invoice invoice : invoices) {
+            getTagsWithinTransaction(invoice, invoiceSqlDao);
+        }
+    }
+
+    private void getTagsWithinTransaction(final Invoice invoice, final Transmogrifier dao) {
+        List<Tag> tags = tagDao.loadTagsFromTransaction(dao, invoice.getId(), Invoice.ObjectType);
+        invoice.addTags(tags);
+    }
+
+    private void getFieldsWithinTransaction(final List<Invoice> invoices, final InvoiceSqlDao invoiceSqlDao) {
+        for (final Invoice invoice : invoices) {
+            getFieldsWithinTransaction(invoice, invoiceSqlDao);
+        }
+    }
+
+    private void getFieldsWithinTransaction(final Invoice invoice, final InvoiceSqlDao invoiceSqlDao) {
+        CustomFieldSqlDao customFieldSqlDao = invoiceSqlDao.become(CustomFieldSqlDao.class);
+        String invoiceId = invoice.getId().toString();
+        List<CustomField> customFields = customFieldSqlDao.load(invoiceId, Invoice.ObjectType);
+        invoice.setFields(customFields);
+    }
+
     private void notifyOfFutureBillingEvents(final InvoiceSqlDao dao, final List<InvoiceItem> invoiceItems) {
         for (final InvoiceItem item : invoiceItems) {
             if (item instanceof RecurringInvoiceItem) {
@@ -285,7 +377,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
     }
     
     private void setChargedThroughDates(final InvoiceSqlDao dao, final Collection<InvoiceItem> fixedPriceItems,
-                                        final Collection<InvoiceItem> recurringItems) {
+                                        final Collection<InvoiceItem> recurringItems, CallContext context) {
         Map<UUID, DateTime> chargeThroughDates = new HashMap<UUID, DateTime>();
         addInvoiceItemsToChargeThroughDates(chargeThroughDates, fixedPriceItems);
         addInvoiceItemsToChargeThroughDates(chargeThroughDates, recurringItems);
@@ -293,7 +385,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
         for (UUID subscriptionId : chargeThroughDates.keySet()) {
             DateTime chargeThroughDate = chargeThroughDates.get(subscriptionId);
             log.info("Setting CTD for subscription {} to {}", subscriptionId.toString(), chargeThroughDate.toString());
-            entitlementBillingApi.setChargedThroughDateFromTransaction(dao, subscriptionId, chargeThroughDate);
+            entitlementBillingApi.setChargedThroughDateFromTransaction(dao, subscriptionId, chargeThroughDate, context);
         }
     }
 
@@ -311,5 +403,4 @@ public class DefaultInvoiceDao implements InvoiceDao {
             }
         }
     }
-
 }
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..f564658 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.CallContext;
+import com.ning.billing.util.callcontext.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)
@@ -77,6 +79,7 @@ public interface FixedPriceInvoiceItemSqlDao extends EntityDao<InvoiceItem> {
                     public void bind(SQLStatement q, FixedPriceInvoiceItemBinder bind, FixedPriceInvoiceItem item) {
                         q.bind("id", item.getId().toString());
                         q.bind("invoiceId", item.getInvoiceId().toString());
+                        q.bind("accountId", item.getAccountId().toString());
                         q.bind("subscriptionId", item.getSubscriptionId().toString());
                         q.bind("planName", item.getPlanName());
                         q.bind("phaseName", item.getPhaseName());
@@ -84,7 +87,6 @@ public interface FixedPriceInvoiceItemSqlDao extends EntityDao<InvoiceItem> {
                         q.bind("endDate", item.getEndDate().toDate());
                         q.bind("amount", item.getAmount());
                         q.bind("currency", item.getCurrency().toString());
-                        q.bind("createdDate", item.getCreatedDate().toDate());
                     }
                 };
             }
@@ -96,6 +98,7 @@ public interface FixedPriceInvoiceItemSqlDao extends EntityDao<InvoiceItem> {
         public FixedPriceInvoiceItem map(int index, ResultSet result, StatementContext context) throws SQLException {
             UUID id = UUID.fromString(result.getString("id"));
             UUID invoiceId = UUID.fromString(result.getString("invoice_id"));
+            UUID accountId = UUID.fromString(result.getString("account_id"));
             UUID subscriptionId = UUID.fromString(result.getString("subscription_id"));
             String planName = result.getString("plan_name");
             String phaseName = result.getString("phase_name");
@@ -103,10 +106,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);
+            return new FixedPriceInvoiceItem(id, invoiceId, accountId, subscriptionId, planName, phaseName,
+                                            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 e09b9d2..2b22c90 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
@@ -17,16 +17,18 @@
 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.CallContext;
+import com.ning.billing.util.tag.Taggable;
+import com.ning.billing.util.tag.dao.TaggableDao;
 import org.joda.time.DateTime;
 
 import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
-public interface InvoiceDao {
-    void create(Invoice invoice);
+public interface InvoiceDao extends TaggableDao {
+    void create(Invoice invoice, CallContext context);
 
     Invoice getById(final UUID id);
 
@@ -42,7 +44,7 @@ public interface InvoiceDao {
 
     InvoicePayment getInvoicePayment(final UUID paymentAttemptId);
 
-    void notifyOfPaymentAttempt(final InvoicePayment invoicePayment);
+    void notifyOfPaymentAttempt(final InvoicePayment invoicePayment, final CallContext context);
 
     BigDecimal getAccountBalance(final UUID accountId);
 
@@ -50,5 +52,5 @@ public interface InvoiceDao {
 
     void test();
 
-	List<Invoice> getAllInvoicesByAccount(UUID accountId);
+	List<Invoice> getAllInvoicesByAccount(final UUID accountId);
 }
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..c51d65d 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
@@ -24,13 +24,14 @@ import java.lang.annotation.Target;
 import java.math.BigDecimal;
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.sql.Timestamp;
 import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.dao.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;
@@ -41,6 +42,8 @@ import org.skife.jdbi.v2.sqlobject.SqlBatch;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
+import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
@@ -48,7 +51,7 @@ import com.ning.billing.invoice.api.InvoicePayment;
 
 @ExternalizedSqlViaStringTemplate3
 @RegisterMapper(InvoicePaymentSqlDao.InvoicePaymentMapper.class)
-public interface InvoicePaymentSqlDao {
+public interface InvoicePaymentSqlDao extends Transactional<InvoicePaymentSqlDao>, Transmogrifier {
     @SqlQuery
     public InvoicePayment getByPaymentAttemptId(@Bind("paymentAttempt") final String paymentAttemptId);
 
@@ -56,38 +59,44 @@ public interface InvoicePaymentSqlDao {
     public List<InvoicePayment> get();
 
     @SqlUpdate
-    public void create(@InvoicePaymentBinder  InvoicePayment invoicePayment);
+    public void create(@InvoicePaymentBinder final InvoicePayment invoicePayment,
+                       @CallContextBinder final CallContext context);
 
     @SqlBatch(transactional=false)
-    void batchCreateFromTransaction(@InvoicePaymentBinder List<InvoicePayment> items);
+    void batchCreateFromTransaction(@InvoicePaymentBinder final List<InvoicePayment> items,
+                                    @CallContextBinder final CallContext context);
 
     @SqlQuery
-    public List<InvoicePayment> getPaymentsForInvoice(@Bind("invoiceId") String invoiceId);
+    public List<InvoicePayment> getPaymentsForInvoice(@Bind("invoiceId") final String invoiceId);
 
     @SqlQuery
-    InvoicePayment getInvoicePayment(@Bind("paymentAttemptId") UUID paymentAttemptId);
+    InvoicePayment getInvoicePayment(@Bind("paymentAttemptId") final UUID paymentAttemptId);
 
     @SqlUpdate
-    void notifyOfPaymentAttempt(@InvoicePaymentBinder InvoicePayment invoicePayment);
+    void notifyOfPaymentAttempt(@InvoicePaymentBinder final InvoicePayment invoicePayment,
+                                @CallContextBinder final CallContext context);
+
 
-    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 id = UUID.fromString(result.getString("id"));
             final UUID paymentAttemptId = UUID.fromString(result.getString("payment_attempt_id"));
             final UUID invoiceId = UUID.fromString(result.getString("invoice_id"));
             final DateTime paymentAttemptDate = getDate(result, "payment_attempt_date");
             final BigDecimal amount = result.getBigDecimal("amount");
             final String currencyString = result.getString("currency");
             final Currency currency = (currencyString == null) ? null : Currency.valueOf(currencyString);
+            final String createdBy = result.getString("created_by");
             final DateTime createdDate = getDate(result, "created_date");
 
             return new InvoicePayment() {
                 @Override
+                public UUID getId() {
+                    return id;
+                }
+                @Override
                 public UUID getPaymentAttemptId() {
                     return paymentAttemptId;
                 }
@@ -108,6 +117,10 @@ public interface InvoicePaymentSqlDao {
                     return currency;
                 }
                 @Override
+                public String getCreatedBy() {
+                    return createdBy;
+                }
+                @Override
                 public DateTime getCreatedDate() {
                     return createdDate ;
                 }
@@ -125,14 +138,13 @@ public interface InvoicePaymentSqlDao {
                 return new Binder<InvoicePaymentBinder, InvoicePayment>() {
                     @Override
                     public void bind(SQLStatement q, InvoicePaymentBinder bind, InvoicePayment payment) {
+                        q.bind("id", payment.getId().toString());
                         q.bind("invoiceId", payment.getInvoiceId().toString());
                         q.bind("paymentAttemptId", payment.getPaymentAttemptId().toString());
                         q.bind("paymentAttemptDate", payment.getPaymentAttemptDate().toDate());
                         q.bind("amount", payment.getAmount());
                         Currency currency = payment.getCurrency();
                         q.bind("currency", (currency == null) ? null : currency.toString());
-                        DateTime createdDate = payment.getCreatedDate();
-                        q.bind("createdDate", (createdDate == null) ? new DateTime().toDate() : createdDate.toDate());
                     }
                 };
             }
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 61c3644..dc8e303 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.CallContext;
 import com.ning.billing.util.UuidMapper;
+import com.ning.billing.util.callcontext.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);
@@ -74,11 +76,6 @@ public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<Invoice
     UUID getInvoiceIdByPaymentAttemptId(@Bind("paymentAttemptId") final String paymentAttemptId);
 
     @SqlQuery
-    @RegisterMapper(UuidMapper.class)
-    List<UUID> getInvoicesForPayment(@Bind("targetDate") final Date targetDate,
-                                    @Bind("numberOfDays") final int numberOfDays);
-
-    @SqlQuery
     @RegisterMapper(BalanceMapper.class)
     BigDecimal getAccountBalance(@Bind("accountId") final String accountId);
 
@@ -120,8 +117,10 @@ public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<Invoice
             DateTime targetDate = new DateTime(result.getTimestamp("target_date"));
             Currency currency = Currency.valueOf(result.getString("currency"));
             boolean isMigrationInvoice = result.getBoolean("migrated");
+            String createdBy = result.getString("created_by");
+            DateTime createdDate = new DateTime(result.getTimestamp("created_date"));
 
-            return new DefaultInvoice(id, accountId, invoiceNumber, invoiceDate, targetDate, currency, isMigrationInvoice);
+            return new DefaultInvoice(id, accountId, invoiceNumber, invoiceDate, targetDate, currency, isMigrationInvoice, 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..29dfb9e 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.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
 import com.ning.billing.util.entity.EntityDao;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.SQLStatement;
@@ -59,10 +61,10 @@ 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);
+    void batchCreateFromTransaction(@RecurringInvoiceItemBinder final List<InvoiceItem> items, @CallContextBinder final CallContext context);
 
     @BindingAnnotation(RecurringInvoiceItemBinder.InvoiceItemBinderFactory.class)
     @Retention(RetentionPolicy.RUNTIME)
@@ -76,6 +78,7 @@ public interface RecurringInvoiceItemSqlDao extends EntityDao<InvoiceItem> {
                     public void bind(SQLStatement q, RecurringInvoiceItemBinder bind, RecurringInvoiceItem item) {
                         q.bind("id", item.getId().toString());
                         q.bind("invoiceId", item.getInvoiceId().toString());
+                        q.bind("accountId", item.getAccountId().toString());
                         q.bind("subscriptionId", item.getSubscriptionId().toString());
                         q.bind("planName", item.getPlanName());
                         q.bind("phaseName", item.getPhaseName());
@@ -85,7 +88,6 @@ public interface RecurringInvoiceItemSqlDao extends EntityDao<InvoiceItem> {
                         q.bind("rate", item.getRate());
                         q.bind("currency", item.getCurrency().toString());
                         q.bind("reversedItemId", (item.getReversedItemId() == null) ? null : item.getReversedItemId().toString());
-                        q.bind("createdDate", item.getCreatedDate().toDate());
                     }
                 };
             }
@@ -97,6 +99,7 @@ public interface RecurringInvoiceItemSqlDao extends EntityDao<InvoiceItem> {
         public InvoiceItem map(int index, ResultSet result, StatementContext context) throws SQLException {
             UUID id = UUID.fromString(result.getString("id"));
             UUID invoiceId = UUID.fromString(result.getString("invoice_id"));
+            UUID accountId = UUID.fromString(result.getString("account_id"));
             UUID subscriptionId = UUID.fromString(result.getString("subscription_id"));
             String planName = result.getString("plan_name");
             String phaseName = result.getString("phase_name");
@@ -107,10 +110,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);
+            return new RecurringInvoiceItem(id, invoiceId, accountId, subscriptionId, planName, phaseName, startDate, endDate,
+                    amount, rate, currency, reversedItemId, createdBy, createdDate);
         }
     }
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java b/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java
index c5e55cc..ba63bf1 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java
@@ -76,7 +76,6 @@ public class InvoiceModule extends AbstractModule {
     }
 
     protected void installInvoiceListener() {
-
         bind(InvoiceListener.class).asEagerSingleton();
     }
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
index c4a3b6a..32c625c 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.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..ff23965 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,10 @@ package com.ning.billing.invoice;
 
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.callcontext.CallContextFactory;
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -30,16 +34,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 CallContextFactory factory;
 
     @Inject
-    public InvoiceListener(InvoiceDispatcher dispatcher) {
+    public InvoiceListener(CallContextFactory factory, InvoiceDispatcher dispatcher) {
         this.dispatcher = dispatcher;
+        this.factory = factory;
     }
 
     @Subscribe
     public void handleSubscriptionTransition(final SubscriptionTransition transition) {
         try {
-        	dispatcher.processSubscription(transition);
+            CallContext context = factory.createCallContext("Transition", CallOrigin.INTERNAL, UserType.SYSTEM);
+        	dispatcher.processSubscription(transition, context);
         } catch (InvoiceApiException e) {
             log.error(e.getMessage());
         }
@@ -47,7 +54,8 @@ public class InvoiceListener {
 
     public void handleNextBillingDateEvent(final UUID subscriptionId, final DateTime eventDateTime) {
         try {
-        	dispatcher.processSubscription(subscriptionId, eventDateTime);
+            CallContext context = factory.createCallContext("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 ed8f42d..1467415 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
@@ -23,19 +23,19 @@ import java.util.UUID;
 
 import javax.annotation.Nullable;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.entity.ExtendedEntityBase;
 import org.joda.time.DateTime;
 
 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;
 
-
-public class DefaultInvoice implements Invoice {
+public class DefaultInvoice extends ExtendedEntityBase 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;
@@ -43,28 +43,21 @@ public class DefaultInvoice implements Invoice {
     private final Currency currency;
     private final boolean migrationInvoice;
 
-    public DefaultInvoice(UUID accountId, DateTime targetDate, Currency currency, Clock clock) {
-        this(UUID.randomUUID(), accountId, null, clock.getUTCNow(), targetDate, currency);
-    }
-
-    public DefaultInvoice(UUID accountId, DateTime targetDate, Currency currency, Clock clock, boolean migrationInvoice) {
-        this(UUID.randomUUID(), accountId, null, clock.getUTCNow(), targetDate, currency, migrationInvoice);
+    // used to create a new invoice
+    public DefaultInvoice(UUID accountId, DateTime invoiceDate, DateTime targetDate, Currency currency) {
+        this(UUID.randomUUID(), accountId, null, invoiceDate, targetDate, currency, false, null, null);
     }
 
-    public DefaultInvoice(UUID invoiceId, UUID accountId, @Nullable Integer invoiceNumber, DateTime invoiceDate, DateTime targetDate,
-                          Currency currency) {
-    	this(invoiceId, accountId, invoiceNumber, invoiceDate, targetDate, currency, false);
-    }
-    
-    public DefaultInvoice(UUID invoiceId, UUID accountId, @Nullable Integer invoiceNumber,DateTime invoiceDate, DateTime targetDate,
-                Currency currency, boolean migrationInvoice) {
-        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, boolean isMigrationInvoice, @Nullable String createdBy, @Nullable DateTime createdDate) {
+        super(invoiceId, createdBy, createdDate);
         this.accountId = accountId;
         this.invoiceNumber = invoiceNumber;
         this.invoiceDate = invoiceDate;
         this.targetDate = targetDate;
         this.currency = currency;
-        this.migrationInvoice = migrationInvoice;
+        this.migrationInvoice = isMigrationInvoice;
     }
 
     @Override
@@ -215,5 +208,24 @@ public class DefaultInvoice implements Invoice {
         return "DefaultInvoice [items=" + invoiceItems + ", payments=" + payments + ", id=" + id + ", accountId=" + accountId + ", invoiceDate=" + invoiceDate + ", targetDate=" + targetDate + ", currency=" + currency + ", amountPaid=" + getAmountPaid() + ", lastPaymentAttempt=" + getLastPaymentAttempt() + "]";
     }
 
+    @Override
+    public String getObjectName() {
+        return Invoice.ObjectType;
+    }
+
+    @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();
+    }
 }
 
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..389010c 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,9 +79,9 @@ 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);
+        List<InvoiceItem> proposedItems = generateInvoiceItems(invoiceId, accountId, events, targetDate, targetCurrency);
 
         removeCancellingInvoiceItems(existingItems);
         removeDuplicatedInvoiceItems(proposedItems, existingItems);
@@ -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());
@@ -164,7 +165,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
         }
     }
 
-    private List<InvoiceItem> generateInvoiceItems(final UUID invoiceId, final BillingEventSet events,
+    private List<InvoiceItem> generateInvoiceItems(final UUID invoiceId, final UUID accountId, final BillingEventSet events,
                                                    final DateTime targetDate, final Currency currency) throws InvoiceApiException {
         List<InvoiceItem> items = new ArrayList<InvoiceItem>();
 
@@ -175,16 +176,16 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
                 nextEvent = (thisEvent.getSubscription().getId() == nextEvent.getSubscription().getId()) ? nextEvent : null;
             }
 
-            items.addAll(processEvents(invoiceId, thisEvent, nextEvent, targetDate, currency));
+            items.addAll(processEvents(invoiceId, accountId, thisEvent, nextEvent, targetDate, currency));
         }
 
         return items;
     }
 
-    private List<InvoiceItem> processEvents(final UUID invoiceId, final BillingEvent thisEvent, final BillingEvent nextEvent,
+    private List<InvoiceItem> processEvents(final UUID invoiceId, final UUID accountId, final BillingEvent thisEvent, final BillingEvent nextEvent,
                                             final DateTime targetDate, final Currency currency) throws InvoiceApiException {
         List<InvoiceItem> items = new ArrayList<InvoiceItem>();
-        InvoiceItem fixedPriceInvoiceItem = generateFixedPriceItem(invoiceId, thisEvent, targetDate, currency);
+        InvoiceItem fixedPriceInvoiceItem = generateFixedPriceItem(invoiceId, accountId, thisEvent, targetDate, currency);
         if (fixedPriceInvoiceItem != null) {
             items.add(fixedPriceInvoiceItem);
         }
@@ -210,11 +211,12 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
                     if (rate != null) {
                         BigDecimal amount = itemDatum.getNumberOfCycles().multiply(rate).setScale(NUMBER_OF_DECIMALS, ROUNDING_MODE);
 
-                        RecurringInvoiceItem recurringItem = new RecurringInvoiceItem(invoiceId, thisEvent.getSubscription().getId(),
+                        RecurringInvoiceItem recurringItem = new RecurringInvoiceItem(invoiceId, accountId,
+                                thisEvent.getSubscription().getId(),
                                 thisEvent.getPlan().getName(),
                                 thisEvent.getPlanPhase().getName(),
                                 itemDatum.getStartDate(), itemDatum.getEndDate(),
-                                amount, rate, currency, clock.getUTCNow());
+                                amount, rate, currency);
                         items.add(recurringItem);
                     }
                 }
@@ -233,7 +235,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
         }
     }
 
-    private InvoiceItem generateFixedPriceItem(final UUID invoiceId, final BillingEvent thisEvent,
+    private InvoiceItem generateFixedPriceItem(final UUID invoiceId, final UUID accountId, final BillingEvent thisEvent,
                                                final DateTime targetDate, final Currency currency) throws InvoiceApiException {
         if (thisEvent.getEffectiveDate().isAfter(targetDate)) {
             return null;
@@ -244,10 +246,9 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
                 Duration duration = thisEvent.getPlanPhase().getDuration();
                 DateTime endDate = duration.addToDateTime(thisEvent.getEffectiveDate());
 
-                return new FixedPriceInvoiceItem(invoiceId, thisEvent.getSubscription().getId(),
+                return new FixedPriceInvoiceItem(invoiceId, accountId, 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/DefaultInvoicePayment.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoicePayment.java
index a0f518a..22eb5e6 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoicePayment.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoicePayment.java
@@ -18,47 +18,38 @@ package com.ning.billing.invoice.model;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.util.entity.EntityBase;
 import org.joda.time.DateTime;
 
 import javax.annotation.Nullable;
 import java.math.BigDecimal;
 import java.util.UUID;
 
-public class DefaultInvoicePayment implements InvoicePayment {
+public class DefaultInvoicePayment extends EntityBase implements InvoicePayment {
     private final UUID paymentAttemptId;
     private final UUID invoiceId;
     private final DateTime paymentDate;
     private final BigDecimal amount;
     private final Currency currency;
-    private final DateTime createdDate;
-
-    public DefaultInvoicePayment(final UUID invoiceId, final DateTime paymentDate) {
-        this(UUID.randomUUID(), invoiceId, paymentDate, null, null, null);
-    }
 
     public DefaultInvoicePayment(final UUID paymentAttemptId, final UUID invoiceId, final DateTime paymentDate) {
-        this(paymentAttemptId, invoiceId, paymentDate, null, null, null);
-    }
-
-    public DefaultInvoicePayment(final UUID invoiceId, final DateTime paymentDate,
-                                 final BigDecimal amount, final Currency currency) {
-        this(UUID.randomUUID(), invoiceId, paymentDate, amount, currency, null);
+        this(UUID.randomUUID(), paymentAttemptId, invoiceId, paymentDate, null, null, null, null);
     }
 
     public DefaultInvoicePayment(final UUID paymentAttemptId, final UUID invoiceId, final DateTime paymentDate,
                                  final BigDecimal amount, final Currency currency) {
-        this(paymentAttemptId, invoiceId, paymentDate, amount, currency, null);
+        this(UUID.randomUUID(), paymentAttemptId, invoiceId, paymentDate, amount, currency, null, null);
     }
 
-    public DefaultInvoicePayment(final UUID paymentAttemptId, final UUID invoiceId, final DateTime paymentDate,
+    public DefaultInvoicePayment(final UUID id, final UUID paymentAttemptId, final UUID invoiceId, final DateTime paymentDate,
                                  @Nullable final BigDecimal amount, @Nullable final Currency currency,
-                                 @Nullable final DateTime createdDate) {
+                                 @Nullable final String createdBy, @Nullable final DateTime createdDate) {
+        super(id, createdBy, createdDate);
         this.paymentAttemptId = paymentAttemptId;
         this.amount = amount;
         this.invoiceId = invoiceId;
         this.paymentDate = paymentDate;
         this.currency = currency;
-        this.createdDate = (createdDate == null) ? new DateTime() : createdDate;
     }
 
     @Override
@@ -85,9 +76,4 @@ public class DefaultInvoicePayment implements InvoicePayment {
     public Currency getCurrency() {
         return currency;
     }
-
-    @Override
-    public DateTime getCreatedDate() {
-        return createdDate;
-    }
 }
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..91e49fa 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
@@ -24,16 +24,15 @@ import java.math.BigDecimal;
 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);
+    public FixedPriceInvoiceItem(UUID invoiceId, UUID accountId, UUID subscriptionId, String planName, String phaseName,
+                                 DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
+        super(invoiceId, accountId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
     }
 
-    public FixedPriceInvoiceItem(UUID id, UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
+    public FixedPriceInvoiceItem(UUID id, UUID invoiceId, UUID accountId, 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, accountId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, createdBy, createdDate);
     }
 
     @Override
@@ -48,13 +47,14 @@ public class FixedPriceInvoiceItem extends InvoiceItemBase {
 
     @Override
     public int hashCode() {
-        int result = subscriptionId != null ? subscriptionId.hashCode() : 0;
+        int result = accountId.hashCode();
+        result = 31 * result + (subscriptionId != null ? subscriptionId.hashCode() : 0);
         result = 31 * result + (planName != null ? planName.hashCode() : 0);
         result = 31 * result + (phaseName != null ? phaseName.hashCode() : 0);
         result = 31 * result + (startDate != null ? startDate.hashCode() : 0);
         result = 31 * result + (endDate != null ? endDate.hashCode() : 0);
-        result = 31 * result + (amount != null ? amount.hashCode() : 0);
-        result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + amount.hashCode();
+        result = 31 * result + currency.hashCode();
         return result;
     }
 
@@ -65,12 +65,16 @@ public class FixedPriceInvoiceItem extends InvoiceItemBase {
         }
 
         FixedPriceInvoiceItem that = (FixedPriceInvoiceItem) item;
-        int compareSubscriptions = getSubscriptionId().compareTo(that.getSubscriptionId());
-
-        if (compareSubscriptions == 0) {
-            return getStartDate().compareTo(that.getStartDate());
+        int compareAccounts = getAccountId().compareTo(that.getAccountId());
+        if (compareAccounts == 0) {
+            int compareSubscriptions = getSubscriptionId().compareTo(that.getSubscriptionId());
+            if (compareSubscriptions == 0) {
+                return getStartDate().compareTo(that.getStartDate());
+            } else {
+                return compareSubscriptions;
+            }
         } else {
-            return compareSubscriptions;
+            return compareAccounts;
         }
     }
 
@@ -79,6 +83,7 @@ public class FixedPriceInvoiceItem extends InvoiceItemBase {
         StringBuilder sb = new StringBuilder();
         sb.append("InvoiceItem = {").append("id = ").append(id.toString()).append(", ");
         sb.append("invoiceId = ").append(invoiceId.toString()).append(", ");
+        sb.append("accountId = ").append(accountId.toString()).append(", ");
         sb.append("subscriptionId = ").append(subscriptionId.toString()).append(", ");
         sb.append("planName = ").append(planName).append(", ");
         sb.append("phaseName = ").append(phaseName).append(", ");
@@ -102,15 +107,15 @@ public class FixedPriceInvoiceItem extends InvoiceItemBase {
         if (o == null || getClass() != o.getClass()) return false;
 
         FixedPriceInvoiceItem that = (FixedPriceInvoiceItem) o;
-
+        if (accountId.compareTo(that.accountId) != 0) return false;
+        if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null)
+            return false;
         if (amount != null ? amount.compareTo(that.amount) != 0 : that.amount != null) return false;
         if (currency != that.currency) return false;
         if (startDate != null ? startDate.compareTo(that.startDate) != 0 : that.startDate != null) return false;
         if (endDate != null ? endDate.compareTo(that.endDate) != 0 : that.endDate != null) return false;
         if (phaseName != null ? !phaseName.equals(that.phaseName) : that.phaseName != null) return false;
         if (planName != null ? !planName.equals(that.planName) : that.planName != null) return false;
-        if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null)
-            return false;
 
         return true;
     }
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..2f16468 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,14 +18,16 @@ 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 javax.annotation.Nullable;
 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 accountId;
     protected final UUID subscriptionId;
     protected final String planName;
     protected final String phaseName;
@@ -33,20 +35,19 @@ 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) {
-        this(UUID.randomUUID(), invoiceId, subscriptionId, planName, phaseName,
-                startDate, endDate, amount, currency, createdDate);
+    public InvoiceItemBase(UUID invoiceId, UUID accountId, @Nullable UUID subscriptionId, String planName, String phaseName,
+                           DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
+        this(UUID.randomUUID(), invoiceId, accountId,  subscriptionId, planName, phaseName,
+                startDate, endDate, amount, currency, null, null);
     }
 
-    public InvoiceItemBase(UUID id, UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
+    public InvoiceItemBase(UUID id, UUID invoiceId, UUID accountId, @Nullable UUID subscriptionId, String planName, String phaseName,
                            DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency,
-                           DateTime createdDate) {
-        this.id = id;
+                           @Nullable String createdBy, @Nullable DateTime createdDate) {
+        super(id, createdBy, createdDate);
         this.invoiceId = invoiceId;
+        this.accountId = accountId;
         this.subscriptionId = subscriptionId;
         this.planName = planName;
         this.phaseName = phaseName;
@@ -54,7 +55,6 @@ public abstract class InvoiceItemBase implements InvoiceItem {
         this.endDate = endDate;
         this.amount = amount;
         this.currency = currency;
-        this.createdDate = createdDate;
     }
 
     public DateTime getCreatedDate() {
@@ -72,6 +72,11 @@ public abstract class InvoiceItemBase implements InvoiceItem {
     }
 
     @Override
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    @Override
     public UUID getSubscriptionId() {
         return subscriptionId;
     }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoicingConfiguration.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoicingConfiguration.java
index 554dc62..7741f00 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InvoicingConfiguration.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoicingConfiguration.java
@@ -20,7 +20,7 @@ import java.math.BigDecimal;
 
 public class InvoicingConfiguration {
     private final static int roundingMethod = BigDecimal.ROUND_HALF_UP;
-    private final static int numberOfDecimals = 4;
+    private final static int numberOfDecimals = 2;
 
     public static int getRoundingMode() {
         return roundingMethod;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/MigrationInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/MigrationInvoiceItem.java
index 3910ad7..01c1296 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/MigrationInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/MigrationInvoiceItem.java
@@ -23,15 +23,12 @@ import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.api.MigrationPlan;
-import com.ning.billing.util.clock.Clock;
 
 public class MigrationInvoiceItem extends FixedPriceInvoiceItem {
 	private final static UUID MIGRATION_SUBSCRIPTION_ID = UUID.fromString("ed25f954-3aa2-4422-943b-c3037ad7257c"); //new UUID(0L,0L);
 
-	public MigrationInvoiceItem(UUID invoiceId, DateTime startDate, BigDecimal amount, Currency currency, Clock clock) {
-		super(invoiceId, MIGRATION_SUBSCRIPTION_ID, MigrationPlan.MIGRATION_PLAN_NAME, MigrationPlan.MIGRATION_PLAN_PHASE_NAME, startDate, startDate,
-				amount, currency, clock.getUTCNow());
+	public MigrationInvoiceItem(UUID invoiceId, UUID accountId, DateTime startDate, BigDecimal amount, Currency currency) {
+		super(invoiceId, accountId, MIGRATION_SUBSCRIPTION_ID, MigrationPlan.MIGRATION_PLAN_NAME, MigrationPlan.MIGRATION_PLAN_PHASE_NAME,
+              startDate, startDate, amount, currency);
 	}
-
-	
-}
+}
\ No newline at end of file
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..f63599d 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
@@ -27,41 +27,42 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
     private final BigDecimal rate;
     private final UUID reversedItemId;
 
-    public RecurringInvoiceItem(UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
+    public RecurringInvoiceItem(UUID invoiceId, UUID accountId, 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, accountId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+        this.rate = rate;
+        this.reversedItemId = null;
     }
 
-    public RecurringInvoiceItem(UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
+    public RecurringInvoiceItem(UUID invoiceId, UUID accountId, 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, accountId, 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,
+    public RecurringInvoiceItem(UUID id, UUID invoiceId, UUID accountId, 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, accountId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, createdBy, createdDate);
 
         this.rate = rate;
         this.reversedItemId = null;
     }
 
-    public RecurringInvoiceItem(UUID id, UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
+    public RecurringInvoiceItem(UUID id, UUID invoiceId, UUID accountId, UUID subscriptionId, String planName, String phaseName,
                                 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, accountId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, createdBy, createdDate);
 
         this.rate = rate;
         this.reversedItemId = reversedItemId;
@@ -70,8 +71,8 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
     @Override
     public InvoiceItem asCredit() {
         BigDecimal amountNegated = amount == null ? null : amount.negate();
-        return new RecurringInvoiceItem(invoiceId, subscriptionId, planName, phaseName, startDate, endDate,
-                amountNegated, rate, currency, id, createdDate);
+        return new RecurringInvoiceItem(invoiceId, accountId, subscriptionId, planName, phaseName, startDate, endDate,
+                amountNegated, rate, currency, id);
     }
 
     @Override
@@ -101,17 +102,21 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
         }
 
         RecurringInvoiceItem that = (RecurringInvoiceItem) item;
-
-        int compareSubscriptions = getSubscriptionId().compareTo(that.getSubscriptionId());
-        if (compareSubscriptions == 0) {
-            int compareStartDates = getStartDate().compareTo(that.getStartDate());
-            if (compareStartDates == 0) {
-                return getEndDate().compareTo(that.getEndDate());
+        int compareAccounts = getAccountId().compareTo(that.getAccountId());
+        if (compareAccounts == 0) {
+            int compareSubscriptions = getSubscriptionId().compareTo(that.getSubscriptionId());
+            if (compareSubscriptions == 0) {
+                int compareStartDates = getStartDate().compareTo(that.getStartDate());
+                if (compareStartDates == 0) {
+                    return getEndDate().compareTo(that.getEndDate());
+                } else {
+                    return compareStartDates;
+                }
             } else {
-                return compareStartDates;
+                return compareSubscriptions;
             }
         } else {
-            return compareSubscriptions;
+            return compareAccounts;
         }
     }
 
@@ -122,6 +127,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
 
         RecurringInvoiceItem that = (RecurringInvoiceItem) o;
 
+        if (accountId.compareTo(that.accountId) != 0) return false;
         if (amount.compareTo(that.amount) != 0) return false;
         if (currency != that.currency) return false;
         if (startDate.compareTo(that.startDate) != 0) return false;
@@ -138,8 +144,8 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
 
     @Override
     public int hashCode() {
-        int result = invoiceId.hashCode();
-        result = 31 * result + subscriptionId.hashCode();
+        int result = accountId.hashCode();
+        result = 31 * result + (subscriptionId != null ? subscriptionId.hashCode() : 0);
         result = 31 * result + planName.hashCode();
         result = 31 * result + phaseName.hashCode();
         result = 31 * result + startDate.hashCode();
@@ -161,36 +167,5 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
         sb.append(amount.toString()).append(", ");
 
         return sb.toString();
-
-//        StringBuilder sb = new StringBuilder();
-//        sb.append("InvoiceItem = {").append("id = ").append(id.toString()).append(", ");
-//        sb.append("invoiceId = ").append(invoiceId.toString()).append(", ");
-//        sb.append("subscriptionId = ").append(subscriptionId.toString()).append(", ");
-//        sb.append("planName = ").append(planName).append(", ");
-//        sb.append("phaseName = ").append(phaseName).append(", ");
-//        sb.append("startDate = ").append(startDate.toString()).append(", ");
-//        if (endDate != null) {
-//            sb.append("endDate = ").append(endDate.toString()).append(", ");
-//        } else {
-//            sb.append("endDate = null");
-//        }
-//        sb.append("recurringAmount = ");
-//        if (amount == null) {
-//            sb.append("null");
-//        } else {
-//            sb.append(amount.toString());
-//        }
-//        sb.append(", ");
-//
-//        sb.append("recurringRate = ");
-//        if (rate == null) {
-//            sb.append("null");
-//        } else {
-//            sb.append(rate.toString());
-//        }
-//        sb.append(", ");
-//
-//        sb.append("}");
-//        return sb.toString();
     }
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDateNotifier.java b/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDateNotifier.java
index 5107d71..03dc211 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDateNotifier.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDateNotifier.java
@@ -25,8 +25,8 @@ import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
 import com.ning.billing.config.InvoiceConfig;
+import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.invoice.InvoiceListener;
 import com.ning.billing.invoice.api.DefaultInvoiceService;
 import com.ning.billing.util.bus.Bus;
@@ -45,17 +45,18 @@ public class DefaultNextBillingDateNotifier implements  NextBillingDateNotifier 
 
     private final NotificationQueueService notificationQueueService;
 	private final InvoiceConfig config;
-    private final EntitlementDao entitlementDao;
+	private final EntitlementUserApi entitlementUserApi;
 
+	
     private NotificationQueue nextBillingQueue;
 	private final InvoiceListener listener;
 
     @Inject
 	public DefaultNextBillingDateNotifier(NotificationQueueService notificationQueueService,
-			InvoiceConfig config, EntitlementDao entitlementDao, InvoiceListener listener){
+			InvoiceConfig config, EntitlementUserApi entitlementUserApi, InvoiceListener listener){
 		this.notificationQueueService = notificationQueueService;
 		this.config = config;
-        this.entitlementDao = entitlementDao;
+        this.entitlementUserApi = entitlementUserApi;
         this.listener = listener;
 	}
 
@@ -69,7 +70,7 @@ public class DefaultNextBillingDateNotifier implements  NextBillingDateNotifier 
                 public void handleReadyNotification(String notificationKey, DateTime eventDate) {
                 	try {
                  		UUID key = UUID.fromString(notificationKey);
-                        Subscription subscription = entitlementDao.getSubscriptionFromId(key);
+                        Subscription subscription = entitlementUserApi.getSubscriptionFromId(key);
                         if (subscription == null) {
                             log.warn("Next Billing Date Notification Queue handled spurious notification (key: " + key + ")" );
                         } else {
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.sql.stg
index 61f5e68..100182f 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.sql.stg
@@ -3,6 +3,7 @@ group FixedPriceInvoiceItemSqlDao;
 fields(prefix) ::= <<
   <prefix>id,
   <prefix>invoice_id,
+  <prefix>account_id,
   <prefix>subscription_id,
   <prefix>plan_name,
   <prefix>phase_name,
@@ -10,6 +11,7 @@ fields(prefix) ::= <<
   <prefix>end_date,
   <prefix>amount,
   <prefix>currency,
+  <prefix>created_by,
   <prefix>created_date
 >>
 
@@ -40,14 +42,14 @@ getInvoiceItemsBySubscription() ::= <<
 
 create() ::= <<
   INSERT INTO fixed_invoice_items(<fields()>)
-  VALUES(:id, :invoiceId, :subscriptionId, :planName, :phaseName,
-         :startDate, :endDate, :amount, :currency, :createdDate);
+  VALUES(:id, :invoiceId, :accountId, :subscriptionId, :planName, :phaseName,
+         :startDate, :endDate, :amount, :currency, :userName, :createdDate);
 >>
 
 batchCreateFromTransaction() ::= <<
   INSERT INTO fixed_invoice_items(<fields()>)
-  VALUES(:id, :invoiceId, :subscriptionId, :planName, :phaseName,
-         :startDate, :endDate, :amount, :currency, :createdDate);
+  VALUES(:id, :invoiceId, :accountId, :subscriptionId, :planName, :phaseName,
+         :startDate, :endDate, :amount, :currency, :userName, :createdDate);
 >>
 
 test() ::= <<
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
index 5e1586e..a673b81 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
@@ -1,22 +1,24 @@
 group InvoicePayment;
 
 invoicePaymentFields(prefix) ::= <<
+  <prefix>id,
   <prefix>invoice_id,
   <prefix>payment_attempt_id,
   <prefix>payment_attempt_date,
   <prefix>amount,
   <prefix>currency,
+  <prefix>created_by,
   <prefix>created_date
 >>
 
 create() ::= <<
   INSERT INTO invoice_payments(<invoicePaymentFields()>)
-  VALUES(:invoiceId, :paymentAttemptId, :paymentAttemptDate, :amount, :currency, :createdDate);
+  VALUES(:id, :invoiceId, :paymentAttemptId, :paymentAttemptDate, :amount, :currency, :userName, :createdDate);
 >>
 
 batchCreateFromTransaction() ::= <<
   INSERT INTO invoice_payments(<invoicePaymentFields()>)
-  VALUES(:invoiceId, :paymentAttemptId, :paymentAttemptDate, :amount, :currency, :createdDate);
+  VALUES(:id, :invoiceId, :paymentAttemptId, :paymentAttemptDate, :amount, :currency, :userName, :createdDate);
 >>
 
 getByPaymentAttemptId() ::= <<
@@ -38,7 +40,7 @@ getPaymentsForInvoice() ::= <<
 
 notifyOfPaymentAttempt() ::= <<
   INSERT INTO invoice_payments(<invoicePaymentFields()>)
-  VALUES(:invoiceId, :paymentAttemptId, :paymentAttemptDate, :amount, :currency, NOW());
+  VALUES(:id, :invoiceId, :paymentAttemptId, :paymentAttemptDate, :amount, :currency, :userName, :createdDate);
 >>
 
 getInvoicePayment() ::= <<
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 51347fb..b83dc82 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
@@ -7,7 +7,9 @@ invoiceFetchFields(prefix) ::= <<
     <prefix>invoice_date,
     <prefix>target_date,
     <prefix>currency,
-  	<prefix>migrated
+  	<prefix>migrated,
+    <prefix>created_by,
+    <prefix>created_date
 >>
 
 invoiceSetFields(prefix) ::= <<
@@ -16,7 +18,9 @@ invoiceSetFields(prefix) ::= <<
     <prefix>invoice_date,
     <prefix>target_date,
     <prefix>currency,
-    <prefix>migrated
+    <prefix>migrated,
+    <prefix>created_by,
+    <prefix>created_date
 >>
 
 get() ::= <<
@@ -72,7 +76,7 @@ getAccountBalance() ::= <<
 
 create() ::= <<
   INSERT INTO invoices(<invoiceSetFields()>)
-  VALUES (:id, :accountId, :invoiceDate, :targetDate, :currency, :migrated);
+  VALUES (:id, :accountId, :invoiceDate, :targetDate, :currency, :migrated, :userName, :createdDate);
 >>
 
 getInvoiceIdByPaymentAttemptId() ::= <<
@@ -93,7 +97,6 @@ getUnpaidInvoicesByAccountId() ::= <<
   ORDER BY i.target_date ASC;
 >>
 
-
 test() ::= <<
   SELECT 1
   FROM invoices;
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/RecurringInvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/RecurringInvoiceItemSqlDao.sql.stg
index e703d0b..6b5f504 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/RecurringInvoiceItemSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/RecurringInvoiceItemSqlDao.sql.stg
@@ -3,6 +3,7 @@ group RecurringInvoiceItemSqlDao;
 fields(prefix) ::= <<
   <prefix>id,
   <prefix>invoice_id,
+  <prefix>account_id,
   <prefix>subscription_id,
   <prefix>plan_name,
   <prefix>phase_name,
@@ -12,6 +13,7 @@ fields(prefix) ::= <<
   <prefix>rate,
   <prefix>currency,
   <prefix>reversed_item_id,
+  <prefix>created_by,
   <prefix>created_date
 >>
 
@@ -42,14 +44,14 @@ getInvoiceItemsBySubscription() ::= <<
 
 create() ::= <<
   INSERT INTO recurring_invoice_items(<fields()>)
-  VALUES(:id, :invoiceId, :subscriptionId, :planName, :phaseName, :startDate, :endDate,
-         :amount, :rate, :currency, :reversedItemId, :createdDate);
+  VALUES(:id, :invoiceId, :accountId, :subscriptionId, :planName, :phaseName, :startDate, :endDate,
+         :amount, :rate, :currency, :reversedItemId, :userName, :createdDate);
 >>
 
 batchCreateFromTransaction() ::= <<
   INSERT INTO recurring_invoice_items(<fields()>)
-  VALUES(:id, :invoiceId, :subscriptionId, :planName, :phaseName, :startDate, :endDate,
-         :amount, :rate, :currency, :reversedItemId, :createdDate);
+  VALUES(:id, :invoiceId, :accountId, :subscriptionId, :planName, :phaseName, :startDate, :endDate,
+         :amount, :rate, :currency, :reversedItemId, :userName, :createdDate);
 >>
 
 test() ::= <<
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 88f7595..3adb40e 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
@@ -3,6 +3,7 @@ DROP TABLE IF EXISTS recurring_invoice_items;
 CREATE TABLE recurring_invoice_items (
   id char(36) NOT NULL,
   invoice_id char(36) NOT NULL,
+  account_id char(36) NOT NULL,
   subscription_id char(36) NOT NULL,
   plan_name varchar(50) NOT NULL,
   phase_name varchar(50) NOT NULL,
@@ -12,6 +13,7 @@ CREATE TABLE recurring_invoice_items (
   rate numeric(10,4) NULL,
   currency char(3) NOT NULL,
   reversed_item_id char(36),
+  created_by varchar(50) NOT NULL,
   created_date datetime NOT NULL,
   PRIMARY KEY(id)
 ) ENGINE=innodb;
@@ -22,6 +24,7 @@ DROP TABLE IF EXISTS fixed_invoice_items;
 CREATE TABLE fixed_invoice_items (
   id char(36) NOT NULL,
   invoice_id char(36) NOT NULL,
+  account_id char(36) NOT NULL,
   subscription_id char(36) NOT NULL,
   plan_name varchar(50) NOT NULL,
   phase_name varchar(50) NOT NULL,
@@ -29,6 +32,7 @@ CREATE TABLE fixed_invoice_items (
   end_date datetime NOT NULL,
   amount numeric(10,4) NULL,
   currency char(3) NOT NULL,
+  created_by varchar(50) NOT NULL,
   created_date datetime NOT NULL,
   PRIMARY KEY(id)
 ) ENGINE=innodb;
@@ -46,6 +50,8 @@ CREATE TABLE invoices (
   target_date datetime NOT NULL,
   currency char(3) NOT NULL,
   migrated bool NOT NULL,
+  created_by varchar(50) NOT NULL,
+  created_date datetime NOT NULL,
   PRIMARY KEY(invoice_number)
 ) ENGINE=innodb;
 CREATE INDEX invoices_invoice_number ON invoices(invoice_number ASC);
@@ -54,11 +60,13 @@ CREATE INDEX invoices_account_id ON invoices(account_id ASC);
 
 DROP TABLE IF EXISTS invoice_payments;
 CREATE TABLE invoice_payments (
+  id char(36) NOT NULL,
   invoice_id char(36) NOT NULL,
   payment_attempt_id char(36) COLLATE utf8_bin NOT NULL,
   payment_attempt_date datetime,
   amount numeric(10,4),
   currency char(3),
+  created_by varchar(50) NOT NULL,
   created_date datetime NOT NULL,
   PRIMARY KEY(invoice_id, payment_attempt_id)
 ) ENGINE=innodb;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
index 23561a1..07c8e54 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
@@ -23,6 +23,11 @@ import java.util.SortedSet;
 import java.util.TreeSet;
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
+import com.ning.billing.util.clock.Clock;
 import org.apache.commons.io.IOUtils;
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
@@ -103,7 +108,7 @@ public class TestDefaultInvoiceMigrationApi {
 	private static final BigDecimal MIGRATION_INVOICE_AMOUNT =  new BigDecimal("100.00");
 	private static final Currency MIGRATION_INVOICE_CURRENCY =  Currency.USD;
 
-
+    private final Clock clock = new ClockMock();
 
 	@BeforeClass(alwaysRun = true)
 	public void setup() throws Exception
@@ -111,8 +116,8 @@ public class TestDefaultInvoiceMigrationApi {
 		log.info("Starting set up");
 		accountId = UUID.randomUUID();
 		subscriptionId = UUID.randomUUID();
-		date_migrated = new ClockMock().getUTCNow().minusYears(1);
-		date_regular = new ClockMock().getUTCNow();
+		date_migrated = clock.getUTCNow().minusYears(1);
+		date_regular = clock.getUTCNow();
 
 		final String invoiceDdl = IOUtils.toString(TestInvoiceDispatcher.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
 		final String utilDdl = IOUtils.toString(TestInvoiceDispatcher.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
@@ -182,13 +187,14 @@ public class TestDefaultInvoiceMigrationApi {
 
 		InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountUserApi, entitlementBillingApi, invoiceDao, locker);
 
-		Invoice invoice = dispatcher.processAccount(accountId, date_regular, true);
+        CallContext context = new DefaultCallContextFactory(clock).createCallContext("Migration test", CallOrigin.TEST, UserType.TEST);
+		Invoice invoice = dispatcher.processAccount(accountId, date_regular, true, context);
 		Assert.assertNotNull(invoice);
 
 		List<Invoice> invoices = invoiceDao.getInvoicesByAccount(accountId);
 		Assert.assertEquals(invoices.size(),0);
 
-		invoice = dispatcher.processAccount(accountId, date_regular, false);
+		invoice = dispatcher.processAccount(accountId, date_regular, false, context);
 		Assert.assertNotNull(invoice);
 
 		invoices = invoiceDao.getInvoicesByAccount(accountId);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java
index 3384274..3a00c1b 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.CopyOnWriteArrayList;
 
+import com.ning.billing.util.callcontext.CallContext;
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.Currency;
@@ -37,7 +38,7 @@ public class MockInvoicePaymentApi implements InvoicePaymentApi
     }
 
     @Override
-    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment) {
+    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment, CallContext context) {
         for (InvoicePayment existingInvoicePayment : invoicePayments) {
             if (existingInvoicePayment.getInvoiceId().equals(invoicePayment.getInvoiceId()) && existingInvoicePayment.getPaymentAttemptId().equals(invoicePayment.getPaymentAttemptId())) {
                 invoicePayments.remove(existingInvoicePayment);
@@ -89,15 +90,15 @@ public class MockInvoicePaymentApi implements InvoicePaymentApi
     }
 
     @Override
-    public void notifyOfPaymentAttempt(UUID invoiceId, BigDecimal amountOutstanding, Currency currency, UUID paymentAttemptId, DateTime paymentAttemptDate) {
+    public void notifyOfPaymentAttempt(UUID invoiceId, BigDecimal amountOutstanding, Currency currency, UUID paymentAttemptId, DateTime paymentAttemptDate, CallContext context) {
         InvoicePayment invoicePayment = new DefaultInvoicePayment(paymentAttemptId, invoiceId, paymentAttemptDate, amountOutstanding, currency);
-        notifyOfPaymentAttempt(invoicePayment);
+        notifyOfPaymentAttempt(invoicePayment, context);
     }
 
     @Override
-    public void notifyOfPaymentAttempt(UUID invoiceId, UUID paymentAttemptId, DateTime paymentAttemptDate) {
+    public void notifyOfPaymentAttempt(UUID invoiceId, UUID paymentAttemptId, DateTime paymentAttemptDate, CallContext context) {
         InvoicePayment invoicePayment = new DefaultInvoicePayment(paymentAttemptId, invoiceId, paymentAttemptDate);
-        notifyOfPaymentAttempt(invoicePayment);
+        notifyOfPaymentAttempt(invoicePayment, context);
     }
 
 }
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 9249420..60a4313 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.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
+import com.ning.billing.util.clock.Clock;
 import org.apache.commons.io.IOUtils;
 import org.skife.jdbi.v2.Handle;
 import org.skife.jdbi.v2.TransactionCallback;
@@ -42,19 +50,31 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
     protected RecurringInvoiceItemSqlDao recurringInvoiceItemDao;
     protected InvoicePaymentSqlDao invoicePaymentDao;
     protected InvoiceModuleWithEmbeddedDb module;
-    private BusService busService;
+    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"));
             final String invoiceDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
             final String entitlementDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
 
             module.startDb();
-            module.initDb(accountDdl);
             module.initDb(invoiceDdl);
             module.initDb(entitlementDdl);
 
@@ -66,8 +86,11 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
             recurringInvoiceItemDao = module.getInvoiceItemSqlDao();
 
             invoicePaymentDao = module.getInvoicePaymentSqlDao();
-            
-            busService = injector.getInstance(BusService.class);
+            clock = injector.getInstance(Clock.class);
+            context = new DefaultCallContextFactory(clock).createCallContext("Count Rogan", CallOrigin.TEST, UserType.TEST);
+            generator = new DefaultInvoiceGenerator(clock, invoiceConfig);
+
+            BusService busService = injector.getInstance(BusService.class);
             ((DefaultBusService) busService).startBus();
 
             assertTrue(true);
@@ -106,7 +129,6 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
 
     @AfterClass(alwaysRun = true)
     protected void tearDown() {
-    	((DefaultBusService) busService).stopBus();
         module.stopDb();
         assertTrue(true);
     }
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 ef70c33..ad329bc 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
@@ -37,14 +37,11 @@ import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.invoice.model.BillingEventSet;
 import com.ning.billing.invoice.model.DefaultInvoice;
-import com.ning.billing.invoice.model.DefaultInvoiceGenerator;
 import com.ning.billing.invoice.model.DefaultInvoicePayment;
-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 com.ning.billing.util.tag.ControlTagType;
 import org.joda.time.DateTime;
 import org.testng.annotations.Test;
 
@@ -55,23 +52,20 @@ import java.util.List;
 import java.util.UUID;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
 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 +81,16 @@ 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());
+        InvoiceItem invoiceItem = new RecurringInvoiceItem(invoiceId, accountId, subscriptionId,
+                "test plan", "test phase", startDate, endDate,
+                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);
@@ -107,7 +102,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         BigDecimal paymentAmount = new BigDecimal("11.00");
         UUID paymentAttemptId = UUID.randomUUID();
 
-        invoiceDao.notifyOfPaymentAttempt(new DefaultInvoicePayment(paymentAttemptId, invoiceId, clock.getUTCNow().plusDays(12), paymentAmount, Currency.USD));
+        invoiceDao.notifyOfPaymentAttempt(new DefaultInvoicePayment(paymentAttemptId, invoiceId, clock.getUTCNow().plusDays(12), paymentAmount, Currency.USD), context);
 
         Invoice retrievedInvoice = invoiceDao.getById(invoiceId);
         assertNotNull(retrievedInvoice);
@@ -127,14 +122,14 @@ 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.notifyOfPaymentAttempt(new DefaultInvoicePayment(paymentAttemptId, invoice.getId(), paymentAttemptDate, paymentAmount, Currency.USD));
+        invoiceDao.create(invoice, context);
+        invoiceDao.notifyOfPaymentAttempt(new DefaultInvoicePayment(paymentAttemptId, invoice.getId(), paymentAttemptDate, paymentAmount, Currency.USD), context);
 
         invoice = invoiceDao.getById(invoice.getId());
         assertEquals(invoice.getAmountPaid().compareTo(paymentAmount), 0);
@@ -146,30 +141,17 @@ 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.notifyOfPaymentAttempt(new DefaultInvoicePayment(invoice.getId(), paymentAttemptDate));
+        invoiceDao.create(invoice, context);
+        invoiceDao.notifyOfPaymentAttempt(new DefaultInvoicePayment(UUID.randomUUID(), invoice.getId(), paymentAttemptDate), context);
 
         invoice = invoiceDao.getById(invoice.getId());
         assertEquals(invoice.getLastPaymentAttempt().compareTo(paymentAttemptDate), 0);
     }
 
- 
-    private List<Invoice> getInvoicesDueForPaymentAttempt(final List<Invoice> invoices, final DateTime date) {
-        List<Invoice> invoicesDue = new ArrayList<Invoice>();
-
-        for (final Invoice invoice : invoices) {
-            if (invoice.isDueForPayment(date, NUMBER_OF_DAY_BETWEEN_RETRIES)) {
-                invoicesDue.add(invoice);
-            }
-        }
-
-        return invoicesDue;
-    }
-
     @Test
     public void testGetInvoicesBySubscription() {
         UUID accountId = UUID.randomUUID();
@@ -187,50 +169,50 @@ 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();
 
         DateTime startDate = new DateTime(2011, 3, 1, 0, 0, 0, 0);
         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);
+        RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoiceId1, accountId, subscriptionId1, "test plan", "test A", startDate, endDate,
+                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);
+        RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoiceId1, accountId, subscriptionId2, "test plan", "test B", startDate, endDate,
+                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);
+        RecurringInvoiceItem item3 = new RecurringInvoiceItem(invoiceId1, accountId, subscriptionId3, "test plan", "test C", startDate, endDate,
+                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);
+        RecurringInvoiceItem item4 = new RecurringInvoiceItem(invoiceId1, accountId, subscriptionId4, "test plan", "test D", startDate, endDate,
+                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();
 
         startDate = endDate;
         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);
+        RecurringInvoiceItem item5 = new RecurringInvoiceItem(invoiceId2, accountId, subscriptionId1, "test plan", "test phase A", startDate, endDate,
+                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);
+        RecurringInvoiceItem item6 = new RecurringInvoiceItem(invoiceId2, accountId, subscriptionId2, "test plan", "test phase B", startDate, endDate,
+                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);
+        RecurringInvoiceItem item7 = new RecurringInvoiceItem(invoiceId2, accountId, subscriptionId3, "test plan", "test phase C", startDate, endDate,
+                rate3, rate3, Currency.USD);
+        recurringInvoiceItemDao.create(item7, context);
 
         // check that each subscription returns the correct number of invoices
         List<Invoice> items1 = invoiceDao.getInvoicesBySubscription(subscriptionId1);
@@ -250,12 +232,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;
@@ -279,8 +261,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);
@@ -288,17 +270,17 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         BigDecimal rate1 = new BigDecimal("17.0");
         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);
+        RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice1.getId(), accountId, UUID.randomUUID(), "test plan", "test phase A", startDate,
+                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);
+        RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, UUID.randomUUID(), "test plan", "test phase B", startDate,
+                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);
-        invoicePaymentDao.create(payment);
+        InvoicePayment payment = new DefaultInvoicePayment(UUID.randomUUID(), invoice1.getId(), new DateTime(), payment1, Currency.USD);
+        invoicePaymentDao.create(payment, context);
 
         BigDecimal balance = invoiceDao.getAccountBalance(accountId);
         assertEquals(balance.compareTo(rate1.add(rate2).subtract(payment1)), 0);
@@ -308,8 +290,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);
@@ -317,13 +299,13 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         BigDecimal rate1 = new BigDecimal("17.0");
         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);
+        RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice1.getId(), accountId, UUID.randomUUID(), "test plan", "test phase A", startDate, 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);
+        RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, UUID.randomUUID(), "test plan", "test phase B", startDate, endDate,
+                rate2, rate2, Currency.USD);
+        recurringInvoiceItemDao.create(item2, context);
 
         BigDecimal balance = invoiceDao.getAccountBalance(accountId);
         assertEquals(balance.compareTo(rate1.add(rate2)), 0);
@@ -333,12 +315,12 @@ 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);
-        invoicePaymentDao.create(payment);
+        InvoicePayment payment = new DefaultInvoicePayment(UUID.randomUUID(), invoice1.getId(), new DateTime(), payment1, Currency.USD);
+        invoicePaymentDao.create(payment, context);
 
         BigDecimal balance = invoiceDao.getAccountBalance(accountId);
         assertEquals(balance.compareTo(BigDecimal.ZERO.subtract(payment1)), 0);
@@ -348,8 +330,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);
@@ -357,13 +339,13 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         BigDecimal rate1 = new BigDecimal("17.0");
         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);
+        RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice1.getId(), accountId, UUID.randomUUID(), "test plan", "test phase A", startDate, 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);
+        RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, UUID.randomUUID(), "test plan", "test phase B", startDate, endDate,
+                rate2, rate2, Currency.USD);
+        recurringInvoiceItemDao.create(item2, context);
 
         DateTime upToDate;
         Collection<Invoice> invoices;
@@ -377,17 +359,17 @@ 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);
 
         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);
+        RecurringInvoiceItem item3 = new RecurringInvoiceItem(invoice2.getId(), accountId, UUID.randomUUID(), "test plan", "test phase C", startDate2, endDate2,
+                rate3, rate3, Currency.USD);
+        recurringInvoiceItemDao.create(item3, context);
 
         upToDate = new DateTime(2011, 1, 1, 0, 0, 0, 0);
         invoices = invoiceDao.getUnpaidInvoicesByAccountId(accountId, upToDate);
@@ -449,8 +431,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);
@@ -510,7 +492,8 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         BillingEventSet events = new BillingEventSet();
         events.add(event1);
 
-        Invoice invoice1 = generator.generateInvoice(UUID.randomUUID(), events, null, effectiveDate1, Currency.USD);
+        UUID accountId = UUID.randomUUID();
+        Invoice invoice1 = generator.generateInvoice(accountId, events, null, effectiveDate1, Currency.USD);
         assertNotNull(invoice1);
         assertEquals(invoice1.getNumberOfItems(), 1);
         assertEquals(invoice1.getTotalAmount().compareTo(ZERO), 0);
@@ -524,7 +507,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
                 "testEvent2", 2L, SubscriptionTransitionType.CHANGE);
         events.add(event2);
 
-        Invoice invoice2 = generator.generateInvoice(UUID.randomUUID(), events, invoiceList, effectiveDate2, Currency.USD);
+        Invoice invoice2 = generator.generateInvoice(accountId, events, invoiceList, effectiveDate2, Currency.USD);
         assertNotNull(invoice2);
         assertEquals(invoice2.getNumberOfItems(), 1);
         assertEquals(invoice2.getTotalAmount().compareTo(cheapAmount), 0);
@@ -532,7 +515,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         invoiceList.add(invoice2);
 
         DateTime effectiveDate3 = effectiveDate2.plusMonths(1);
-        Invoice invoice3 = generator.generateInvoice(UUID.randomUUID(), events, invoiceList, effectiveDate3, Currency.USD);
+        Invoice invoice3 = generator.generateInvoice(accountId, events, invoiceList, effectiveDate3, Currency.USD);
         assertNotNull(invoice3);
         assertEquals(invoice3.getNumberOfItems(), 1);
         assertEquals(invoice3.getTotalAmount().compareTo(cheapAmount), 0);
@@ -581,7 +564,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);
@@ -618,7 +601,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());
 
@@ -628,8 +611,74 @@ 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());
     }
+
+    @Test
+    public void testAddingWrittenOffTag() throws InvoiceApiException {
+        Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
+        ((ZombieControl) subscription).addResult("getId", UUID.randomUUID());
+
+        Plan plan = BrainDeadProxyFactory.createBrainDeadProxyFor(Plan.class);
+        ((ZombieControl) plan).addResult("getName", "plan");
+
+        PlanPhase phase1 = BrainDeadProxyFactory.createBrainDeadProxyFor(PlanPhase.class);
+        ((ZombieControl) phase1).addResult("getName", "plan-phase1");
+
+        DateTime targetDate1 = clock.getUTCNow();
+        Currency currency = Currency.USD;
+
+        // create pseudo-random invoice
+        BillingEvent event1 = new DefaultBillingEvent(subscription, targetDate1, plan, phase1, null,
+                                                      TEN, currency,
+                                                      BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
+                                                      "testEvent1", 1L, SubscriptionTransitionType.CHANGE);
+        BillingEventSet events = new BillingEventSet();
+        events.add(event1);
+
+        Invoice invoice = generator.generateInvoice(UUID.randomUUID(), events, null, targetDate1, Currency.USD);
+        invoiceDao.create(invoice, context);
+
+        invoiceDao.addControlTag(ControlTagType.WRITTEN_OFF, invoice.getId(), context);
+
+        Invoice savedInvoice = invoiceDao.getById(invoice.getId());
+        assertTrue(savedInvoice.hasTag(ControlTagType.WRITTEN_OFF.toString()));
+    }
+
+    @Test
+    public void testRemoveWrittenOffTag() throws InvoiceApiException {
+        Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
+        ((ZombieControl) subscription).addResult("getId", UUID.randomUUID());
+
+        Plan plan = BrainDeadProxyFactory.createBrainDeadProxyFor(Plan.class);
+        ((ZombieControl) plan).addResult("getName", "plan");
+
+        PlanPhase phase1 = BrainDeadProxyFactory.createBrainDeadProxyFor(PlanPhase.class);
+        ((ZombieControl) phase1).addResult("getName", "plan-phase1");
+
+        DateTime targetDate1 = clock.getUTCNow();
+        Currency currency = Currency.USD;
+
+        // create pseudo-random invoice
+        BillingEvent event1 = new DefaultBillingEvent(subscription, targetDate1, plan, phase1, null,
+                                                      TEN, currency,
+                                                      BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
+                                                      "testEvent1", 1L, SubscriptionTransitionType.CHANGE);
+        BillingEventSet events = new BillingEventSet();
+        events.add(event1);
+
+        Invoice invoice = generator.generateInvoice(UUID.randomUUID(), events, null, targetDate1, Currency.USD);
+        invoiceDao.create(invoice, context);
+
+        invoiceDao.addControlTag(ControlTagType.WRITTEN_OFF, invoice.getId(), context);
+
+        Invoice savedInvoice = invoiceDao.getById(invoice.getId());
+        assertTrue(savedInvoice.hasTag(ControlTagType.WRITTEN_OFF.toString()));
+
+        invoiceDao.removeControlTag(ControlTagType.WRITTEN_OFF, invoice.getId(), context);
+        savedInvoice = invoiceDao.getById(invoice.getId());
+        assertFalse(savedInvoice.hasTag(ControlTagType.WRITTEN_OFF.toString()));
+    }
 }
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..464ef86 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,6 @@ 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.clock.Clock;
-import com.ning.billing.util.clock.DefaultClock;
 import org.joda.time.DateTime;
 import org.testng.annotations.Test;
 
@@ -34,20 +32,18 @@ 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 accountId = UUID.randomUUID();
         UUID invoiceId = UUID.randomUUID();
         UUID subscriptionId = UUID.randomUUID();
         DateTime startDate = new DateTime(2011, 10, 1, 0, 0, 0, 0);
         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);
+        RecurringInvoiceItem item = new RecurringInvoiceItem(invoiceId, accountId, subscriptionId, "test plan", "test phase", startDate, endDate,
+                rate, rate, Currency.USD);
+        recurringInvoiceItemDao.create(item, context);
 
         RecurringInvoiceItem thisItem = (RecurringInvoiceItem) recurringInvoiceItemDao.getById(item.getId().toString());
         assertNotNull(thisItem);
@@ -59,20 +55,23 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         assertEquals(thisItem.getAmount().compareTo(item.getRate()), 0);
         assertEquals(thisItem.getRate().compareTo(item.getRate()), 0);
         assertEquals(thisItem.getCurrency(), item.getCurrency());
-        assertEquals(thisItem.getCreatedDate().compareTo(item.getCreatedDate()), 0);
+        // created date is no longer set before persistence layer call
+        // assertEquals(thisItem.getCreatedDate().compareTo(item.getCreatedDate()), 0);
     }
 
     @Test(groups = "slow")
     public void testGetInvoiceItemsBySubscriptionId() {
+        UUID accountId = UUID.randomUUID();
         UUID subscriptionId = UUID.randomUUID();
         DateTime startDate = new DateTime(2011, 3, 1, 0, 0, 0, 0);
         BigDecimal rate = new BigDecimal("20.00");
 
         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);
+            RecurringInvoiceItem item = new RecurringInvoiceItem(invoiceId, accountId, subscriptionId,
+                    "test plan", "test phase", startDate.plusMonths(i), startDate.plusMonths(i + 1),
+                    rate, rate, Currency.USD);
+            recurringInvoiceItemDao.create(item, context);
         }
 
         List<InvoiceItem> items = recurringInvoiceItemDao.getInvoiceItemsBySubscription(subscriptionId.toString());
@@ -81,6 +80,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
 
     @Test(groups = "slow")
     public void testGetInvoiceItemsByInvoiceId() {
+        UUID accountId = UUID.randomUUID();
         UUID invoiceId = UUID.randomUUID();
         DateTime startDate = new DateTime(2011, 3, 1, 0, 0, 0, 0);
         BigDecimal rate = new BigDecimal("20.00");
@@ -88,9 +88,10 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         for (int i = 0; i < 5; i++) {
             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);
+            RecurringInvoiceItem item = new RecurringInvoiceItem(invoiceId, accountId, subscriptionId,
+                    "test plan", "test phase", startDate, startDate.plusMonths(1),
+                    amount, amount, Currency.USD);
+            recurringInvoiceItemDao.create(item, context);
         }
 
         List<InvoiceItem> items = recurringInvoiceItemDao.getInvoiceItemsByInvoice(invoiceId.toString());
@@ -101,18 +102,19 @@ 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);
         BigDecimal rate = new BigDecimal("20.00");
 
         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);
+        RecurringInvoiceItem item = new RecurringInvoiceItem(invoiceId, accountId, subscriptionId,
+                "test plan", "test phase", startDate, startDate.plusMonths(1),
+                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 031e02a..dec7697 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,7 +24,9 @@ import java.util.Map;
 import java.util.UUID;
 
 import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.bus.Bus;
+import com.ning.billing.util.tag.ControlTagType;
 import org.joda.time.DateTime;
 
 import com.google.inject.Inject;
@@ -43,7 +45,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);
         }
@@ -151,7 +153,7 @@ public class MockInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment) {
+    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment, CallContext context) {
         synchronized (monitor) {
             Invoice invoice = invoices.get(invoicePayment.getInvoiceId());
             if (invoice != null) {
@@ -199,4 +201,14 @@ public class MockInvoiceDao implements InvoiceDao {
 	        }
 	        return result;
 	}
+
+    @Override
+    public void addControlTag(ControlTagType controlTagType, UUID objectId, CallContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removeControlTag(ControlTagType controlTagType, UUID objectId, CallContext context) {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java
index 22fec14..fd15171 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java
@@ -23,8 +23,16 @@ import com.ning.billing.invoice.api.test.InvoiceTestApi;
 import com.ning.billing.invoice.api.test.DefaultInvoiceTestApi;
 import com.ning.billing.invoice.dao.InvoicePaymentSqlDao;
 import com.ning.billing.invoice.dao.RecurringInvoiceItemSqlDao;
+import com.ning.billing.invoice.notification.MockNextBillingDateNotifier;
+import com.ning.billing.invoice.notification.MockNextBillingDatePoster;
+import com.ning.billing.invoice.notification.NextBillingDateNotifier;
+import com.ning.billing.invoice.notification.NextBillingDatePoster;
 import com.ning.billing.mock.BrainDeadProxyFactory;
+import com.ning.billing.util.callcontext.CallContextFactory;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
+import com.ning.billing.util.glue.FieldStoreModule;
 import com.ning.billing.util.glue.GlobalLockerModule;
+import com.ning.billing.util.glue.TagStoreModule;
 import org.skife.jdbi.v2.IDBI;
 
 import com.ning.billing.account.api.AccountUserApi;
@@ -72,6 +80,12 @@ public class InvoiceModuleWithEmbeddedDb extends InvoiceModule {
     }
 
     @Override
+    protected void installNotifier() {
+        bind(NextBillingDateNotifier.class).to(MockNextBillingDateNotifier.class).asEagerSingleton();
+        bind(NextBillingDatePoster.class).to(MockNextBillingDatePoster.class).asEagerSingleton();
+    }
+
+    @Override
     public void configure() {
         loadSystemPropertiesFromClasspath("/resource.properties");
 
@@ -79,9 +93,13 @@ public class InvoiceModuleWithEmbeddedDb extends InvoiceModule {
         bind(IDBI.class).toInstance(dbi);
 
         bind(Clock.class).to(DefaultClock.class).asEagerSingleton();
+        bind(CallContextFactory.class).to(DefaultCallContextFactory.class).asEagerSingleton();
+                install(new FieldStoreModule());
+        install(new TagStoreModule());
+
         installNotificationQueue();
 //      install(new AccountModule());
-      bind(AccountUserApi.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class));
+        bind(AccountUserApi.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class));
         install(new CatalogModule());
         install(new EntitlementModule());
         install(new GlobalLockerModule());
@@ -89,7 +107,6 @@ public class InvoiceModuleWithEmbeddedDb extends InvoiceModule {
         super.configure();
 
         bind(InvoiceTestApi.class).to(DefaultInvoiceTestApi.class).asEagerSingleton();
-
         install(new BusModule());
     }
 
diff --git a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java
index 4be12cc..fe4feb4 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java
@@ -20,6 +20,10 @@ import com.ning.billing.invoice.dao.InvoiceDao;
 import com.ning.billing.invoice.dao.MockInvoiceDao;
 import com.ning.billing.util.globallocker.GlobalLocker;
 import com.ning.billing.util.globallocker.MockGlobalLocker;
+import com.ning.billing.util.glue.CallContextModule;
+import com.ning.billing.util.glue.FieldStoreModule;
+import com.ning.billing.util.glue.TagStoreModule;
+import org.skife.jdbi.v2.Call;
 
 
 public class InvoiceModuleWithMocks extends InvoiceModule {
@@ -49,8 +53,17 @@ public class InvoiceModuleWithMocks extends InvoiceModule {
     protected void installInvoiceService() {
 
     }
-    
+
+    @Override
     protected void installInvoiceMigrationApi() {
 
     }
+
+    @Override
+    public void configure() {
+        super.configure();
+
+        install(new FieldStoreModule());
+        //install(new TagStoreModule());
+    }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/MockModule.java b/invoice/src/test/java/com/ning/billing/invoice/MockModule.java
index a06808e..99f12b4 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/MockModule.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/MockModule.java
@@ -16,6 +16,10 @@
 
 package com.ning.billing.invoice;
 
+import com.ning.billing.util.callcontext.CallContextFactory;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
+import com.ning.billing.util.glue.FieldStoreModule;
+import com.ning.billing.util.glue.TagStoreModule;
 import org.skife.config.ConfigurationObjectFactory;
 import org.skife.jdbi.v2.IDBI;
 
@@ -36,15 +40,15 @@ import com.ning.billing.util.glue.NotificationQueueModule;
 
 
 public class MockModule extends AbstractModule {
-
-
     public static final String PLUGIN_NAME = "yoyo";
 
     @Override
     protected void configure() {
-
         bind(Clock.class).to(ClockMock.class).asEagerSingleton();
         bind(ClockMock.class).asEagerSingleton();
+        bind(CallContextFactory.class).to(DefaultCallContextFactory.class).asEagerSingleton();
+        install(new TagStoreModule());
+        install(new FieldStoreModule());
 
         final MysqlTestingHelper helper = new MysqlTestingHelper();
         bind(MysqlTestingHelper.class).toInstance(helper);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDateNotifier.java b/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDateNotifier.java
new file mode 100644
index 0000000..727d9d7
--- /dev/null
+++ b/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDateNotifier.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.invoice.notification;
+
+public class MockNextBillingDateNotifier implements NextBillingDateNotifier {
+    @Override
+    public void initialize() {
+        // do nothing
+    }
+
+    @Override
+    public void start() {
+        // do nothing
+    }
+
+    @Override
+    public void stop() {
+        // do nothing
+    }
+}
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 0b2a456..26df2af 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,32 @@ 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.account.api.AccountUserApi;
+import com.ning.billing.account.api.MockAccountUserApi;
+import com.ning.billing.entitlement.api.billing.DefaultEntitlementBillingApi;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
+import com.ning.billing.invoice.InvoiceDispatcher;
+import com.ning.billing.invoice.dao.DefaultInvoiceDao;
+import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.invoice.model.DefaultInvoiceGenerator;
+import com.ning.billing.invoice.model.InvoiceGenerator;
+import com.ning.billing.util.callcontext.CallContextFactory;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
+import com.ning.billing.util.customfield.dao.AuditedCustomFieldDao;
+import com.ning.billing.util.customfield.dao.CustomFieldDao;
+import com.ning.billing.util.globallocker.GlobalLocker;
+import com.ning.billing.util.globallocker.MySqlGlobalLocker;
+import com.ning.billing.util.tag.dao.AuditedTagDao;
+import com.ning.billing.util.tag.dao.TagDao;
 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.AfterClass;
 import org.testng.annotations.BeforeClass;
@@ -47,16 +61,12 @@ 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.DefaultEntitlementUserApi;
+import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 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;
@@ -70,24 +80,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(CallContextFactory factory, InvoiceDispatcher dispatcher) {
+			super(factory, dispatcher);
 		}
 
-
 		@Override
 		public void handleNextBillingDateEvent(UUID subscriptionId,
 				DateTime eventDateTime) {
@@ -112,19 +120,29 @@ public class TestNextBillingDateNotifier {
         final Injector g = Guice.createInjector(Stage.PRODUCTION,  new AbstractModule() {
 			@Override
             protected void configure() {
-				 bind(Clock.class).to(ClockMock.class).asEagerSingleton();
-				 bind(Bus.class).to(InMemoryBus.class).asEagerSingleton();
-				 bind(NotificationQueueService.class).to(DefaultNotificationQueueService.class).asEagerSingleton();
-				 final InvoiceConfig invoiceConfig = new ConfigurationObjectFactory(System.getProperties()).build(InvoiceConfig.class);
-				 bind(InvoiceConfig.class).toInstance(invoiceConfig);
-				 final CatalogConfig catalogConfig = new ConfigurationObjectFactory(System.getProperties()).build(CatalogConfig.class);
-                 bind(CatalogConfig.class).toInstance(catalogConfig);
-                 bind(CatalogService.class).to(DefaultCatalogService.class).asEagerSingleton();
-                 final MysqlTestingHelper helper = new MysqlTestingHelper();
-				 bind(MysqlTestingHelper.class).toInstance(helper);
-				 IDBI dbi = helper.getDBI();
-				 bind(IDBI.class).toInstance(dbi);
-                 bind(EntitlementDao.class).to(EntitlementSqlDao.class).asEagerSingleton();
+                bind(Clock.class).to(ClockMock.class).asEagerSingleton();
+                bind(CallContextFactory.class).to(DefaultCallContextFactory.class).asEagerSingleton();
+                bind(Bus.class).to(InMemoryBus.class).asEagerSingleton();
+                bind(NotificationQueueService.class).to(DefaultNotificationQueueService.class).asEagerSingleton();
+                final InvoiceConfig invoiceConfig = new ConfigurationObjectFactory(System.getProperties()).build(InvoiceConfig.class);
+                bind(InvoiceConfig.class).toInstance(invoiceConfig);
+                final CatalogConfig catalogConfig = new ConfigurationObjectFactory(System.getProperties()).build(CatalogConfig.class);
+                bind(CatalogConfig.class).toInstance(catalogConfig);
+                bind(CatalogService.class).to(DefaultCatalogService.class).asEagerSingleton();
+                final MysqlTestingHelper helper = new MysqlTestingHelper();
+                bind(MysqlTestingHelper.class).toInstance(helper);
+                IDBI dbi = helper.getDBI();
+                bind(IDBI.class).toInstance(dbi);
+                bind(TagDao.class).to(AuditedTagDao.class).asEagerSingleton();
+                bind(EntitlementDao.class).to(EntitlementSqlDao.class).asEagerSingleton();
+                bind(CustomFieldDao.class).to(AuditedCustomFieldDao.class).asEagerSingleton();
+                bind(GlobalLocker.class).to(MySqlGlobalLocker.class).asEagerSingleton();
+                bind(InvoiceGenerator.class).to(DefaultInvoiceGenerator.class).asEagerSingleton();
+                bind(InvoiceDao.class).to(DefaultInvoiceDao.class);
+                bind(NextBillingDatePoster.class).to(DefaultNextBillingDatePoster.class).asEagerSingleton();
+                bind(AccountUserApi.class).to(MockAccountUserApi.class).asEagerSingleton();
+                bind(EntitlementBillingApi.class).to(DefaultEntitlementBillingApi.class).asEagerSingleton();
+                bind(EntitlementUserApi.class).to(DefaultEntitlementUserApi.class).asEagerSingleton();                
 			}
         });
 
@@ -134,13 +152,15 @@ 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);
-        ((ZombieControl) entitlementDao).addResult("getSubscriptionFromId", subscription);
+        EntitlementUserApi entitlementUserApi = BrainDeadProxyFactory.createBrainDeadProxyFor(EntitlementUserApi.class);
+        ((ZombieControl) entitlementUserApi).addResult("getSubscriptionFromId", subscription);
 
-        notifier = new DefaultNextBillingDateNotifier(notificationQueueService,g.getInstance(InvoiceConfig.class), entitlementDao, listener);
+        CallContextFactory factory = new DefaultCallContextFactory(clock);
+        listener = new InvoiceListenerMock(factory, dispatcher);
+        notifier = new DefaultNextBillingDateNotifier(notificationQueueService,g.getInstance(InvoiceConfig.class), entitlementUserApi, listener);
         startMysql();
 	}
 
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 87b29ba..7008c90 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.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
+import com.ning.billing.util.clock.Clock;
 import org.apache.commons.io.IOUtils;
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
@@ -64,14 +69,17 @@ import com.ning.billing.util.globallocker.GlobalLocker;
 @Test(groups = "slow")
 @Guice(modules = {MockModule.class})
 public class TestInvoiceDispatcher {
-	Logger log = LoggerFactory.getLogger(TestInvoiceDispatcher.class);
+	private Logger log = LoggerFactory.getLogger(TestInvoiceDispatcher.class);
 
 	@Inject
-	InvoiceUserApi invoiceUserApi;
+	private InvoiceUserApi invoiceUserApi;
+
 	@Inject
 	private InvoiceGenerator generator;
+
 	@Inject
 	private InvoiceDao invoiceDao;
+
 	@Inject
 	private GlobalLocker locker;
 
@@ -79,18 +87,19 @@ public class TestInvoiceDispatcher {
 	private MysqlTestingHelper helper;
 
 	@Inject
-	NextBillingDateNotifier notifier;
+	private NextBillingDateNotifier notifier;
 
 	@Inject
 	private BusService busService;
 
+    @Inject
+    private Clock clock;
 
+    private CallContext context;
 
     @BeforeSuite(groups = "slow")
     public void setup() throws IOException
     {
-
-
 		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"));
 		final String utilDdl = IOUtils.toString(TestInvoiceDispatcher.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
@@ -103,6 +112,8 @@ public class TestInvoiceDispatcher {
 		notifier.initialize();
 		notifier.start();
 
+        context = new DefaultCallContextFactory(clock).createCallContext("Miracle Max", CallOrigin.TEST, UserType.TEST);
+
 		busService.getBus().start();
 	}
 
@@ -115,11 +126,12 @@ public class TestInvoiceDispatcher {
 		} catch (Exception e) {
 			log.warn("Failed to tearDown test properly ", e);
 		}
-        }
-	    @Test(groups={"slow"}, enabled=true)
-	    public void testDryrunInvoice() throws InvoiceApiException {
-	    	UUID accountId = UUID.randomUUID();
-	    	UUID subscriptionId = UUID.randomUUID();
+    }
+	    
+    @Test(groups={"slow"}, enabled=true)
+    public void testDryRunInvoice() throws InvoiceApiException {
+        UUID accountId = UUID.randomUUID();
+        UUID subscriptionId = UUID.randomUUID();
 
 		AccountUserApi accountUserApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
 		Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class);
@@ -147,21 +159,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);
+		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);
+		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..6e6b9b3 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
@@ -267,7 +294,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         // plan 3: change of term from monthly (BCD = 20) to annual (BCD = 31; immediate)
         // plan 4: change of plan, effective EOT, BCD = 7 (covers change of plan)
         // plan 5: addon to plan 2, with bill cycle alignment to plan; immediate cancellation
-
+        UUID accountId = UUID.randomUUID();
         UUID subscriptionId1 = UUID.randomUUID();
         UUID subscriptionId2 = UUID.randomUUID();
         UUID subscriptionId3 = UUID.randomUUID();
@@ -317,108 +344,108 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         // on 1/5/2011, create subscription 1 (trial)
         events.add(createBillingEvent(subscriptionId1, plan1StartDate, plan1, plan1Phase1, 5));
         expectedAmount = EIGHT;
-        testInvoiceGeneration(events, invoices, plan1StartDate, 1, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, plan1StartDate, 1, expectedAmount);
 
         // on 2/5/2011, invoice subscription 1 (trial)
         expectedAmount = EIGHT;
-        testInvoiceGeneration(events, invoices, buildDateTime(2011, 2, 5) , 1, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, buildDateTime(2011, 2, 5) , 1, expectedAmount);
 
         // on 3/5/2011, invoice subscription 1 (trial)
         expectedAmount = EIGHT;
-        testInvoiceGeneration(events, invoices, buildDateTime(2011, 3, 5), 1, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, buildDateTime(2011, 3, 5), 1, expectedAmount);
 
         // on 3/10/2011, create subscription 2 (trial)
         events.add(createBillingEvent(subscriptionId2, plan2StartDate, plan2, plan2Phase1, 10));
         expectedAmount = TWENTY;
-        testInvoiceGeneration(events, invoices, plan2StartDate, 1, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, plan2StartDate, 1, expectedAmount);
 
         // on 4/5/2011, invoice subscription 1 (discount)
         events.add(createBillingEvent(subscriptionId1, plan1PhaseChangeDate, plan1, plan1Phase2, 5));
         expectedAmount = TWELVE;
-        testInvoiceGeneration(events, invoices, plan1PhaseChangeDate, 1, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, plan1PhaseChangeDate, 1, expectedAmount);
 
         // on 4/10/2011, invoice subscription 2 (trial)
         expectedAmount = TWENTY;
-        testInvoiceGeneration(events, invoices, buildDateTime(2011, 4, 10), 1, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, buildDateTime(2011, 4, 10), 1, expectedAmount);
 
         // on 4/29/2011, cancel subscription 1
         events.add(createBillingEvent(subscriptionId1, plan1CancelDate, plan1, plan1Phase3, 5));
         expectedAmount = TWELVE.multiply(SIX.divide(THIRTY, NUMBER_OF_DECIMALS, ROUNDING_METHOD)).negate().setScale(NUMBER_OF_DECIMALS);
-        testInvoiceGeneration(events, invoices, plan1CancelDate, 2, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, plan1CancelDate, 2, expectedAmount);
 
         // on 5/10/2011, invoice subscription 2 (trial)
         expectedAmount = TWENTY;
-        testInvoiceGeneration(events, invoices, buildDateTime(2011, 5, 10), 1, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, buildDateTime(2011, 5, 10), 1, expectedAmount);
 
         // on 5/20/2011, create subscription 3 (monthly)
         events.add(createBillingEvent(subscriptionId3, plan3StartDate, plan3, plan3Phase1, 20));
         expectedAmount = TEN;
-        testInvoiceGeneration(events, invoices, plan3StartDate, 1, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, plan3StartDate, 1, expectedAmount);
 
         // on 6/7/2011, create subscription 4
         events.add(createBillingEvent(subscriptionId4, plan4StartDate, plan4a, plan4aPhase1, 7));
         expectedAmount = FIFTEEN;
-        testInvoiceGeneration(events, invoices, plan4StartDate, 1, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, plan4StartDate, 1, expectedAmount);
 
         // on 6/10/2011, invoice subscription 2 (discount)
         events.add(createBillingEvent(subscriptionId2, plan2PhaseChangeToDiscountDate, plan2, plan2Phase2, 10));
         expectedAmount = THIRTY;
-        testInvoiceGeneration(events, invoices, plan2PhaseChangeToDiscountDate, 1, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, plan2PhaseChangeToDiscountDate, 1, expectedAmount);
 
         // on 6/20/2011, invoice subscription 3 (monthly)
         expectedAmount = TEN;
-        testInvoiceGeneration(events, invoices, buildDateTime(2011, 6, 20), 1, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, buildDateTime(2011, 6, 20), 1, expectedAmount);
 
         // on 6/21/2011, create add-on (subscription 5)
         events.add(createBillingEvent(subscriptionId5, plan5StartDate, plan5, plan5Phase1, 10));
         expectedAmount = TWENTY.multiply(NINETEEN).divide(THIRTY, NUMBER_OF_DECIMALS, ROUNDING_METHOD);
-        testInvoiceGeneration(events, invoices, plan5StartDate, 1, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, plan5StartDate, 1, expectedAmount);
 
         // on 7/7/2011, invoice subscription 4 (plan 1)
         expectedAmount = FIFTEEN;
-        testInvoiceGeneration(events, invoices, buildDateTime(2011, 7, 7), 1, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, buildDateTime(2011, 7, 7), 1, expectedAmount);
 
         // on 7/10/2011, invoice subscription 2 (discount), invoice subscription 5
         expectedAmount = THIRTY.add(TWENTY);
-        testInvoiceGeneration(events, invoices, buildDateTime(2011, 7, 10), 2, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, buildDateTime(2011, 7, 10), 2, expectedAmount);
 
         // on 7/20/2011, invoice subscription 3 (monthly)
         expectedAmount = TEN;
-        testInvoiceGeneration(events, invoices, buildDateTime(2011, 7, 20), 1, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, buildDateTime(2011, 7, 20), 1, expectedAmount);
 
         // on 7/31/2011, convert subscription 3 to annual
         events.add(createBillingEvent(subscriptionId3, plan3UpgradeToAnnualDate, plan3, plan3Phase2, 31));
         expectedAmount = ONE_HUNDRED.subtract(TEN);
         expectedAmount = expectedAmount.add(TEN.multiply(ELEVEN.divide(THIRTY_ONE, 2 * NUMBER_OF_DECIMALS, ROUNDING_METHOD)));
         expectedAmount = expectedAmount.setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
-        testInvoiceGeneration(events, invoices, plan3UpgradeToAnnualDate, 3, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, plan3UpgradeToAnnualDate, 3, expectedAmount);
 
         // on 8/7/2011, invoice subscription 4 (plan 2)
         events.add(createBillingEvent(subscriptionId4, plan4ChangeOfPlanDate, plan4b, plan4bPhase1, 7));
         expectedAmount = TWENTY_FOUR;
-        testInvoiceGeneration(events, invoices, plan4ChangeOfPlanDate, 1, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, plan4ChangeOfPlanDate, 1, expectedAmount);
 
         // on 8/10/2011, invoice plan 2 (discount), invoice subscription 5
         expectedAmount = THIRTY.add(TWENTY);
-        testInvoiceGeneration(events, invoices, buildDateTime(2011, 8, 10), 2, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, buildDateTime(2011, 8, 10), 2, expectedAmount);
 
         // on 9/7/2011, invoice subscription 4 (plan 2)
         expectedAmount = TWENTY_FOUR;
-        testInvoiceGeneration(events, invoices, buildDateTime(2011, 9, 7), 1, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, buildDateTime(2011, 9, 7), 1, expectedAmount);
 
         // on 9/10/2011, invoice plan 2 (evergreen), invoice subscription 5
         events.add(createBillingEvent(subscriptionId2, plan2PhaseChangeToEvergreenDate, plan2, plan2Phase3, 10));
         expectedAmount = FORTY.add(TWENTY);
-        testInvoiceGeneration(events, invoices, plan2PhaseChangeToEvergreenDate, 2, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, plan2PhaseChangeToEvergreenDate, 2, expectedAmount);
 
         // on 10/7/2011, invoice subscription 4 (plan 2), cancel subscription 5
         events.add(createBillingEvent(subscriptionId5, plan5CancelDate, plan5, plan5Phase2, 10));
         expectedAmount = TWENTY_FOUR.add(TWENTY.multiply(THREE.divide(THIRTY)).negate().setScale(NUMBER_OF_DECIMALS));
-        testInvoiceGeneration(events, invoices, plan5CancelDate, 3, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, plan5CancelDate, 3, expectedAmount);
 
         // on 10/10/2011, invoice plan 2 (evergreen)
         expectedAmount = FORTY ;
-        testInvoiceGeneration(events, invoices, buildDateTime(2011, 10, 10), 1, expectedAmount);
+        testInvoiceGeneration(accountId, events, invoices, buildDateTime(2011, 10, 10), 1, expectedAmount);
     }
 
     @Test
@@ -671,11 +698,10 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
                                        billCycleDay, BillingModeType.IN_ADVANCE, "Test", 1L, SubscriptionTransitionType.CREATE);
     }
 
-    private void testInvoiceGeneration(final BillingEventSet events, final List<Invoice> existingInvoices,
+    private void testInvoiceGeneration(final UUID accountId, final BillingEventSet events, final List<Invoice> existingInvoices,
                                        final DateTime targetDate, final int expectedNumberOfItems,
                                        final BigDecimal expectedAmount) throws InvoiceApiException {
         Currency currency = Currency.USD;
-        UUID accountId = UUID.randomUUID();
         Invoice invoice = generator.generateInvoice(accountId, events, existingInvoices, targetDate, currency);
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), expectedNumberOfItems);
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);

payment/pom.xml 2(+1 -1)

diff --git a/payment/pom.xml b/payment/pom.xml
index fc660f7..d55d8d9 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.8-SNAPSHOT</version>
+        <version>0.1.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-payment</artifactId>
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
index e12d963..7866ed3 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
@@ -23,6 +23,7 @@ import java.util.UUID;
 
 import javax.annotation.Nullable;
 
+import com.ning.billing.util.callcontext.CallContext;
 import org.apache.commons.lang.StringUtils;
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
@@ -48,6 +49,7 @@ public class DefaultPaymentApi implements PaymentApi {
     private final PaymentDao paymentDao;
     private final PaymentConfig config;
 
+
     private static final Logger log = LoggerFactory.getLogger(DefaultPaymentApi.class);
 
     @Inject
@@ -101,7 +103,7 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public Either<PaymentError, Void> updatePaymentGateway(String accountKey) {
+    public Either<PaymentError, Void> updatePaymentGateway(String accountKey, CallContext context) {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
         return plugin.updatePaymentGateway(accountKey);
     }
@@ -113,31 +115,31 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public Either<PaymentError, String> addPaymentMethod(@Nullable String accountKey, PaymentMethodInfo paymentMethod) {
+    public Either<PaymentError, String> addPaymentMethod(@Nullable String accountKey, PaymentMethodInfo paymentMethod, CallContext context) {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
         return plugin.addPaymentMethod(accountKey, paymentMethod);
     }
 
     @Override
-    public Either<PaymentError, Void> deletePaymentMethod(String accountKey, String paymentMethodId) {
+    public Either<PaymentError, Void> deletePaymentMethod(String accountKey, String paymentMethodId, CallContext context) {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
         return plugin.deletePaymentMethod(accountKey, paymentMethodId);
     }
 
     @Override
-    public Either<PaymentError, PaymentMethodInfo> updatePaymentMethod(String accountKey, PaymentMethodInfo paymentMethodInfo) {
+    public Either<PaymentError, PaymentMethodInfo> updatePaymentMethod(String accountKey, PaymentMethodInfo paymentMethodInfo, CallContext context) {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
         return plugin.updatePaymentMethod(accountKey, paymentMethodInfo);
     }
 
     @Override
-    public List<Either<PaymentError, PaymentInfo>> createPayment(String accountKey, List<String> invoiceIds) {
+    public List<Either<PaymentError, PaymentInfo>> createPayment(String accountKey, List<String> invoiceIds, CallContext context) {
         final Account account = accountUserApi.getAccountByKey(accountKey);
-        return createPayment(account, invoiceIds);
+        return createPayment(account, invoiceIds, context);
     }
 
     @Override
-    public Either<PaymentError, PaymentInfo> createPaymentForPaymentAttempt(UUID paymentAttemptId) {
+    public Either<PaymentError, PaymentInfo> createPaymentForPaymentAttempt(UUID paymentAttemptId, CallContext context) {
         PaymentAttempt paymentAttempt = paymentDao.getPaymentAttemptById(paymentAttemptId);
 
         if (paymentAttempt != null) {
@@ -159,8 +161,8 @@ public class DefaultPaymentApi implements PaymentApi {
                                                                          .setPaymentAttemptId(UUID.randomUUID())
                                                                          .build();
 
-                    paymentDao.createPaymentAttempt(newPaymentAttempt);
-                    return processPayment(getPaymentProviderPlugin(account), account, invoice, newPaymentAttempt);
+                    paymentDao.createPaymentAttempt(newPaymentAttempt, context);
+                    return processPayment(getPaymentProviderPlugin(account), account, invoice, newPaymentAttempt, context);
                 }
             }
         }
@@ -171,7 +173,7 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public List<Either<PaymentError, PaymentInfo>> createPayment(Account account, List<String> invoiceIds) {
+    public List<Either<PaymentError, PaymentInfo>> createPayment(Account account, List<String> invoiceIds, CallContext context) {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(account);
 
         List<Either<PaymentError, PaymentInfo>> processedPaymentsOrErrors = new ArrayList<Either<PaymentError, PaymentInfo>>(invoiceIds.size());
@@ -180,13 +182,8 @@ public class DefaultPaymentApi implements PaymentApi {
             Invoice invoice = invoicePaymentApi.getInvoice(UUID.fromString(invoiceId));
 
             if (invoice.getBalance().compareTo(BigDecimal.ZERO) <= 0 ) {
-                // TODO: send a notification that invoice was ignored?
-                log.info("Received invoice for payment with balance of 0 {} ", invoice);
-                Either<PaymentError, PaymentInfo> result = Either.left(new PaymentError("invoice_balance_0",
-                                                                                        "Invoice balance was 0 or less",
-                                                                                        account.getId(),
-                                                                                        UUID.fromString(invoiceId)));
-                processedPaymentsOrErrors.add(result);
+                log.debug("Received invoice for payment with balance of 0 {} ", invoice);
+
             }
             else if (invoice.isMigrationInvoice()) {
             	log.info("Received invoice for payment that is a migration invoice - don't know how to handle those yet: {}", invoice);
@@ -197,16 +194,17 @@ public class DefaultPaymentApi implements PaymentApi {
             			processedPaymentsOrErrors.add(result);
             }
             else {
-                PaymentAttempt paymentAttempt = paymentDao.createPaymentAttempt(invoice);
+                PaymentAttempt paymentAttempt = paymentDao.createPaymentAttempt(invoice, context);
 
-                processedPaymentsOrErrors.add(processPayment(plugin, account, invoice, paymentAttempt));
+                processedPaymentsOrErrors.add(processPayment(plugin, account, invoice, paymentAttempt, context));
             }
         }
 
         return processedPaymentsOrErrors;
     }
 
-    private Either<PaymentError, PaymentInfo> processPayment(PaymentProviderPlugin plugin, Account account, Invoice invoice, PaymentAttempt paymentAttempt) {
+    private Either<PaymentError, PaymentInfo> processPayment(PaymentProviderPlugin plugin, Account account, Invoice invoice,
+                                                             PaymentAttempt paymentAttempt, CallContext context) {
         Either<PaymentError, PaymentInfo> paymentOrError = plugin.processInvoice(account, invoice);
         PaymentInfo paymentInfo = null;
 
@@ -218,7 +216,7 @@ public class DefaultPaymentApi implements PaymentApi {
         }
         else {
             paymentInfo = paymentOrError.getRight();
-            paymentDao.savePaymentInfo(paymentInfo);
+            paymentDao.savePaymentInfo(paymentInfo, context);
 
             final String paymentMethodId = paymentInfo.getPaymentMethodId();
             log.debug("Fetching payment method info for payment method id " + ((paymentMethodId == null) ? "null" : paymentMethodId));
@@ -229,18 +227,18 @@ public class DefaultPaymentApi implements PaymentApi {
 
                 if (paymentMethodInfo instanceof CreditCardPaymentMethodInfo) {
                     CreditCardPaymentMethodInfo ccPaymentMethod = (CreditCardPaymentMethodInfo)paymentMethodInfo;
-                    paymentDao.updatePaymentInfo(ccPaymentMethod.getType(), paymentInfo.getPaymentId(), ccPaymentMethod.getCardType(), ccPaymentMethod.getCardCountry());
+                    paymentDao.updatePaymentInfo(ccPaymentMethod.getType(), paymentInfo.getPaymentId(), ccPaymentMethod.getCardType(), ccPaymentMethod.getCardCountry(), context);
                 }
                 else if (paymentMethodInfo instanceof PaypalPaymentMethodInfo) {
                     PaypalPaymentMethodInfo paypalPaymentMethodInfo = (PaypalPaymentMethodInfo)paymentMethodInfo;
-                    paymentDao.updatePaymentInfo(paypalPaymentMethodInfo.getType(), paymentInfo.getPaymentId(), null, null);
+                    paymentDao.updatePaymentInfo(paypalPaymentMethodInfo.getType(), paymentInfo.getPaymentId(), null, null, context);
                 }
             } else {
                 log.info(paymentMethodInfoOrError.getLeft().getMessage());
             }
 
             if (paymentInfo.getPaymentId() != null) {
-                paymentDao.updatePaymentAttemptWithPaymentId(paymentAttempt.getPaymentAttemptId(), paymentInfo.getPaymentId());
+                paymentDao.updatePaymentAttemptWithPaymentId(paymentAttempt.getPaymentAttemptId(), paymentInfo.getPaymentId(), context);
             }
         }
 
@@ -249,7 +247,8 @@ public class DefaultPaymentApi implements PaymentApi {
                                                                            paymentAttempt.getPaymentAttemptDate(),
                                                                            paymentInfo == null || paymentInfo.getStatus().equalsIgnoreCase("Error") ? null : paymentInfo.getAmount(),
 //                                                                         paymentInfo.getRefundAmount(), TODO
-                                                                           paymentInfo == null || paymentInfo.getStatus().equalsIgnoreCase("Error") ? null : invoice.getCurrency()));
+                                                                           paymentInfo == null || paymentInfo.getStatus().equalsIgnoreCase("Error") ? null : invoice.getCurrency()),
+                                                                           context);
 
         return paymentOrError;
     }
@@ -286,13 +285,13 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public Either<PaymentError, String> createPaymentProviderAccount(Account account) {
+    public Either<PaymentError, String> createPaymentProviderAccount(Account account, CallContext context) {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin((Account)null);
         return plugin.createPaymentProviderAccount(account);
     }
 
     @Override
-    public Either<PaymentError, Void> updatePaymentProviderAccountContact(String externalKey) {
+    public Either<PaymentError, Void> updatePaymentProviderAccountContact(String externalKey, CallContext context) {
     	Account account = accountUserApi.getAccountByKey(externalKey);
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(account);
         return plugin.updatePaymentProviderAccountExistingContact(account);
@@ -305,8 +304,8 @@ public class DefaultPaymentApi implements PaymentApi {
 
     @Override
     public List<Either<PaymentError, PaymentInfo>> createRefund(Account account, List<String> invoiceIds) {
-        //TODO
-        throw new UnsupportedOperationException();
+        final PaymentProviderPlugin plugin = getPaymentProviderPlugin(account);
+        return plugin.processRefund(account);
     }
 
     @Override
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
index 2184841..ce6adf6 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
@@ -22,22 +22,23 @@ import java.util.UUID;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.payment.api.PaymentAttempt;
 import com.ning.billing.payment.api.PaymentInfo;
+import com.ning.billing.util.callcontext.CallContext;
 
 public interface PaymentDao {
 
-    PaymentAttempt createPaymentAttempt(Invoice invoice);
-    PaymentAttempt createPaymentAttempt(PaymentAttempt paymentAttempt);
+    PaymentAttempt createPaymentAttempt(Invoice invoice, CallContext context);
+    PaymentAttempt createPaymentAttempt(PaymentAttempt paymentAttempt, CallContext context);
 
-    void savePaymentInfo(PaymentInfo right);
+    void savePaymentInfo(PaymentInfo right, CallContext context);
 
     PaymentAttempt getPaymentAttemptForPaymentId(String paymentId);
     List<PaymentAttempt> getPaymentAttemptsForInvoiceIds(List<String> invoiceIds);
 
-    void updatePaymentAttemptWithPaymentId(UUID paymentAttemptId, String paymentId);
+    void updatePaymentAttemptWithPaymentId(UUID paymentAttemptId, String paymentId, CallContext context);
 
     List<PaymentAttempt> getPaymentAttemptsForInvoiceId(String invoiceId);
 
-    void updatePaymentInfo(String paymentMethodType, String paymentId, String cardType, String cardCountry);
+    void updatePaymentInfo(String paymentMethodType, String paymentId, String cardType, String cardCountry, CallContext context);
 
     List<PaymentInfo> getPaymentInfo(List<String> invoiceIds);
 
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 6a53978..96d7e0b 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
@@ -19,20 +19,21 @@ package com.ning.billing.payment.dao;
 import java.math.BigDecimal;
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.sql.Timestamp;
-import java.util.Date;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.dao.BinderBase;
+import com.ning.billing.util.dao.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;
 import org.skife.jdbi.v2.sqlobject.Binder;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
-import org.skife.jdbi.v2.sqlobject.customizers.Mapper;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
 import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
 import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
@@ -45,60 +46,65 @@ import com.ning.billing.payment.api.PaymentAttempt;
 import com.ning.billing.payment.api.PaymentInfo;
 
 @ExternalizedSqlViaStringTemplate3()
+@RegisterMapper({PaymentSqlDao.PaymentAttemptMapper.class, PaymentSqlDao.PaymentInfoMapper.class})
 public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Transmogrifier {
     @SqlUpdate
-    void insertPaymentAttempt(@Bind(binder = PaymentAttemptBinder.class) PaymentAttempt paymentAttempt);
+    void insertPaymentAttempt(@Bind(binder = PaymentAttemptBinder.class) PaymentAttempt paymentAttempt,
+                              @CallContextBinder CallContext context);
+
+    @SqlUpdate
+    void insertPaymentAttemptHistory(@Bind("historyRecordId") final String historyRecordId,
+                                     @Bind(binder = PaymentAttemptBinder.class) final PaymentAttempt paymentAttempt,
+                                     @CallContextBinder final CallContext context);
 
     @SqlQuery
-    @Mapper(PaymentAttemptMapper.class)
     PaymentAttempt getPaymentAttemptForPaymentId(@Bind("payment_id") String paymentId);
 
     @SqlQuery
-    @Mapper(PaymentAttemptMapper.class)
     PaymentAttempt getPaymentAttemptById(@Bind("payment_attempt_id") String paymentAttemptId);
 
     @SqlQuery
-    @Mapper(PaymentAttemptMapper.class)
     List<PaymentAttempt> getPaymentAttemptsForInvoiceId(@Bind("invoice_id") String invoiceId);
 
     @SqlQuery
-    @Mapper(PaymentAttemptMapper.class)
     List<PaymentAttempt> getPaymentAttemptsForInvoiceIds(@BindIn("invoiceIds") List<String> invoiceIds);
 
     @SqlQuery
-    @Mapper(PaymentInfoMapper.class)
     PaymentInfo getPaymentInfoForPaymentAttemptId(@Bind("payment_attempt_id") String paymentAttemptId);
 
     @SqlUpdate
     void updatePaymentAttemptWithPaymentId(@Bind("payment_attempt_id") String paymentAttemptId,
                                            @Bind("payment_id") String paymentId,
-                                           @Bind("updated_dt") Date updatedDate);
+                                           @CallContextBinder CallContext context);
 
     @SqlUpdate
     void updatePaymentAttemptWithRetryInfo(@Bind("payment_attempt_id") String paymentAttemptId,
                                            @Bind("retry_count") int retryCount,
-                                           @Bind("updated_dt") Date updatedDate);
+                                           @CallContextBinder CallContext context);
 
     @SqlUpdate
     void updatePaymentInfo(@Bind("payment_method") String paymentMethod,
                            @Bind("payment_id") String paymentId,
                            @Bind("card_type") String cardType,
                            @Bind("card_country") String cardCountry,
-                           @Bind("updated_dt") Date updatedDate);
+                           @CallContextBinder CallContext context);
 
     @SqlQuery
-    @Mapper(PaymentInfoMapper.class)
-    List<PaymentInfo> getPaymentInfos(@BindIn("invoiceIds") List<String> invoiceIds);
+    List<PaymentInfo> getPaymentInfos(@BindIn("invoiceIds") final List<String> invoiceIds);
 
     @SqlUpdate
-    void insertPaymentInfo(@Bind(binder = PaymentInfoBinder.class) PaymentInfo paymentInfo);
+    void insertPaymentInfo(@Bind(binder = PaymentInfoBinder.class) final PaymentInfo paymentInfo,
+                           @CallContextBinder final CallContext context);
 
-    public static final class PaymentAttemptBinder implements Binder<Bind, PaymentAttempt> {
+    @SqlUpdate
+    void insertPaymentInfoHistory(@Bind("historyRecordId") final String historyRecordId,
+                                  @Bind(binder = PaymentInfoBinder.class) final PaymentInfo paymentInfo,
+                                  @CallContextBinder final CallContext context);
 
-        private Date getDate(DateTime dateTime) {
-            return dateTime == null ? null : dateTime.toDate();
-        }
+    @SqlQuery
+    PaymentInfo getPaymentInfo(@Bind("paymentId") final String paymentId);
 
+    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 +121,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 +151,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 +172,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/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
index 280d1e0..a23a4f7 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
@@ -108,4 +108,10 @@ public class NoOpPaymentProviderPlugin implements PaymentProviderPlugin {
         return Either.right(null);
     }
 
+    @Override
+    public List<Either<PaymentError, PaymentInfo>> processRefund(Account account) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/PaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/PaymentProviderPlugin.java
index 7a9ae71..fd9d160 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/PaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/PaymentProviderPlugin.java
@@ -42,5 +42,6 @@ public interface PaymentProviderPlugin {
 
     Either<PaymentError, Void> updatePaymentProviderAccountExistingContact(Account account);
     Either<PaymentError, Void> updatePaymentProviderAccountWithNewContact(Account account);
+    List<Either<PaymentError, PaymentInfo>> processRefund(Account account);
 
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java b/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java
index d53c236..e8034a1 100644
--- a/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java
@@ -19,6 +19,11 @@ package com.ning.billing.payment;
 import java.util.Arrays;
 import java.util.List;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.DefaultCallContext;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.clock.Clock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -40,14 +45,17 @@ public class RequestProcessor {
     private final AccountUserApi accountUserApi;
     private final PaymentApi paymentApi;
     private final Bus eventBus;
+    private final Clock clock;
 
     private static final Logger log = LoggerFactory.getLogger(RequestProcessor.class);
 
     @Inject
-    public RequestProcessor(AccountUserApi accountUserApi,
+    public RequestProcessor(Clock clock,
+                            AccountUserApi accountUserApi,
                             PaymentApi paymentApi,
                             PaymentProviderPluginRegistry pluginRegistry,
                             Bus eventBus) {
+        this.clock = clock;
         this.accountUserApi = accountUserApi;
         this.paymentApi = paymentApi;
         this.eventBus = eventBus;
@@ -63,7 +71,8 @@ public class RequestProcessor {
                 log.info("could not process invoice payment: could not find a valid account for event {}", event);
             }
             else {
-                List<Either<PaymentError, PaymentInfo>> results = paymentApi.createPayment(account, Arrays.asList(event.getInvoiceId().toString()));
+                CallContext context = new DefaultCallContext("PaymentRequestProcessor", CallOrigin.INTERNAL, UserType.SYSTEM, clock);
+                List<Either<PaymentError, PaymentInfo>> results = paymentApi.createPayment(account, Arrays.asList(event.getInvoiceId().toString()), context);
                 if (!results.isEmpty()) {
                     Either<PaymentError, PaymentInfo> result = results.get(0);
                     eventBus.post(result.isLeft() ? result.getLeft() : result.getRight());
diff --git a/payment/src/main/java/com/ning/billing/payment/RetryService.java b/payment/src/main/java/com/ning/billing/payment/RetryService.java
index 927df3f..d03b241 100644
--- a/payment/src/main/java/com/ning/billing/payment/RetryService.java
+++ b/payment/src/main/java/com/ning/billing/payment/RetryService.java
@@ -18,6 +18,11 @@ package com.ning.billing.payment;
 
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.DefaultCallContext;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.clock.Clock;
 import org.joda.time.DateTime;
 
 import com.google.inject.Inject;
@@ -39,15 +44,18 @@ public class RetryService implements KillbillService {
     public static final String SERVICE_NAME = "retry-service";
     public static final String QUEUE_NAME = "retry-events";
 
+    private final Clock clock;
     private final NotificationQueueService notificationQueueService;
     private final PaymentConfig config;
     private final PaymentApi paymentApi;
     private NotificationQueue retryQueue;
 
     @Inject
-    public RetryService(NotificationQueueService notificationQueueService,
+    public RetryService(Clock clock,
+                        NotificationQueueService notificationQueueService,
                         PaymentConfig config,
                         PaymentApi paymentApi) {
+        this.clock = clock;
         this.notificationQueueService = notificationQueueService;
         this.paymentApi = paymentApi;
         this.config = config;
@@ -63,7 +71,8 @@ public class RetryService implements KillbillService {
         retryQueue = notificationQueueService.createNotificationQueue(SERVICE_NAME, QUEUE_NAME, new NotificationQueueHandler() {
             @Override
             public void handleReadyNotification(String notificationKey, DateTime eventDateTime) {
-                retry(notificationKey);
+                CallContext context = new DefaultCallContext("RetryService", CallOrigin.INTERNAL, UserType.SYSTEM, clock);
+                retry(notificationKey, context);
             }
         },
         config);
@@ -93,7 +102,7 @@ public class RetryService implements KillbillService {
         retryQueue.recordFutureNotification(timeOfRetry, key);
     }
 
-    private void retry(String paymentAttemptId) {
+    private void retry(String paymentAttemptId, CallContext context) {
         PaymentInfo paymentInfo = paymentApi.getPaymentInfoForPaymentAttemptId(paymentAttemptId);
 
         if (paymentInfo != null && PaymentStatus.Processed.equals(PaymentStatus.valueOf(paymentInfo.getStatus()))) {
@@ -102,7 +111,7 @@ public class RetryService implements KillbillService {
         }
         else {
             System.out.println("Creating payment for payment attempt " + paymentAttemptId);
-            paymentApi.createPaymentForPaymentAttempt(UUID.fromString(paymentAttemptId));
+            paymentApi.createPaymentForPaymentAttempt(UUID.fromString(paymentAttemptId), context);
         }
     }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/setup/PaymentModule.java b/payment/src/main/java/com/ning/billing/payment/setup/PaymentModule.java
index d3d9320..0c769fd 100644
--- a/payment/src/main/java/com/ning/billing/payment/setup/PaymentModule.java
+++ b/payment/src/main/java/com/ning/billing/payment/setup/PaymentModule.java
@@ -26,7 +26,7 @@ import com.ning.billing.payment.RetryService;
 import com.ning.billing.payment.api.DefaultPaymentApi;
 import com.ning.billing.payment.api.PaymentApi;
 import com.ning.billing.payment.api.PaymentService;
-import com.ning.billing.payment.dao.DefaultPaymentDao;
+import com.ning.billing.payment.dao.AuditedPaymentDao;
 import com.ning.billing.payment.dao.PaymentDao;
 import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
 
@@ -42,7 +42,7 @@ public class PaymentModule extends AbstractModule {
     }
 
     protected void installPaymentDao() {
-        bind(PaymentDao.class).to(DefaultPaymentDao.class).asEagerSingleton();
+        bind(PaymentDao.class).to(AuditedPaymentDao.class).asEagerSingleton();
     }
 
     protected void installPaymentProviderPlugins(PaymentConfig config) {
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
index 55c3efa..87c8d8f 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
@@ -10,7 +10,9 @@ paymentAttemptFields(prefix) ::= <<
     <prefix>payment_attempt_dt,
     <prefix>invoice_dt,
     <prefix>retry_count,
+    <prefix>created_by,
     <prefix>created_dt,
+    <prefix>updated_by,
     <prefix>updated_dt
 >>
 
@@ -28,13 +30,22 @@ paymentInfoFields(prefix) ::= <<
     <prefix>card_type,
     <prefix>card_country,
     <prefix>effective_dt,
+    <prefix>created_by,
     <prefix>created_dt,
+    <prefix>updated_by,
     <prefix>updated_dt
 >>
 
 insertPaymentAttempt() ::= <<
     INSERT INTO payment_attempts (<paymentAttemptFields()>)
-    VALUES (:payment_attempt_id, :invoice_id, :account_id, :amount, :currency, :payment_id, :payment_attempt_dt, :invoice_dt, :retry_count, :created_dt, :updated_dt);
+    VALUES (:payment_attempt_id, :invoice_id, :account_id, :amount, :currency, :payment_id,
+            :payment_attempt_dt, :invoice_dt, :retry_count, :userName, :createdDate, :userName, :createdDate);
+>>
+
+insertPaymentAttemptHistory() ::= <<
+    INSERT INTO payment_attempt_history (history_record_id, <paymentAttemptFields()>)
+    VALUES (:historyRecordId, :payment_attempt_id, :invoice_id, :account_id, :amount, :currency, :payment_id,
+            :payment_attempt_dt, :invoice_dt, :retry_count, :userName, :createdDate, :userName, :createdDate);
 >>
 
 getPaymentAttemptForPaymentId() ::= <<
@@ -64,13 +75,23 @@ getPaymentAttemptsForInvoiceId() ::= <<
 updatePaymentAttemptWithPaymentId() ::= <<
     UPDATE payment_attempts
        SET payment_id = :payment_id,
-           updated_dt = :updated_dt
+           updated_by = :userName,
+           updated_dt = :updatedDate
      WHERE payment_attempt_id = :payment_attempt_id
 >>
 
 insertPaymentInfo() ::= <<
     INSERT INTO payments (<paymentInfoFields()>)
-    VALUES (:payment_id, :amount, :refund_amount, :bank_identification_number, :payment_number, :payment_type, :status, :reference_id, :payment_method_id, :payment_method, :card_type, :card_country, :effective_dt, :created_dt, :updated_dt);
+    VALUES (:payment_id, :amount, :refund_amount, :bank_identification_number, :payment_number,
+    :payment_type, :status, :reference_id, :payment_method_id, :payment_method, :card_type,
+    :card_country, :effective_dt, :userName, :createdDate, :userName, :createdDate);
+>>
+
+insertPaymentInfoHistory() ::= <<
+    INSERT INTO payment_history (history_record_id, <paymentInfoFields()>)
+    VALUES (:historyRecordId, :payment_id, :amount, :refund_amount, :bank_identification_number, :payment_number,
+    :payment_type, :status, :reference_id, :payment_method_id, :payment_method, :card_type,
+    :card_country, :effective_dt, :userName, :createdDate, :userName, :createdDate);
 >>
 
 updatePaymentInfo() ::= <<
@@ -78,7 +99,8 @@ updatePaymentInfo() ::= <<
        SET payment_method = :payment_method,
            card_type = :card_type,
            card_country = :card_country,
-           updated_dt = :updated_dt
+           updated_by = :userName,
+           updated_dt = :updatedDate
      WHERE payment_id = :payment_id
 >>
 
@@ -95,3 +117,9 @@ getPaymentInfoForPaymentAttemptId() ::= <<
      WHERE pa.payment_attempt_id = :payment_attempt_id
        AND pa.payment_id = p.payment_id
 >>
+
+getPaymentInfo() ::= <<
+    SELECT <paymentInfoFields()>
+    FROM payments
+    WHERE payment_id = :paymentId
+>>
diff --git a/payment/src/main/resources/com/ning/billing/payment/ddl.sql b/payment/src/main/resources/com/ning/billing/payment/ddl.sql
index 3d0cc1a..3a344f9 100644
--- a/payment/src/main/resources/com/ning/billing/payment/ddl.sql
+++ b/payment/src/main/resources/com/ning/billing/payment/ddl.sql
@@ -9,11 +9,32 @@ CREATE TABLE payment_attempts (
       payment_id varchar(36) COLLATE utf8_bin,
       retry_count tinyint,
       invoice_dt datetime NOT NULL,
+      created_by varchar(50) NOT NULL,
       created_dt datetime NOT NULL,
+      updated_by varchar(50) NOT NULL,
       updated_dt datetime NOT NULL,
       PRIMARY KEY (payment_attempt_id)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
 
+DROP TABLE IF EXISTS payment_attempt_history;
+CREATE TABLE payment_attempt_history (
+      history_record_id char(36) NOT NULL,
+      payment_attempt_id char(36) COLLATE utf8_bin NOT NULL,
+      account_id char(36) COLLATE utf8_bin NOT NULL,
+      invoice_id char(36) COLLATE utf8_bin NOT NULL,
+      amount decimal(8,2),
+      currency char(3),
+      payment_attempt_dt datetime NOT NULL,
+      payment_id varchar(36) COLLATE utf8_bin,
+      retry_count tinyint,
+      invoice_dt datetime NOT NULL,
+      created_by varchar(50) NOT NULL,
+      created_dt datetime NOT NULL,
+      updated_by varchar(50) NOT NULL,
+      updated_dt datetime NOT NULL,
+      PRIMARY KEY (history_record_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
+
 DROP TABLE IF EXISTS payments; 
 CREATE TABLE payments (
       payment_id varchar(36) COLLATE utf8_bin NOT NULL,
@@ -29,7 +50,32 @@ CREATE TABLE payments (
       card_type varchar(20) COLLATE utf8_bin,
       card_country varchar(50) COLLATE utf8_bin,
       effective_dt datetime,
+      created_by varchar(50) NOT NULL,
       created_dt datetime NOT NULL,
+      updated_by varchar(50) NOT NULL,
       updated_dt datetime NOT NULL,
       PRIMARY KEY (payment_id)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
+
+DROP TABLE IF EXISTS payment_history;
+CREATE TABLE payment_history (
+      history_record_id char(36) NOT NULL,
+      payment_id varchar(36) COLLATE utf8_bin NOT NULL,
+      amount decimal(8,2),
+      refund_amount decimal(8,2),
+      payment_number varchar(36) COLLATE utf8_bin,
+      bank_identification_number varchar(36) COLLATE utf8_bin,
+      status varchar(20) COLLATE utf8_bin,
+      reference_id varchar(36) COLLATE utf8_bin,
+      payment_type varchar(20) COLLATE utf8_bin,
+      payment_method_id varchar(36) COLLATE utf8_bin,
+      payment_method varchar(20) COLLATE utf8_bin,
+      card_type varchar(20) COLLATE utf8_bin,
+      card_country varchar(50) COLLATE utf8_bin,
+      effective_dt datetime,
+      created_by varchar(50) NOT NULL,
+      created_dt datetime NOT NULL,
+      updated_by varchar(50) NOT NULL,
+      updated_dt datetime NOT NULL,
+      PRIMARY KEY (history_record_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
diff --git a/payment/src/test/java/com/ning/billing/payment/api/TestMockPaymentApi.java b/payment/src/test/java/com/ning/billing/payment/api/TestMockPaymentApi.java
index b9f761a..7c005ee 100644
--- a/payment/src/test/java/com/ning/billing/payment/api/TestMockPaymentApi.java
+++ b/payment/src/test/java/com/ning/billing/payment/api/TestMockPaymentApi.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.payment.api;
 
+import com.google.inject.Inject;
+import com.ning.billing.util.clock.Clock;
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
@@ -26,5 +28,8 @@ import com.ning.billing.payment.setup.PaymentTestModuleWithMocks;
 @Guice(modules = { PaymentTestModuleWithMocks.class, AccountModuleWithMocks.class, InvoiceModuleWithMocks.class })
 @Test(groups = "fast")
 public class TestMockPaymentApi extends TestPaymentApi {
-
+    @Inject
+    public TestMockPaymentApi(Clock clock) {
+        super(clock);
+    }
 }
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 5d151b7..ea9b5d7 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
@@ -27,6 +27,11 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.DefaultCallContext;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.clock.Clock;
 import org.apache.commons.lang.RandomStringUtils;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
@@ -54,6 +59,13 @@ public abstract class TestPaymentApi {
     @Inject
     protected TestHelper testHelper;
 
+    protected CallContext context;
+
+    @Inject
+    public TestPaymentApi(Clock clock) {
+        context = new DefaultCallContext("Payment Tests", CallOrigin.INTERNAL, UserType.SYSTEM, clock);
+    }
+
     @BeforeMethod(alwaysRun = true)
     public void setUp() throws EventBusException {
         eventBus.start();
@@ -72,17 +84,16 @@ public abstract class TestPaymentApi {
         final BigDecimal amount = new BigDecimal("10.0011");
         final UUID subscriptionId = UUID.randomUUID();
 
-        invoice.addInvoiceItem(new RecurringInvoiceItem(invoice.getId(),
+        invoice.addInvoiceItem(new RecurringInvoiceItem(invoice.getId(), account.getId(),
                                                        subscriptionId,
                                                        "test plan", "test phase",
                                                        now,
                                                        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()));
+        List<Either<PaymentError, PaymentInfo>> results = paymentApi.createPayment(account.getExternalKey(), Arrays.asList(invoice.getId().toString()), context);
 
         assertEquals(results.size(), 1);
         assertTrue(results.get(0).isRight());
@@ -127,7 +138,7 @@ public abstract class TestPaymentApi {
 
     private PaymentProviderAccount setupAccountWithPaypalPaymentMethod() throws AccountApiException, EntityPersistenceException {
         final Account account = testHelper.createTestPayPalAccount();
-        paymentApi.createPaymentProviderAccount(account);
+        paymentApi.createPaymentProviderAccount(account, context);
 
         String accountKey = account.getExternalKey();
 
@@ -136,7 +147,7 @@ public abstract class TestPaymentApi {
                                                                            .setEmail(account.getEmail())
                                                                            .setDefaultMethod(true)
                                                                            .build();
-        Either<PaymentError, String> paymentMethodIdOrError = paymentApi.addPaymentMethod(accountKey, paymentMethod);
+        Either<PaymentError, String> paymentMethodIdOrError = paymentApi.addPaymentMethod(accountKey, paymentMethod, context);
 
         assertTrue(paymentMethodIdOrError.isRight());
         assertNotNull(paymentMethodIdOrError.getRight());
@@ -163,7 +174,7 @@ public abstract class TestPaymentApi {
     @Test(enabled=true)
     public void testUpdatePaymentProviderAccountContact() throws AccountApiException, EntityPersistenceException {
         final Account account = testHelper.createTestPayPalAccount();
-        paymentApi.createPaymentProviderAccount(account);
+        paymentApi.createPaymentProviderAccount(account, context);
 
         String newName = "Tester " + RandomStringUtils.randomAlphanumeric(10);
         String newNumber = "888-888-" + RandomStringUtils.randomNumeric(4);
@@ -178,7 +189,7 @@ public abstract class TestPaymentApi {
                                                                   .billingCycleDay(account.getBillCycleDay())
                                                                   .build();
 
-        Either<PaymentError, Void> voidOrError = paymentApi.updatePaymentProviderAccountContact(accountToUpdate.getExternalKey());
+        Either<PaymentError, Void> voidOrError = paymentApi.updatePaymentProviderAccountContact(accountToUpdate.getExternalKey(), context);
         assertTrue(voidOrError.isRight());
     }
 
@@ -186,7 +197,7 @@ public abstract class TestPaymentApi {
     public void testCannotDeleteDefaultPaymentMethod() throws AccountApiException, EntityPersistenceException {
         PaymentProviderAccount account = setupAccountWithPaypalPaymentMethod();
 
-        Either<PaymentError, Void> errorOrVoid = paymentApi.deletePaymentMethod(account.getAccountKey(), account.getDefaultPaymentMethodId());
+        Either<PaymentError, Void> errorOrVoid = paymentApi.deletePaymentMethod(account.getAccountKey(), account.getDefaultPaymentMethodId(), context);
 
         assertTrue(errorOrVoid.isLeft());
     }
@@ -194,19 +205,19 @@ public abstract class TestPaymentApi {
     @Test(enabled=true)
     public void testDeleteNonDefaultPaymentMethod() throws AccountApiException, EntityPersistenceException {
         final Account account = testHelper.createTestPayPalAccount();
-        paymentApi.createPaymentProviderAccount(account);
+        paymentApi.createPaymentProviderAccount(account, context);
 
         String accountKey = account.getExternalKey();
 
         PaypalPaymentMethodInfo paymentMethod1 = new PaypalPaymentMethodInfo.Builder().setDefaultMethod(false).setBaid("12345").setEmail(account.getEmail()).build();
-        Either<PaymentError, String> paymentMethodIdOrError1 = paymentApi.addPaymentMethod(accountKey, paymentMethod1);
+        Either<PaymentError, String> paymentMethodIdOrError1 = paymentApi.addPaymentMethod(accountKey, paymentMethod1, context);
 
         assertTrue(paymentMethodIdOrError1.isRight());
         assertNotNull(paymentMethodIdOrError1.getRight());
 
         PaypalPaymentMethodInfo paymentMethod2 = new PaypalPaymentMethodInfo.Builder().setDefaultMethod(true).setBaid("12345").setEmail(account.getEmail()).build();
 
-        Either<PaymentError, String> paymentMethodIdOrError2 = paymentApi.addPaymentMethod(accountKey, paymentMethod2);
+        Either<PaymentError, String> paymentMethodIdOrError2 = paymentApi.addPaymentMethod(accountKey, paymentMethod2, context);
 
         assertTrue(paymentMethodIdOrError2.isRight());
         assertNotNull(paymentMethodIdOrError2.getRight());
@@ -215,8 +226,8 @@ public abstract class TestPaymentApi {
 
         assertTrue(paymentMethodsOrError.isRight());
 
-        Either<PaymentError, Void> errorOrVoid1 = paymentApi.deletePaymentMethod(accountKey, paymentMethodIdOrError1.getRight());
-        Either<PaymentError, Void> errorOrVoid2 = paymentApi.deletePaymentMethod(accountKey, paymentMethodIdOrError2.getRight());
+        Either<PaymentError, Void> errorOrVoid1 = paymentApi.deletePaymentMethod(accountKey, paymentMethodIdOrError1.getRight(), context);
+        Either<PaymentError, Void> errorOrVoid2 = paymentApi.deletePaymentMethod(accountKey, paymentMethodIdOrError2.getRight(), context);
 
         assertTrue(errorOrVoid1.isRight());
         assertTrue(errorOrVoid2.isLeft());
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
index c1ba1db..7c8239f 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
@@ -23,9 +23,8 @@ import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 
+import com.ning.billing.util.callcontext.CallContext;
 import org.apache.commons.collections.CollectionUtils;
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
@@ -48,25 +47,35 @@ public class MockPaymentDao implements PaymentDao {
     }
 
     @Override
-    public PaymentAttempt createPaymentAttempt(Invoice invoice) {
-        PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice);
-        paymentAttempts.put(paymentAttempt.getPaymentAttemptId(), paymentAttempt);
-        return paymentAttempt;
+    public PaymentAttempt createPaymentAttempt(Invoice invoice, CallContext context) {
+        PaymentAttempt updatedPaymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice.getId(), invoice.getAccountId(),
+                invoice.getBalance(), invoice.getCurrency(), invoice.getInvoiceDate(),
+                null, null, null, context.getCreatedDate(), context.getUpdatedDate());
+
+        paymentAttempts.put(updatedPaymentAttempt.getPaymentAttemptId(), updatedPaymentAttempt);
+        return updatedPaymentAttempt;
     }
 
     @Override
-    public PaymentAttempt createPaymentAttempt(PaymentAttempt paymentAttempt) {
-        paymentAttempts.put(paymentAttempt.getPaymentAttemptId(), paymentAttempt);
-        return paymentAttempt;
+    public PaymentAttempt createPaymentAttempt(PaymentAttempt paymentAttempt, CallContext context) {
+        PaymentAttempt updatedPaymentAttempt = new PaymentAttempt(paymentAttempt.getPaymentAttemptId(),
+                paymentAttempt.getInvoiceId(),
+                paymentAttempt.getAccountId(), paymentAttempt.getAmount(), paymentAttempt.getCurrency(),
+                paymentAttempt.getInvoiceDate(), paymentAttempt.getPaymentAttemptDate(),
+                paymentAttempt.getPaymentId(), paymentAttempt.getRetryCount(),
+                context.getCreatedDate(), context.getUpdatedDate());
+
+        paymentAttempts.put(updatedPaymentAttempt.getPaymentAttemptId(), updatedPaymentAttempt);
+        return updatedPaymentAttempt;
     }
 
     @Override
-    public void savePaymentInfo(PaymentInfo paymentInfo) {
+    public void savePaymentInfo(PaymentInfo paymentInfo, CallContext context) {
         payments.put(paymentInfo.getPaymentId(), paymentInfo);
     }
 
     @Override
-    public void updatePaymentAttemptWithPaymentId(UUID paymentAttemptId, String paymentId) {
+    public void updatePaymentAttemptWithPaymentId(UUID paymentAttemptId, String paymentId, CallContext context) {
         PaymentAttempt existingPaymentAttempt = paymentAttempts.get(paymentAttemptId);
 
         if (existingPaymentAttempt != null) {
@@ -87,15 +96,14 @@ public class MockPaymentDao implements PaymentDao {
     }
 
     @Override
-    public void updatePaymentInfo(String paymentMethodType, String paymentId, String cardType, String cardCountry) {
+    public void updatePaymentInfo(String paymentMethodType, String paymentId, String cardType, String cardCountry, CallContext context) {
         PaymentInfo existingPayment = payments.get(paymentId);
         if (existingPayment != null) {
             PaymentInfo payment = existingPayment.cloner()
                     .setPaymentMethod(paymentMethodType)
                     .setCardType(cardType)
                     .setCardCountry(cardCountry)
-                    // TODO pass the clock?
-                    .setUpdatedDate(new DateTime(DateTimeZone.UTC))
+                    .setUpdatedDate(context.getUpdatedDate())
                     .build();
             payments.put(paymentId, payment);
         }
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
index a97786d..472fc2f 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
@@ -21,8 +21,14 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.DefaultCallContext;
+import com.ning.billing.util.callcontext.TestCallContext;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.clock.DefaultClock;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -32,8 +38,8 @@ import com.ning.billing.payment.api.PaymentAttempt;
 import com.ning.billing.payment.api.PaymentInfo;
 
 public abstract class TestPaymentDao {
-
     protected PaymentDao paymentDao;
+    protected CallContext context = new TestCallContext("PaymentTests");
 
     @Test
     public void testCreatePayment() {
@@ -45,16 +51,14 @@ public abstract class TestPaymentDao {
                 .setPaymentMethodId("12345")
                 .setReferenceId("12345")
                 .setType("Electronic")
-                .setCreatedDate(new DateTime(DateTimeZone.UTC))
-                .setUpdatedDate(new DateTime(DateTimeZone.UTC))
-                .setEffectiveDate(new DateTime(DateTimeZone.UTC))
+                .setEffectiveDate(new DefaultClock().getUTCNow())
                 .build();
 
-        paymentDao.savePaymentInfo(paymentInfo);
+        paymentDao.savePaymentInfo(paymentInfo, context);
     }
 
     @Test
-    public void testUpdatePaymenInfo() {
+    public void testUpdatePaymentInfo() {
         PaymentInfo paymentInfo = new PaymentInfo.Builder().setPaymentId(UUID.randomUUID().toString())
                 .setAmount(BigDecimal.TEN)
                 .setStatus("Processed")
@@ -63,14 +67,14 @@ public abstract class TestPaymentDao {
                 .setPaymentMethodId("12345")
                 .setReferenceId("12345")
                 .setType("Electronic")
-                .setCreatedDate(new DateTime(DateTimeZone.UTC))
-                .setUpdatedDate(new DateTime(DateTimeZone.UTC))
-                .setEffectiveDate(new DateTime(DateTimeZone.UTC))
+                .setCreatedDate(new DefaultClock().getUTCNow())
+                .setUpdatedDate(new DefaultClock().getUTCNow())
+                .setEffectiveDate(new DefaultClock().getUTCNow())
                 .build();
 
-        paymentDao.savePaymentInfo(paymentInfo);
-
-        paymentDao.updatePaymentInfo("CreditCard", paymentInfo.getPaymentId(), "Visa", "US");
+        CallContext context = new TestCallContext("PaymentTests");
+        paymentDao.savePaymentInfo(paymentInfo, context);
+        paymentDao.updatePaymentInfo("CreditCard", paymentInfo.getPaymentId(), "Visa", "US", context);
     }
 
     @Test
@@ -81,12 +85,10 @@ public abstract class TestPaymentDao {
                 .setAccountId(UUID.randomUUID())
                 .setAmount(BigDecimal.TEN)
                 .setCurrency(Currency.USD)
-                .setInvoiceDate(new DateTime(DateTimeZone.UTC))
-                .setCreatedDate(new DateTime(DateTimeZone.UTC))
-                .setUpdatedDate(new DateTime(DateTimeZone.UTC))
+                .setInvoiceDate(context.getCreatedDate())
                 .build();
 
-        paymentDao.createPaymentAttempt(paymentAttempt);
+        paymentDao.createPaymentAttempt(paymentAttempt, context);
     }
 
     @Test
@@ -98,11 +100,11 @@ public abstract class TestPaymentDao {
         final BigDecimal invoiceAmount = BigDecimal.TEN;
 
         // Move the clock backwards to test the updated_date field (see below)
-        final DateTime now = new DateTime(DateTimeZone.UTC).minusDays(1);
-
-        PaymentAttempt originalPaymenAttempt = new PaymentAttempt(paymentAttemptId, invoiceId, accountId, invoiceAmount, Currency.USD, now, now, paymentId, 0);
+        ClockMock clock = new ClockMock();
+        CallContext thisContext = new DefaultCallContext("Payment Tests", CallOrigin.TEST, UserType.TEST, clock);
 
-        PaymentAttempt attempt = paymentDao.createPaymentAttempt(originalPaymenAttempt);
+        PaymentAttempt originalPaymentAttempt = new PaymentAttempt(paymentAttemptId, invoiceId, accountId, invoiceAmount, Currency.USD, clock.getUTCNow(), clock.getUTCNow(), paymentId, 0);
+        PaymentAttempt attempt = paymentDao.createPaymentAttempt(originalPaymentAttempt, thisContext);
 
         List<PaymentAttempt> attemptsFromGet = paymentDao.getPaymentAttemptsForInvoiceId(invoiceId.toString());
 
@@ -124,18 +126,19 @@ public abstract class TestPaymentDao {
                 .setPaymentMethodId("12345")
                 .setReferenceId("12345")
                 .setType("Electronic")
-                .setCreatedDate(now)
-                .setUpdatedDate(now)
-                .setEffectiveDate(now)
+                .setCreatedDate(clock.getUTCNow())
+                .setUpdatedDate(clock.getUTCNow())
+                .setEffectiveDate(clock.getUTCNow())
                 .build();
 
-        paymentDao.savePaymentInfo(originalPaymentInfo);
+        paymentDao.savePaymentInfo(originalPaymentInfo, thisContext);
         PaymentInfo paymentInfo = paymentDao.getPaymentInfo(Arrays.asList(invoiceId.toString())).get(0);
         Assert.assertEquals(paymentInfo, originalPaymentInfo);
 
-        paymentDao.updatePaymentInfo(originalPaymentInfo.getPaymentMethod(), originalPaymentInfo.getPaymentId(), originalPaymentInfo.getCardType(), originalPaymentInfo.getCardCountry());
+        clock.setDeltaFromReality(60 * 60 * 1000); // move clock forward one hour
+        paymentDao.updatePaymentInfo(originalPaymentInfo.getPaymentMethod(), originalPaymentInfo.getPaymentId(), originalPaymentInfo.getCardType(), originalPaymentInfo.getCardCountry(), thisContext);
         paymentInfo = paymentDao.getPaymentInfo(Arrays.asList(invoiceId.toString())).get(0);
-        Assert.assertEquals(paymentInfo.getCreatedDate().getMillis() / 1000, originalPaymentInfo.getCreatedDate().getMillis() / 1000);
+        Assert.assertEquals(paymentInfo.getCreatedDate().compareTo(attempt.getCreatedDate()), 0);
         Assert.assertTrue(paymentInfo.getUpdatedDate().isAfter(originalPaymentInfo.getUpdatedDate()));
     }
 }
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDaoWithEmbeddedDb.java b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDaoWithEmbeddedDb.java
index a5da11b..84e3e5a 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDaoWithEmbeddedDb.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDaoWithEmbeddedDb.java
@@ -18,7 +18,6 @@ package com.ning.billing.payment.dao;
 
 import java.io.IOException;
 
-import com.ning.billing.util.clock.DefaultClock;
 import org.apache.commons.io.IOUtils;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
@@ -31,7 +30,7 @@ import com.ning.billing.dbi.MysqlTestingHelper;
 public class TestPaymentDaoWithEmbeddedDb extends TestPaymentDao {
     private final MysqlTestingHelper helper = new MysqlTestingHelper();
 
-    @BeforeClass(alwaysRun = true)
+    @BeforeClass(groups = { "slow", "database" })
     public void startMysql() throws IOException {
         final String paymentddl = IOUtils.toString(MysqlTestingHelper.class.getResourceAsStream("/com/ning/billing/payment/ddl.sql"));
 
@@ -39,13 +38,13 @@ public class TestPaymentDaoWithEmbeddedDb extends TestPaymentDao {
         helper.initDb(paymentddl);
     }
 
-    @AfterClass(alwaysRun = true)
+    @AfterClass(groups = { "slow", "database" })
     public void stopMysql() {
         helper.stopMysql();
     }
 
-    @BeforeMethod(alwaysRun = true)
+    @BeforeMethod(groups = { "slow", "database" })
     public void setUp() throws IOException {
-        paymentDao = new DefaultPaymentDao(helper.getDBI(), new DefaultClock());
+        paymentDao = new AuditedPaymentDao(helper.getDBI());
     }
 }
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDaoWithMock.java b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDaoWithMock.java
index 6e31f90..d540955 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDaoWithMock.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDaoWithMock.java
@@ -23,7 +23,7 @@ import org.testng.annotations.Test;
 
 @Test(groups = { "fast" })
 public class TestPaymentDaoWithMock extends TestPaymentDao {
-    @BeforeMethod(alwaysRun = true)
+    @BeforeMethod(groups = { "fast" })
     public void setUp() throws IOException {
         paymentDao = new MockPaymentDao();
     }
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
index 47713bb..adda477 100644
--- a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -24,13 +24,11 @@ import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import com.google.inject.Inject;
-import com.ning.billing.util.clock.Clock;
 import org.apache.commons.lang.RandomStringUtils;
-import org.joda.time.DateTime;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
+import com.google.inject.Inject;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.payment.api.CreditCardPaymentMethodInfo;
@@ -40,6 +38,7 @@ import com.ning.billing.payment.api.PaymentInfo;
 import com.ning.billing.payment.api.PaymentMethodInfo;
 import com.ning.billing.payment.api.PaymentProviderAccount;
 import com.ning.billing.payment.api.PaypalPaymentMethodInfo;
+import com.ning.billing.util.clock.Clock;
 
 public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
     private final AtomicBoolean makeNextInvoiceFail = new AtomicBoolean(false);
@@ -266,8 +265,14 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
 
     @Override
     public Either<PaymentError, Void> updatePaymentProviderAccountWithNewContact(Account account) {
-        // nothing to do here
-        return Either.right(null);
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public List<Either<PaymentError, PaymentInfo>> processRefund(Account account) {
+        // TODO Auto-generated method stub
+        return null;
     }
 
 }
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..6112fc2 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,10 @@ package com.ning.billing.payment;
 import java.math.BigDecimal;
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.callcontext.CallContextFactory;
 import com.ning.billing.util.entity.EntityPersistenceException;
 import org.apache.commons.lang.RandomStringUtils;
 import org.joda.time.DateTime;
@@ -38,11 +42,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(CallContextFactory factory, AccountDao accountDao, InvoiceDao invoiceDao) {
         this.accountDao = accountDao;
         this.invoiceDao = invoiceDao;
+        context = factory.createCallContext("Princess Buttercup", CallOrigin.TEST, UserType.TEST);
     }
 
     // These helper methods can be overridden in a plugin implementation
@@ -57,7 +63,7 @@ public class TestHelper {
                                                                      .currency(Currency.USD)
                                                                      .billingCycleDay(1)
                                                                      .build();
-        accountDao.create(account);
+        accountDao.create(account, context);
         return account;
     }
 
@@ -72,7 +78,7 @@ public class TestHelper {
                                                                      .currency(Currency.USD)
                                                                      .billingCycleDay(1)
                                                                      .build();
-        accountDao.create(account);
+        accountDao.create(account, context);
         return account;
     }
 
@@ -80,12 +86,13 @@ 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) {
                 RecurringInvoiceItem recurringInvoiceItem = (RecurringInvoiceItem) item;
                 invoice.addInvoiceItem(new RecurringInvoiceItem(invoice.getId(),
+                                                               account.getId(),
                                                                recurringInvoiceItem.getSubscriptionId(),
                                                                recurringInvoiceItem.getPlanName(),
                                                                recurringInvoiceItem.getPhaseName(),
@@ -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;
     }
 
@@ -105,8 +111,8 @@ public class TestHelper {
         final DateTime now = new DateTime(DateTimeZone.UTC);
         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);
+        final InvoiceItem item = new RecurringInvoiceItem(null, account.getId(), subscriptionId, "test plan", "test phase", now, now.plusMonths(1),
+                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/TestNotifyInvoicePaymentApi.java b/payment/src/test/java/com/ning/billing/payment/TestNotifyInvoicePaymentApi.java
index e38f14f..80bdd82 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestNotifyInvoicePaymentApi.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestNotifyInvoicePaymentApi.java
@@ -20,6 +20,8 @@ import static org.testng.Assert.assertNotNull;
 
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TestCallContext;
 import com.ning.billing.util.entity.EntityPersistenceException;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
@@ -51,6 +53,8 @@ public class TestNotifyInvoicePaymentApi {
     @Inject
     private TestHelper testHelper;
 
+    private CallContext context = new TestCallContext("Payment Api Tests");
+
     @BeforeMethod(alwaysRun = true)
     public void setUp() throws EventBusException {
         eventBus.start();
@@ -74,7 +78,8 @@ public class TestNotifyInvoicePaymentApi {
                                      invoice.getBalance(),
                                      invoice.getCurrency(),
                                      paymentAttempt.getPaymentAttemptId(),
-                                     paymentAttempt.getPaymentAttemptDate());
+                                     paymentAttempt.getPaymentAttemptDate(),
+                                     context);
 
         InvoicePayment invoicePayment = invoicePaymentApi.getInvoicePayment(paymentAttempt.getPaymentAttemptId());
 
@@ -89,7 +94,8 @@ public class TestNotifyInvoicePaymentApi {
         PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice);
         invoicePaymentApi.notifyOfPaymentAttempt(invoice.getId(),
                                                  paymentAttempt.getPaymentAttemptId(),
-                                                 paymentAttempt.getPaymentAttemptDate());
+                                                 paymentAttempt.getPaymentAttemptDate(),
+                                                 context);
 
         InvoicePayment invoicePayment = invoicePaymentApi.getInvoicePayment(paymentAttempt.getPaymentAttemptId());
 
diff --git a/payment/src/test/java/com/ning/billing/payment/TestPaymentInvoiceIntegration.java b/payment/src/test/java/com/ning/billing/payment/TestPaymentInvoiceIntegration.java
index 888e017..8b71c10 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestPaymentInvoiceIntegration.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestPaymentInvoiceIntegration.java
@@ -26,6 +26,8 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.concurrent.Callable;
 
+import com.ning.billing.account.glue.AccountModuleWithMocks;
+import com.ning.billing.util.glue.CallContextModule;
 import org.apache.commons.io.IOUtils;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.IDBI;
@@ -99,8 +101,9 @@ public class TestPaymentInvoiceIntegration {
     @BeforeMethod(alwaysRun = true)
     public void setUp() throws EventBusException {
         Injector injector = Guice.createInjector(new PaymentTestModuleWithEmbeddedDb(),
-                                                 new AccountModule(),
+                                                 new AccountModuleWithMocks(),
                                                  new InvoiceModuleWithMocks(),
+                                                 new CallContextModule(),
                                                  new MockClockModule(),
                                                  new AbstractModule() {
                                                     @Override
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 fe1d5e0..1379267 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
@@ -25,6 +25,10 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.DefaultCallContext;
+import com.ning.billing.util.callcontext.UserType;
 import org.joda.time.DateTime;
 import org.joda.time.Days;
 import org.testng.annotations.AfterMethod;
@@ -83,6 +87,7 @@ public class TestRetryService {
 
     private MockPaymentProviderPlugin mockPaymentProviderPlugin;
     private MockNotificationQueue mockNotificationQueue;
+    private CallContext context;
 
     @BeforeClass(alwaysRun = true)
     public void initialize() throws Exception {
@@ -96,6 +101,7 @@ public class TestRetryService {
 
         mockPaymentProviderPlugin = (MockPaymentProviderPlugin)registry.getPlugin(null);
         mockNotificationQueue = (MockNotificationQueue)notificationQueueService.getNotificationQueue(RetryService.SERVICE_NAME, RetryService.QUEUE_NAME);
+        context = new DefaultCallContext("RetryServiceTests", CallOrigin.INTERNAL, UserType.TEST, clock);
     }
 
     @AfterMethod(alwaysRun = true)
@@ -114,18 +120,18 @@ public class TestRetryService {
         final DateTime startDate = clock.getUTCNow();
         final DateTime endDate = startDate.plusMonths(1);
         invoice.addInvoiceItem(new RecurringInvoiceItem(invoice.getId(),
+                                                       account.getId(),
                                                        subscriptionId,
                                                        "test plan", "test phase",
                                                        startDate,
                                                        endDate,
                                                        amount,
                                                        new BigDecimal("1.0"),
-                                                       Currency.USD,
-                                                       clock.getUTCNow()));
+                                                       Currency.USD));
 
         mockPaymentProviderPlugin.makeNextInvoiceFail();
 
-        List<Either<PaymentError, PaymentInfo>> results = paymentApi.createPayment(account.getExternalKey(), Arrays.asList(invoice.getId().toString()));
+        List<Either<PaymentError, PaymentInfo>> results = paymentApi.createPayment(account.getExternalKey(), Arrays.asList(invoice.getId().toString()), context);
 
         assertEquals(results.size(), 1);
         assertTrue(results.get(0).isLeft());
@@ -155,14 +161,14 @@ public class TestRetryService {
         final DateTime now = clock.getUTCNow();
 
         invoice.addInvoiceItem(new RecurringInvoiceItem(invoice.getId(),
+                                                       account.getId(),
                                                        subscriptionId,
                                                        "test plan", "test phase",
                                                        now,
                                                        now.plusMonths(1),
                                                        amount,
                                                        new BigDecimal("1.0"),
-                                                       Currency.USD,
-                                                       now));
+                                                       Currency.USD));
 
         int numberOfDays = paymentConfig.getPaymentRetryDays().get(0);
         DateTime nextRetryDate = now.plusDays(numberOfDays);
@@ -171,7 +177,7 @@ public class TestRetryService {
                                                                                       .setPaymentAttemptDate(now)
                                                                                       .build();
 
-        PaymentAttempt attempt = paymentDao.createPaymentAttempt(paymentAttempt);
+        paymentDao.createPaymentAttempt(paymentAttempt, context);
         retryService.scheduleRetry(paymentAttempt, nextRetryDate);
         ((ClockMock)clock).setDeltaFromReality(Days.days(numberOfDays).toStandardSeconds().getSeconds() * 1000);
         Thread.sleep(2000);
@@ -179,10 +185,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());
 
         List<PaymentAttempt> updatedAttempts = paymentApi.getPaymentAttemptsForInvoiceId(invoice.getId().toString());

pom.xml 10(+7 -3)

diff --git a/pom.xml b/pom.xml
index c0c46a2..0d810d7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,8 +7,7 @@
     OR CONDITIONS OF ANY KIND, either express or implied. See the ~ License for 
     the specific language governing permissions and limitations ~ under the License. -->
 
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
         <groupId>org.sonatype.oss</groupId>
         <artifactId>oss-parent</artifactId>
@@ -18,7 +17,7 @@
     <groupId>com.ning.billing</groupId>
     <artifactId>killbill</artifactId>
     <packaging>pom</packaging>
-    <version>0.1.8-SNAPSHOT</version>
+    <version>0.1.11-SNAPSHOT</version>
     <name>killbill</name>
     <description>Library for managing recurring subscriptions and the associated billing</description>
     <url>http://github.com/ning/killbill</url>
@@ -396,6 +395,11 @@
                                 <exclude>**/test-output/**</exclude>
                                 <exclude>**/bin/**</exclude>
                                 <exclude>.travis.yml</exclude>
+                                <!--  until we merge from server branch we disable rat for those -->
+                                <exclude>jaxrs/**</exclude>
+                                <exclude>server/**</exclude>
+                                <exclude>bin/**</exclude>
+
                             </excludes>
                         </configuration>
                     </execution>

util/pom.xml 5(+2 -3)

diff --git a/util/pom.xml b/util/pom.xml
index dc5c251..e0d256c 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -8,13 +8,12 @@
     OR CONDITIONS OF ANY KIND, either express or implied. See the ~ License for 
     the specific language governing permissions and limitations ~ under the License. -->
 
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.8-SNAPSHOT</version>
+        <version>0.1.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-util</artifactId>
diff --git a/util/src/main/java/com/ning/billing/util/audit/dao/AuditSqlDao.java b/util/src/main/java/com/ning/billing/util/audit/dao/AuditSqlDao.java
new file mode 100644
index 0000000..8c14942
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/audit/dao/AuditSqlDao.java
@@ -0,0 +1,43 @@
+/*
+ * 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.audit.dao;
+
+import com.ning.billing.util.ChangeType;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.dao.ChangeTypeBinder;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.SqlBatch;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+
+import java.util.List;
+
+@ExternalizedSqlViaStringTemplate3
+public interface AuditSqlDao {
+    @SqlUpdate
+    public void insertAuditFromTransaction(@Bind("tableName") final String tableName,
+                                           @Bind("recordId") final String recordId,
+                                           @ChangeTypeBinder final ChangeType changeType,
+                                           @CallContextBinder CallContext context);
+
+    @SqlBatch(transactional = false)
+    public void insertAuditFromTransaction(@Bind("tableName") final String tableName,
+                                           @Bind("recordId") final List<String> recordIds,
+                                           @ChangeTypeBinder final ChangeType changeType,
+                                           @CallContextBinder CallContext context);
+}
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/CallContextBase.java b/util/src/main/java/com/ning/billing/util/callcontext/CallContextBase.java
new file mode 100644
index 0000000..8164f76
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/callcontext/CallContextBase.java
@@ -0,0 +1,44 @@
+/*
+ * 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.callcontext;
+
+public abstract class CallContextBase implements CallContext {
+    private final String userName;
+    private final CallOrigin callOrigin;
+    private final UserType userType;
+
+    public CallContextBase(String userName, CallOrigin callOrigin, UserType userType) {
+        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;
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/CallContextBinder.java b/util/src/main/java/com/ning/billing/util/callcontext/CallContextBinder.java
new file mode 100644
index 0000000..3ac8a98
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/callcontext/CallContextBinder.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.callcontext;
+
+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(CallContextBinder.CallContextBinderFactory.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER})
+public @interface CallContextBinder {
+    public static class CallContextBinderFactory implements BinderFactory {
+        @Override
+        public Binder build(Annotation annotation) {
+            return new Binder<CallContextBinder, CallContext>() {
+                @Override
+                public void bind(SQLStatement q, CallContextBinder bind, CallContext callContext) {
+                    q.bind("userName", callContext.getUserName());
+                    q.bind("createdDate", callContext.getCreatedDate().toDate());
+                    q.bind("updatedDate", callContext.getUpdatedDate().toDate());
+                }
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/CallContextFactory.java b/util/src/main/java/com/ning/billing/util/callcontext/CallContextFactory.java
new file mode 100644
index 0000000..6ffc554
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/callcontext/CallContextFactory.java
@@ -0,0 +1,27 @@
+/*
+ * 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.callcontext;
+
+import org.joda.time.DateTime;
+
+public interface CallContextFactory {
+    CallContext createCallContext(String userName, CallOrigin callOrigin, UserType userType);
+
+    CallContext createMigrationCallContext(String userName, CallOrigin callOrigin, UserType userType, DateTime createdDate, DateTime updatedDate);
+
+    CallContext toMigrationCallContext(CallContext callContext, DateTime createdDate, DateTime updatedDate);
+}
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java b/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java
new file mode 100644
index 0000000..57dc682
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java
@@ -0,0 +1,39 @@
+/*
+ * 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.callcontext;
+
+import com.ning.billing.util.clock.Clock;
+import org.joda.time.DateTime;
+
+public class DefaultCallContext extends CallContextBase {
+    private final Clock clock;
+
+    public DefaultCallContext(String userName, CallOrigin callOrigin, UserType userType, Clock clock) {
+        super(userName, callOrigin, userType);
+        this.clock = clock;
+    }
+
+    @Override
+    public DateTime getCreatedDate() {
+        return clock.getUTCNow();
+    }
+
+    @Override
+    public DateTime getUpdatedDate() {
+        return clock.getUTCNow();
+    }
+}
\ No newline at end of file
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContextFactory.java b/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContextFactory.java
new file mode 100644
index 0000000..f75574c
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContextFactory.java
@@ -0,0 +1,45 @@
+/*
+ * 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.callcontext;
+
+import com.google.inject.Inject;
+import com.ning.billing.util.clock.Clock;
+import org.joda.time.DateTime;
+
+public class DefaultCallContextFactory implements CallContextFactory {
+    private final Clock clock;
+
+    @Inject
+    public DefaultCallContextFactory(Clock clock) {
+        this.clock = clock;
+    }
+
+    @Override
+    public CallContext createCallContext(String userName, CallOrigin callOrigin, UserType userType) {
+        return new DefaultCallContext(userName, callOrigin, userType, clock);
+    }
+
+    @Override
+    public CallContext createMigrationCallContext(String userName, CallOrigin callOrigin, UserType userType, DateTime createdDate, DateTime updatedDate) {
+        return new MigrationCallContext(userName, callOrigin, userType, createdDate, updatedDate);
+    }
+
+    @Override
+    public CallContext toMigrationCallContext(CallContext callContext, DateTime createdDate, DateTime updatedDate) {
+        return new MigrationCallContext(callContext, createdDate, updatedDate);
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/MigrationCallContext.java b/util/src/main/java/com/ning/billing/util/callcontext/MigrationCallContext.java
new file mode 100644
index 0000000..9d7fc58
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/callcontext/MigrationCallContext.java
@@ -0,0 +1,46 @@
+/*
+ * 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.callcontext;
+
+import org.joda.time.DateTime;
+
+public class MigrationCallContext extends CallContextBase {
+    private final DateTime createdDate;
+    private final DateTime updatedDate;
+
+    public MigrationCallContext(String userName, CallOrigin callOrigin, UserType userType, DateTime createdDate, DateTime updatedDate) {
+        super(userName, callOrigin, userType);
+        this.createdDate = createdDate;
+        this.updatedDate = updatedDate;
+    }
+
+    public MigrationCallContext(CallContext context, DateTime createdDate, DateTime updatedDate) {
+        super(context.getUserName(), context.getCallOrigin(), context.getUserType());
+        this.createdDate = createdDate;
+        this.updatedDate = updatedDate;
+    }
+
+    @Override
+    public DateTime getCreatedDate() {
+        return createdDate;
+    }
+
+    @Override
+    public DateTime getUpdatedDate() {
+        return updatedDate;
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/customfield/CustomFieldHistory.java b/util/src/main/java/com/ning/billing/util/customfield/CustomFieldHistory.java
new file mode 100644
index 0000000..b4bb413
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/CustomFieldHistory.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.customfield;
+
+import com.ning.billing.util.ChangeType;
+import org.joda.time.DateTime;
+
+import java.util.UUID;
+
+public class CustomFieldHistory implements CustomField {
+    private final UUID historyId = UUID.randomUUID();
+    private final CustomField field;
+    private final ChangeType changeType;
+
+    public CustomFieldHistory(CustomField field, ChangeType changeType) {
+        this.field = field;
+        this.changeType = changeType;
+    }
+
+    public UUID getHistoryId() {
+        return historyId;
+    }
+
+    public ChangeType getChangeType() {
+        return changeType;
+    }
+
+    @Override
+    public String getName() {
+        return field.getName();
+    }
+
+    @Override
+    public String getValue() {
+        return field.getValue();
+    }
+
+    @Override
+    public void setValue(String value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public UUID getId() {
+        return field.getId();
+    }
+
+    @Override
+    public String getCreatedBy() {
+        return field.getCreatedBy();
+    }
+
+    @Override
+    public DateTime getCreatedDate() {
+        return field.getCreatedDate();
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/customfield/CustomFieldHistoryBinder.java b/util/src/main/java/com/ning/billing/util/customfield/CustomFieldHistoryBinder.java
new file mode 100644
index 0000000..51e3621
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/CustomFieldHistoryBinder.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+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(CustomFieldHistoryBinder.CustomFieldBinderFactory.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER})
+public @interface CustomFieldHistoryBinder {
+    public static class CustomFieldBinderFactory implements BinderFactory {
+        @Override
+        public Binder build(Annotation annotation) {
+            return new Binder<CustomFieldHistoryBinder, CustomFieldHistory>() {
+                @Override
+                public void bind(SQLStatement q, CustomFieldHistoryBinder bind, CustomFieldHistory customFieldHistory) {
+                    q.bind("historyId", customFieldHistory.getHistoryId().toString());
+                    q.bind("changeType", customFieldHistory.getChangeType().toString());
+                    q.bind("id", customFieldHistory.getId().toString());
+                    q.bind("fieldName", customFieldHistory.getName());
+                    q.bind("fieldValue", customFieldHistory.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..0c2bc5b
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/CustomFieldMapper.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+import com.ning.billing.util.dao.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..88fcffa 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 {
+public abstract class CustomizableEntityBase extends EntityBase implements Customizable {
     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..b7d2de0
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java
@@ -0,0 +1,81 @@
+/*
+ * 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 com.ning.billing.util.ChangeType;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.customfield.CustomFieldHistory;
+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 implements CustomFieldDao {
+    @Override
+    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);
+
+        List<CustomFieldHistory> fieldHistories = new ArrayList<CustomFieldHistory>();
+        fieldHistories.addAll(convertToHistoryEntry(fields, ChangeType.INSERT));
+        fieldHistories.addAll(convertToHistoryEntry(fieldsToUpdate, ChangeType.UPDATE));
+        fieldHistories.addAll(convertToHistoryEntry(existingFields, ChangeType.DELETE));
+
+        CustomFieldHistorySqlDao historyDao = dao.become(CustomFieldHistorySqlDao.class);
+        historyDao.batchAddHistoryFromTransaction(objectId.toString(), objectType, fieldHistories, context);
+
+        CustomFieldAuditSqlDao auditDao = dao.become(CustomFieldAuditSqlDao.class);
+        auditDao.batchInsertAuditLogFromTransaction(objectId.toString(), objectType, fieldHistories, context);
+    }
+
+    private List<CustomFieldHistory> convertToHistoryEntry(List<CustomField> fields, ChangeType changeType) {
+        List<CustomFieldHistory> result = new ArrayList<CustomFieldHistory>();
+
+        for (CustomField field : fields) {
+            result.add(new CustomFieldHistory(field, changeType));
+        }
+
+        return result;
+    }
+}
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..471cdca
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldAuditSqlDao.java
@@ -0,0 +1,37 @@
+/*
+ * 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 com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.customfield.CustomFieldHistory;
+import com.ning.billing.util.customfield.CustomFieldHistoryBinder;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.SqlBatch;
+import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+
+import java.util.List;
+
+@ExternalizedSqlViaStringTemplate3
+public interface CustomFieldAuditSqlDao extends Transactional<CustomFieldAuditSqlDao> {
+    @SqlBatch(transactional=false)
+    public void batchInsertAuditLogFromTransaction(@Bind("objectId") final String objectId,
+                                                   @Bind("objectType") final String objectType,
+                                                   @CustomFieldHistoryBinder final List<CustomFieldHistory> entities,
+                                                   @CallContextBinder final CallContext context);
+}
diff --git a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldDao.java b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldDao.java
new file mode 100644
index 0000000..a987c4d
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldDao.java
@@ -0,0 +1,28 @@
+/*
+ * 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 com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.customfield.CustomField;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+
+import java.util.List;
+import java.util.UUID;
+
+public interface CustomFieldDao {
+    void saveFields(Transmogrifier dao, UUID objectId, String objectType, List<CustomField> fields, CallContext context);
+}
diff --git a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldHistorySqlDao.java b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldHistorySqlDao.java
new file mode 100644
index 0000000..6124ff4
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldHistorySqlDao.java
@@ -0,0 +1,36 @@
+/*
+ * 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 com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.customfield.CustomFieldHistory;
+import com.ning.billing.util.customfield.CustomFieldHistoryBinder;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.SqlBatch;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+
+import java.util.List;
+
+@ExternalizedSqlViaStringTemplate3
+public interface CustomFieldHistorySqlDao {
+    @SqlBatch(transactional=false)
+    public void batchAddHistoryFromTransaction(@Bind("objectId") final String objectId,
+                                               @Bind("objectType") final String objectType,
+                                               @CustomFieldHistoryBinder final List<CustomFieldHistory> 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..733bfeb
--- /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.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.customfield.CustomFieldBinder;
+import com.ning.billing.util.customfield.CustomFieldMapper;
+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/dao/BinderBase.java b/util/src/main/java/com/ning/billing/util/dao/BinderBase.java
new file mode 100644
index 0000000..ef20575
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/dao/BinderBase.java
@@ -0,0 +1,27 @@
+/*
+ * 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.dao;
+
+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/dao/ChangeTypeBinder.java b/util/src/main/java/com/ning/billing/util/dao/ChangeTypeBinder.java
new file mode 100644
index 0000000..58ed27b
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/dao/ChangeTypeBinder.java
@@ -0,0 +1,44 @@
+/*
+ * 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.dao;
+
+import com.ning.billing.util.ChangeType;
+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(ChangeTypeBinder.ChangeTypeBinderFactory.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER})
+public @interface ChangeTypeBinder {
+    public static class ChangeTypeBinderFactory implements BinderFactory {
+        public Binder build(Annotation annotation) {
+            return new Binder<ChangeTypeBinder, ChangeType>() {
+                public void bind(SQLStatement q, ChangeTypeBinder bind, ChangeType changeType) {
+                    q.bind("changeType", changeType.toString());
+                }
+            };
+        }
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/dao/MapperBase.java b/util/src/main/java/com/ning/billing/util/dao/MapperBase.java
new file mode 100644
index 0000000..d10e2a0
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/dao/MapperBase.java
@@ -0,0 +1,32 @@
+/*
+ * 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.dao;
+
+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/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/EntityCollectionBase.java b/util/src/main/java/com/ning/billing/util/entity/EntityCollectionBase.java
index 369f255..a240000 100644
--- a/util/src/main/java/com/ning/billing/util/entity/EntityCollectionBase.java
+++ b/util/src/main/java/com/ning/billing/util/entity/EntityCollectionBase.java
@@ -54,7 +54,7 @@ public abstract class EntityCollectionBase<T extends Entity> implements EntityCo
 
     @Override
     public void remove(T entity) {
-        entities.remove(entity);
+        entities.remove(getEntityKey(entity));
     }
 
     @Override
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..9bb2d8e 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,8 @@
 
 package com.ning.billing.util.entity;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
 import org.skife.jdbi.v2.sqlobject.*;
 
 import java.util.List;
@@ -26,15 +28,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..d093492 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,16 @@ package com.ning.billing.util.entity;
 
 import java.util.List;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
 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/ExtendedEntityBase.java b/util/src/main/java/com/ning/billing/util/entity/ExtendedEntityBase.java
new file mode 100644
index 0000000..0017397
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/ExtendedEntityBase.java
@@ -0,0 +1,133 @@
+/*
+ * 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.CallContext;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.customfield.Customizable;
+import com.ning.billing.util.customfield.DefaultFieldStore;
+import com.ning.billing.util.customfield.FieldStore;
+import com.ning.billing.util.tag.DefaultTagStore;
+import com.ning.billing.util.tag.DescriptiveTag;
+import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.TagDefinition;
+import com.ning.billing.util.tag.TagStore;
+import com.ning.billing.util.tag.Taggable;
+import org.joda.time.DateTime;
+
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.UUID;
+
+public abstract class ExtendedEntityBase extends EntityBase implements Customizable, Taggable {
+    protected final FieldStore fields;
+    protected final TagStore tags;
+
+    public ExtendedEntityBase() {
+        super();
+        this.fields = DefaultFieldStore.create(getId(), getObjectName());
+        this.tags = new DefaultTagStore(id, getObjectName());
+    }
+
+    public ExtendedEntityBase(final UUID id, @Nullable final String createdBy, @Nullable final DateTime createdDate) {
+        super(id, createdBy, createdDate);
+        this.fields = DefaultFieldStore.create(getId(), getObjectName());
+        this.tags = new DefaultTagStore(id, getObjectName());
+    }
+
+    @Override
+    public String getFieldValue(final String fieldName) {
+        return fields.getValue(fieldName);
+    }
+
+    @Override
+    public void setFieldValue(final String fieldName, final String fieldValue) {
+        fields.setValue(fieldName, fieldValue);
+    }
+
+    @Override
+    public List<CustomField> getFieldList() {
+        return fields.getEntityList();
+    }
+
+    @Override
+    public void setFields(final List<CustomField> fields) {
+        if (fields != null) {
+            this.fields.add(fields);
+        }
+    }
+
+    @Override
+    public void clearFields() {
+        fields.clear();
+    }
+
+    @Override
+	public List<Tag> getTagList() {
+		return tags.getEntityList();
+	}
+
+	@Override
+	public boolean hasTag(final String tagName) {
+		return tags.containsTag(tagName);
+	}
+
+	@Override
+	public void addTag(final TagDefinition definition) {
+		Tag tag = new DescriptiveTag(definition);
+		tags.add(tag) ;
+	}
+
+	@Override
+	public void addTags(final List<Tag> tags) {
+		if (tags != null) {
+			this.tags.add(tags);
+		}
+	}
+
+	@Override
+	public void clearTags() {
+		this.tags.clear();
+	}
+
+	@Override
+	public void removeTag(final TagDefinition definition) {
+		tags.remove(definition.getName());
+	}
+
+	@Override
+	public boolean generateInvoice() {
+		return tags.generateInvoice();
+	}
+
+	@Override
+	public boolean processPayment() {
+		return tags.processPayment();
+	}
+
+    @Override
+    public abstract String getObjectName();
+
+    @Override
+    public abstract void saveFieldValue(String fieldName, String fieldValue, CallContext context);
+
+    @Override
+    public abstract void saveFields(List<CustomField> fields, CallContext context);
+
+    @Override
+    public abstract void clearPersistedFields(CallContext context);
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/UpdatableEntity.java b/util/src/main/java/com/ning/billing/util/entity/UpdatableEntity.java
new file mode 100644
index 0000000..c860ddb
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/UpdatableEntity.java
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+public interface UpdatableEntity extends Entity {
+    public String getUpdatedBy();
+    public DateTime getUpdatedDate();
+}
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..ed27853
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/UpdatableEntityCollectionDao.java
@@ -0,0 +1,33 @@
+/*
+ * 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.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.BindBean;
+import org.skife.jdbi.v2.sqlobject.SqlBatch;
+
+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..4438625 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,13 @@
 
 package com.ning.billing.util.entity;
 
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
 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/CallContextModule.java b/util/src/main/java/com/ning/billing/util/glue/CallContextModule.java
new file mode 100644
index 0000000..6c0cca0
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/glue/CallContextModule.java
@@ -0,0 +1,28 @@
+/*
+ * 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.glue;
+
+import com.google.inject.AbstractModule;
+import com.ning.billing.util.callcontext.CallContextFactory;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
+
+public class CallContextModule extends AbstractModule {
+    @Override
+    protected void configure() {
+        bind(CallContextFactory.class).to(DefaultCallContextFactory.class).asEagerSingleton();
+    }
+}
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..2d579fc
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/glue/FieldStoreModule.java
@@ -0,0 +1,28 @@
+/*
+ * 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.glue;
+
+import com.google.inject.AbstractModule;
+import com.ning.billing.util.customfield.dao.AuditedCustomFieldDao;
+import com.ning.billing.util.customfield.dao.CustomFieldDao;
+
+public class FieldStoreModule extends AbstractModule {
+    @Override
+    protected void configure() {
+        bind(CustomFieldDao.class).to(AuditedCustomFieldDao.class).asEagerSingleton();
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/glue/TagDescriptionDaoProvider.java b/util/src/main/java/com/ning/billing/util/glue/TagDescriptionDaoProvider.java
index 560a09f..c05ca29 100644
--- a/util/src/main/java/com/ning/billing/util/glue/TagDescriptionDaoProvider.java
+++ b/util/src/main/java/com/ning/billing/util/glue/TagDescriptionDaoProvider.java
@@ -1,39 +1,39 @@
-/*
- * 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.glue;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.ning.billing.util.tag.dao.TagDefinitionSqlDao;
-import org.skife.jdbi.v2.IDBI;
-
-public class TagDescriptionDaoProvider implements Provider<TagDefinitionSqlDao>
-{
-    private final IDBI dbi;
-
-    @Inject
-    public TagDescriptionDaoProvider(final IDBI dbi)
-    {
-        this.dbi = dbi;
-    }
-
-    @Override
-    public TagDefinitionSqlDao get()
-    {
-        return dbi.onDemand(TagDefinitionSqlDao.class);
-    }
-}
+///*
+// * 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.glue;
+//
+//import com.google.inject.Inject;
+//import com.google.inject.Provider;
+//import com.ning.billing.util.tag.dao.TagDefinitionSqlDao;
+//import org.skife.jdbi.v2.IDBI;
+//
+//public class TagDescriptionDaoProvider implements Provider<TagDefinitionSqlDao>
+//{
+//    private final IDBI dbi;
+//
+//    @Inject
+//    public TagDescriptionDaoProvider(final IDBI dbi)
+//    {
+//        this.dbi = dbi;
+//    }
+//
+//    @Override
+//    public TagDefinitionSqlDao get()
+//    {
+//        return dbi.onDemand(TagDefinitionSqlDao.class);
+//    }
+//}
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..1f97f7e 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
@@ -1,39 +1,39 @@
-/*
- * 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.glue;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.ning.billing.util.tag.dao.TagStoreSqlDao;
-import org.skife.jdbi.v2.IDBI;
-
-public class TagStoreDaoProvider implements Provider<TagStoreSqlDao>
-{
-    private final IDBI dbi;
-
-    @Inject
-    public TagStoreDaoProvider(final IDBI dbi)
-    {
-        this.dbi = dbi;
-    }
-
-    @Override
-    public TagStoreSqlDao get()
-    {
-        return dbi.onDemand(TagStoreSqlDao.class);
-    }
-}
+///*
+// * 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.glue;
+//
+//import com.google.inject.Inject;
+//import com.google.inject.Provider;
+//import com.ning.billing.util.tag.dao.TagSqlDao;
+//import org.skife.jdbi.v2.IDBI;
+//
+//public class TagStoreDaoProvider implements Provider<TagSqlDao>
+//{
+//    private final IDBI dbi;
+//
+//    @Inject
+//    public TagStoreDaoProvider(final IDBI dbi)
+//    {
+//        this.dbi = dbi;
+//    }
+//
+//    @Override
+//    public TagSqlDao get()
+//    {
+//        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 5f87a6a..47e5412 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
@@ -19,17 +19,17 @@ package com.ning.billing.util.glue;
 import com.google.inject.AbstractModule;
 import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.tag.api.DefaultTagDefinitionUserApi;
+import com.ning.billing.util.tag.dao.AuditedTagDao;
 import com.ning.billing.util.tag.dao.DefaultTagDefinitionDao;
+import com.ning.billing.util.tag.dao.TagDao;
 import com.ning.billing.util.tag.dao.TagDefinitionDao;
 import com.ning.billing.util.tag.dao.TagDefinitionSqlDao;
-import com.ning.billing.util.tag.dao.TagStoreSqlDao;
 
 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(TagDao.class).to(AuditedTagDao.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..3d0a8b1 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
@@ -18,13 +18,13 @@ package com.ning.billing.util.notificationq.dao;
 
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.sql.Timestamp;
 import java.util.Date;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.dao.BinderBase;
+import com.ning.billing.util.dao.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;
@@ -63,12 +63,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 +78,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 03e9740..53d8841 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
@@ -22,6 +22,7 @@ import org.joda.time.DateTime;
 
 import com.google.inject.Inject;
 import com.ning.billing.ErrorCode;
+import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.tag.ControlTagType;
@@ -45,18 +46,19 @@ public class DefaultTagDefinitionUserApi implements TagUserApi {
     }
 
     @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);
     }
 
 	@Override
@@ -66,8 +68,7 @@ public class DefaultTagDefinitionUserApi implements TagUserApi {
 	}
 
     @Override
-    public Tag createControlTag(String controlTagName, String addedBy,
-            DateTime addedDate) throws TagDefinitionApiException {
+    public Tag createControlTag(String controlTagName) throws TagDefinitionApiException {
         ControlTagType type = null;
         for(ControlTagType t : ControlTagType.values()) {
             if(t.toString().equals(controlTagName)) {
@@ -78,14 +79,13 @@ public class DefaultTagDefinitionUserApi implements TagUserApi {
         if(type == null) {
             throw new TagDefinitionApiException(ErrorCode.CONTROL_TAG_DOES_NOT_EXIST, controlTagName);
         }
-        return new DefaultControlTag(addedBy, addedDate, type);
+        return new DefaultControlTag(type);
     }
 
     @Override
-    public Tag createDescriptiveTag(String tagDefinitionName, String addedBy,
-            DateTime addedDate) throws TagDefinitionApiException {
+    public Tag createDescriptiveTag(String tagDefinitionName) throws TagDefinitionApiException {
         TagDefinition tagDefinition = getTagDefinition(tagDefinitionName);
         
-        return new DescriptiveTag(tagDefinition, addedBy, addedDate);
+        return new DescriptiveTag(tagDefinition);
     }
 }
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..374e0a3
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java
@@ -0,0 +1,143 @@
+/*
+ * 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.tag.dao;
+
+import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.util.ChangeType;
+import com.ning.billing.util.audit.dao.AuditSqlDao;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.tag.Tag;
+import org.skife.jdbi.v2.IDBI;
+import org.skife.jdbi.v2.Transaction;
+import org.skife.jdbi.v2.TransactionStatus;
+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 AuditedTagDao implements TagDao {
+    private final TagSqlDao tagSqlDao;
+
+    @Inject
+    public AuditedTagDao(final IDBI dbi) {
+        this.tagSqlDao = dbi.onDemand(TagSqlDao.class);
+    }
+
+    @Override
+    public void saveTags(final UUID objectId, final String objectType,
+                         final List<Tag> tags, final CallContext context) {
+        saveTagsFromTransaction(tagSqlDao, objectId, objectType, tags, context);
+    }
+
+    @Override
+    public void saveTagsFromTransaction(final Transmogrifier dao, final UUID objectId, final String objectType,
+                                        final List<Tag> tags, final 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);
+
+        List<String> historyIdsForInsert = getIdList(tags.size());
+        tagSqlDao.batchInsertHistoryFromTransaction(objectId.toString(), objectType, historyIdsForInsert, tags, ChangeType.INSERT, context);
+        List<String> historyIdsForDelete = getIdList(existingTags.size());
+        tagSqlDao.batchInsertHistoryFromTransaction(objectId.toString(), objectType, historyIdsForDelete, existingTags, ChangeType.DELETE, context);
+
+        AuditSqlDao auditSqlDao = tagSqlDao.become(AuditSqlDao.class);
+        auditSqlDao.insertAuditFromTransaction("tag_history", historyIdsForInsert, ChangeType.INSERT, context);
+        auditSqlDao.insertAuditFromTransaction("tag_history", historyIdsForDelete, ChangeType.DELETE, context);
+    }
+
+    private List<String> getIdList(int size) {
+        List<String> results = new ArrayList<String>();
+        for (int i = 0; i < size; i++) {
+            results.add(UUID.randomUUID().toString());
+        }
+        return results;
+    }
+
+    @Override
+    public List<Tag> loadTags(final UUID objectId, final String objectType) {
+        return tagSqlDao.load(objectId.toString(), objectType);
+    }
+
+    @Override
+    public List<Tag> loadTagsFromTransaction(final Transmogrifier dao, final UUID objectId, final String objectType) {
+        TagSqlDao tagSqlDao = dao.become(TagSqlDao.class);
+        return tagSqlDao.load(objectId.toString(), objectType);
+    }
+
+    @Override
+    public void addTag(final String tagName, final UUID objectId, final String objectType, final CallContext context) {
+        tagSqlDao.inTransaction(new Transaction<Void, TagSqlDao>() {
+            @Override
+            public Void inTransaction(final TagSqlDao tagSqlDao, final TransactionStatus status) throws Exception {
+                String tagId = UUID.randomUUID().toString();
+                tagSqlDao.addTagFromTransaction(tagId, tagName, objectId.toString(), objectType, context);
+
+                TagAuditSqlDao auditDao = tagSqlDao.become(TagAuditSqlDao.class);
+                auditDao.addTagFromTransaction(tagId, context);
+
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public void removeTag(final String tagName, final UUID objectId, final String objectType, final CallContext context) {
+        tagSqlDao.inTransaction(new Transaction<Void, TagSqlDao>() {
+            @Override
+            public Void inTransaction(final TagSqlDao tagSqlDao, final TransactionStatus status) throws Exception {
+                Tag tag = tagSqlDao.findTag(tagName, objectId.toString(), objectType);
+
+                if (tag == null) {
+                    throw new InvoiceApiException(ErrorCode.TAG_DOES_NOT_EXIST, tagName);
+                }
+
+                tagSqlDao.removeTagFromTransaction(tagName, objectId.toString(), objectType, context);
+
+                TagAuditSqlDao auditDao = tagSqlDao.become(TagAuditSqlDao.class);
+                auditDao.removeTagFromTransaction(tag.getId().toString(), context);
+
+                return null;
+            }
+        });
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
index dddb14f..884dacd 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.CallContext;
+import com.ning.billing.util.tag.ControlTagType;
 import org.skife.jdbi.v2.IDBI;
 import com.google.inject.Inject;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.util.api.TagDefinitionApiException;
-import com.ning.billing.util.clock.Clock;
-import com.ning.billing.util.tag.ControlTagType;
 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/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..11594ac
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
@@ -0,0 +1,39 @@
+/*
+ * 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.tag.dao;
+
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.tag.ControlTagType;
+import com.ning.billing.util.tag.Tag;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+
+import java.util.List;
+import java.util.UUID;
+
+public interface TagDao {
+    void saveTagsFromTransaction(Transmogrifier dao, UUID objectId, String objectType, List<Tag> tags, CallContext context);
+
+    void saveTags(UUID objectId, String objectType, List<Tag> tags, CallContext context);
+
+    List<Tag> loadTags(UUID objectId, String objectType);
+
+    List<Tag> loadTagsFromTransaction(Transmogrifier dao, UUID objectId, String objectType);
+
+    void addTag(String tagName, UUID objectId, String objectType, CallContext context);
+
+    void removeTag(String tagName, UUID objectId, String objectType, CallContext context);
+}
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..112ff75 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.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..1308ae1 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.CallContext;
+import com.ning.billing.util.callcontext.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/TaggableDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TaggableDao.java
new file mode 100644
index 0000000..de45f87
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TaggableDao.java
@@ -0,0 +1,28 @@
+/*
+ * 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.tag.dao;
+
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.tag.ControlTagType;
+
+import java.util.UUID;
+
+public interface TaggableDao {
+    public void addControlTag(ControlTagType controlTagType, UUID objectId, CallContext context);
+
+    public void removeControlTag(ControlTagType controlTagType, UUID objectId, CallContext context);
+}
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 4e3804f..211ae5a 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,39 +19,37 @@ package com.ning.billing.util.tag.dao;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.UUID;
+
+import com.ning.billing.util.dao.MapperBase;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
 import com.ning.billing.util.tag.ControlTagType;
 import com.ning.billing.util.tag.DefaultControlTag;
-import com.ning.billing.util.tag.DefaultTagDefinition;
 import com.ning.billing.util.tag.DescriptiveTag;
 import com.ning.billing.util.tag.Tag;
-import com.ning.billing.util.tag.TagDefinition;
 
-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"));
+        ControlTagType thisTagType = null;
+        for (ControlTagType controlTagType : ControlTagType.values()) {
+            if (name.equals(controlTagType.toString())) {
+                thisTagType = controlTagType;
+            }
+        }
 
-        Tag tag;
-        try {
-            ControlTagType controlTagType = ControlTagType.valueOf(name);
-            tag = new DefaultControlTag(id, addedBy, addedDate, controlTagType);
-        } catch (Throwable t) {
-            String description = result.getString("tag_description");
+        if (thisTagType == null) {
+            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);
+        } else {
+            return new DefaultControlTag(thisTagType);
         }
-
-        return tag;
     }
 }
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagSqlDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagSqlDao.java
new file mode 100644
index 0000000..a20e757
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagSqlDao.java
@@ -0,0 +1,78 @@
+/*
+ * 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.tag.dao;
+
+import java.util.List;
+
+import com.ning.billing.util.ChangeType;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.dao.ChangeTypeBinder;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.SqlBatch;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
+import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+import com.ning.billing.util.entity.EntityCollectionDao;
+import com.ning.billing.util.tag.Tag;
+
+@ExternalizedSqlViaStringTemplate3
+@RegisterMapper(TagMapper.class)
+public interface TagSqlDao extends EntityCollectionDao<Tag>, Transactional<TagSqlDao>, Transmogrifier {
+    @Override
+    @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);
+
+    @Override
+    @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);
+
+    @SqlBatch(transactional = false)
+    public void batchInsertHistoryFromTransaction(@Bind("objectId") final String objectId,
+                                                  @Bind("objectType") final String objectType,
+                                                  @Bind("historyRecordId") final List<String> historyRecordIdList,
+                                                  @TagBinder final List<Tag> tags,
+                                                  @ChangeTypeBinder final ChangeType changeType,
+                                                  @CallContextBinder final CallContext context);
+
+    @SqlUpdate
+    public void addTagFromTransaction(@Bind("id") final String tagId,
+                                      @Bind("tagDefinitionName") final String tagName,
+                                      @Bind("objectId") final String objectId,
+                                      @Bind("objectType") final String objectType,
+                                      @CallContextBinder final CallContext context);
+
+    @SqlUpdate
+    public void removeTagFromTransaction(@Bind("tagDefinitionName") final String tagName,
+                                         @Bind("objectId") final String objectId,
+                                         @Bind("objectType") final String objectType,
+                                         @CallContextBinder final CallContext context);
+
+    @SqlQuery
+    public Tag findTag(@Bind("tagDefinitionName") final String tagName,
+                       @Bind("objectId") final String objectId,
+                       @Bind("objectType") final String objectType);
+}
\ No newline at end of file
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 588beb7..662de60 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
@@ -22,15 +22,16 @@ 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/audit/dao/AuditSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/audit/dao/AuditSqlDao.sql.stg
new file mode 100644
index 0000000..25cc9d4
--- /dev/null
+++ b/util/src/main/resources/com/ning/billing/util/audit/dao/AuditSqlDao.sql.stg
@@ -0,0 +1,16 @@
+group AuditSqlDao;
+
+fields(prefix) ::= <<
+    <prefix>table_name,
+    <prefix>record_id,
+    <prefix>change_type,
+    <prefix>change_date,
+    <prefix>changed_by,
+    <prefix>reason_code,
+    <prefix>comments
+>>
+
+insertAuditFromTransaction() ::= <<
+    INSERT INTO audit_log(<fields()>)
+    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, NULL, NULL);
+>>
\ No newline at end of file
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..99a2830
--- /dev/null
+++ b/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldAuditSqlDao.sql.stg
@@ -0,0 +1,17 @@
+group CustomFieldAuditSqlDao;
+
+fields(prefix) ::= <<
+    <prefix>table_name,
+    <prefix>record_id,
+    <prefix>change_type,
+    <prefix>change_date,
+    <prefix>changed_by,
+    <prefix>reason_code,
+    <prefix>comments
+>>
+
+batchInsertAuditLogFromTransaction() ::= <<
+    INSERT INTO audit_log(<fields()>)
+    VALUES('custom_field_history', :id, :changeType, :createdDate, :userName, NULL, NULL);
+>>
+;
\ No newline at end of file
diff --git a/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldHistorySqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldHistorySqlDao.sql.stg
new file mode 100644
index 0000000..c9e2ab0
--- /dev/null
+++ b/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldHistorySqlDao.sql.stg
@@ -0,0 +1,18 @@
+group CustomFieldHistorySqlDao;
+
+fields(prefix) ::= <<
+  <prefix>history_id,
+  <prefix>id,
+  <prefix>object_id,
+  <prefix>object_type,
+  <prefix>field_name,
+  <prefix>field_value,
+  <prefix>updated_by,
+  <prefix>date,
+  <prefix>change_type
+>>
+
+batchAddHistoryFromTransaction() ::= <<
+    INSERT INTO custom_field_history(<fields()>)
+    VALUES(:historyId, :id, :objectId, :objectType, :fieldName, :fieldValue, :userName, :updatedDate, :changeType);
+>>
\ No newline at end of file
diff --git a/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.sql.stg
new file mode 100644
index 0000000..1fbf994
--- /dev/null
+++ b/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.sql.stg
@@ -0,0 +1,29 @@
+group CustomFieldSqlDao;
+
+batchInsertFromTransaction() ::= <<
+    INSERT INTO custom_fields(id, object_id, object_type, field_name, field_value, created_by, created_date, updated_by, updated_date)
+    VALUES (:id, :objectId, :objectType, :fieldName, :fieldValue, :userName, :createdDate, :userName, :updatedDate);
+>>
+
+batchUpdateFromTransaction() ::= <<
+    UPDATE custom_fields
+    SET field_value = :fieldValue, updated_by = :userName, updated_date = :updatedDate
+    WHERE object_id = :objectId AND object_type = :objectType AND field_name = :fieldName;
+>>
+
+batchDeleteFromTransaction() ::= <<
+    DELETE FROM custom_fields
+    WHERE object_id = :objectId AND object_type = :objectType AND field_name = :fieldName;
+>>
+
+load() ::= <<
+    SELECT id, object_id, object_type, field_name, field_value, created_by, created_date, updated_by, updated_date
+    FROM custom_fields
+    WHERE object_id = :objectId AND object_type = :objectType;
+>>
+
+test() ::= <<
+    SELECT 1 FROM custom_fields;
+>>
+;
+
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 f7db1db..77b66c9 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(50) NOT NULL,
   created_date datetime NOT NULL,
-  updated_date datetime NOT NULL,
+  updated_by varchar(50) 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);
@@ -14,40 +16,26 @@ CREATE UNIQUE INDEX custom_fields_unique ON custom_fields(object_id, object_type
 
 DROP TABLE IF EXISTS custom_field_history;
 CREATE TABLE custom_field_history (
+  history_id char(36) NOT NULL,
   id char(36) NOT NULL,
   object_id char(36) NOT NULL,
   object_type varchar(30) NOT NULL,
   field_name varchar(30),
   field_value varchar(255),
+  updated_by varchar(50) NOT NULL,
   date datetime NOT NULL,
   change_type char(6) NOT NULL
 ) 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(50) 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 +46,21 @@ CREATE TABLE tag_definition_history (
   name varchar(30) 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(50) 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(50) NOT NULL,
+  created_date datetime NOT NULL,
   PRIMARY KEY(id)
 ) ENGINE = innodb;
 CREATE INDEX tags_by_object ON tags(object_id);
@@ -94,30 +68,17 @@ CREATE UNIQUE INDEX tags_unique ON tags(tag_definition_name, object_id);
 
 DROP TABLE IF EXISTS tag_history;
 CREATE TABLE tag_history (
-  id char(36) NULL,
+  history_record_id char(36) NOT NULL,
+  id char(36) NOT NULL,
   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(50) 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 +98,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,
+    comments 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..e3262a4
--- /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>comments
+>>
+
+addTagFromTransaction() ::= <<
+    INSERT INTO audit_log(<fields()>)
+    VALUES('tag', :id, 'INSERT', :createdDate, :userName, NULL, NULL);
+>>
+
+removeTagFromTransaction() ::= <<
+    INSERT INTO audit_log(<fields()>)
+    VALUES('tag', :id, 'DELETE', :updatedDate, :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..54bdfa9 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, :createdDate);
 >>
 
 load() ::= <<
diff --git a/util/src/main/resources/com/ning/billing/util/tag/dao/TagSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/tag/dao/TagSqlDao.sql.stg
new file mode 100644
index 0000000..8f98452
--- /dev/null
+++ b/util/src/main/resources/com/ning/billing/util/tag/dao/TagSqlDao.sql.stg
@@ -0,0 +1,59 @@
+group TagDao;
+
+fields(prefix) ::= <<
+    <prefix>id,
+    <prefix>tag_definition_name,
+    <prefix>object_id,
+    <prefix>object_type,
+    <prefix>created_by,
+    <prefix>created_date
+>>
+
+batchInsertFromTransaction() ::= <<
+  INSERT INTO tags(<fields()>)
+  VALUES (:id, :tagDefinitionName, :objectId, :objectType, :userName, :createdDate);
+>>
+
+batchInsertHistoryFromTransaction() ::= <<
+    INSERT INTO tag_history (history_record_id, id, tag_definition_name, object_id, object_type, change_type, updated_by, date)
+    VALUES (:historyRecordId, :id, :tagDefinitionName, :objectId, :objectType, :changeType, :userName, :updatedDate);
+>>
+
+batchDeleteFromTransaction() ::= <<
+    DELETE FROM tags
+    WHERE tag_definition_name = :tagDefinitionName
+        AND object_id = :objectId AND object_type = :objectType;
+>>
+
+addTagFromTransaction() ::= <<
+    INSERT INTO tags(<fields()>)
+    VALUES (:id, :tagDefinitionName, :objectId, :objectType, :userName, :createdDate);
+>>
+
+removeTagFromTransaction() ::= <<
+    DELETE FROM tags
+    WHERE tag_definition_name = :tagDefinitionName
+    AND object_id = :objectId AND object_type = :objectType;
+>>
+
+findTag() ::= <<
+    SELECT <fields()>   FROM tags
+    WHERE tag_definition_name = :tagDefinitionName
+    AND object_id = :objectId AND object_type = :objectType;
+>>
+
+load() ::= <<
+    SELECT t.id, t.object_id, t.object_type, t.created_by, t.created_date,
+           td.id AS tag_definition_id,
+           t.tag_definition_name AS tag_definition_name,
+           td.description AS tag_description,
+           td.created_by
+    FROM tags t
+    LEFT JOIN tag_definitions td ON t.tag_definition_name = td.name
+    WHERE t.object_id = :objectId AND t.object_type = :objectType;
+>>
+
+test() ::= <<
+  SELECT 1 FROM tags;
+>>
+;
\ No newline at end of file
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 98d498c..866289e 100644
--- a/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
+++ b/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
@@ -134,7 +134,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/callcontext/TestCallContext.java b/util/src/test/java/com/ning/billing/util/callcontext/TestCallContext.java
new file mode 100644
index 0000000..b632f2e
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/callcontext/TestCallContext.java
@@ -0,0 +1,64 @@
+/*
+ * 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.callcontext;
+
+import com.ning.billing.util.clock.DefaultClock;
+import org.joda.time.DateTime;
+
+public class TestCallContext implements CallContext {
+    private final String userName;
+    private final DateTime updatedDate;
+    private final DateTime createdDate;
+
+    public TestCallContext(String userName) {
+        this.userName = userName;
+        DateTime now = new DefaultClock().getUTCNow();
+        this.updatedDate = now;
+        this.createdDate = now;
+    }
+
+    public TestCallContext(String userName, DateTime createdDate, DateTime updatedDate) {
+        this.userName = userName;
+        this.createdDate = createdDate;
+        this.updatedDate = updatedDate;
+    }
+
+    @Override
+    public String getUserName() {
+        return userName;
+    }
+
+    @Override
+    public CallOrigin getCallOrigin() {
+        return CallOrigin.TEST;
+    }
+
+    @Override
+    public UserType getUserType() {
+        return UserType.TEST;
+    }
+
+    @Override
+    public DateTime getCreatedDate() {
+        return createdDate;
+    }
+
+    @Override
+    public DateTime getUpdatedDate() {
+        return updatedDate;
+    }
+}
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 9bc67dd..8a2385b 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,29 @@
 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.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
+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.customfield.dao.CustomFieldDao;
+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,7 +48,9 @@ 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;
+    private CustomFieldDao customFieldDao;
 
     @BeforeClass(groups = {"util", "slow"})
     protected void setup() throws IOException {
@@ -50,6 +62,13 @@ public class TestFieldStore {
             helper.initDb(utilDdl);
 
             dbi = helper.getDBI();
+            customFieldDao = new AuditedCustomFieldDao();
+
+            FieldStoreModule module = new FieldStoreModule();
+            final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module, new MockClockModule());
+            Clock clock = injector.getInstance(Clock.class);
+            context = new DefaultCallContextFactory(clock).createCallContext("Fezzik", CallOrigin.TEST, UserType.TEST);
+
         }
         catch (Throwable t) {
             log.error("Setup failed", t);
@@ -74,38 +93,22 @@ 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);
+        customFieldDao.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;
-            }
-        });
-
+        customFieldDao.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/notificationq/TestNotificationQueue.java b/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java
index f9e874e..fefbcdb 100644
--- a/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java
+++ b/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java
@@ -178,9 +178,9 @@ public class TestNotificationQueue {
             }
         });
 
-        Assert.assertTrue(expectedNotifications.get(notificationKey.toString()));
         queue.stopQueue();
-    }
+	    Assert.assertTrue(expectedNotifications.get(notificationKey.toString()));
+	}
 
     @Test(groups="slow")
     public void testManyNotifications() throws InterruptedException {
@@ -252,18 +252,18 @@ public class TestNotificationQueue {
                     }
                 });
 
-                if (completed.size() == MAX_NOTIFICATIONS) {
-                    success = true;
-                    break;
-                }
-                //log.debug(String.format("BEFORE WAIT : Got %d notifications at time %s (real time %s)", completed.size(), clock.getUTCNow(), new DateTime()));
-                expectedNotifications.wait(1000);
-            }
-        } while (nbTry-- > 0);
-        assertEquals(success, true);
-        queue.stopQueue();
+				if (completed.size() == MAX_NOTIFICATIONS) {
+					success = true;
+					break;
+				}
+				//log.debug(String.format("BEFORE WAIT : Got %d notifications at time %s (real time %s)", completed.size(), clock.getUTCNow(), new DateTime()));
+				expectedNotifications.wait(1000);
+			}
+		} while (nbTry-- > 0);
 
-    }
+        queue.stopQueue();
+		assertEquals(success, true);
+	}
 
     /**
      * Test that we can post a notification in the future from a transaction and get the notification
@@ -380,10 +380,10 @@ public class TestNotificationQueue {
             // expected behavior
         }
 
-        Assert.assertTrue(expectedNotificationsFred.get(notificationKeyFred.toString()));
-        Assert.assertFalse(expectedNotificationsFred.get(notificationKeyBarney.toString()));
         queueFred.stopQueue();
-    }
+		Assert.assertTrue(expectedNotificationsFred.get(notificationKeyFred.toString()));
+		Assert.assertFalse(expectedNotificationsFred.get(notificationKeyBarney.toString()));
+	}
 
     NotificationConfig getNotificationConfig(final boolean off,
             final long sleepTime, final int maxReadyEvents, final long claimTimeMs) {
diff --git a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
new file mode 100644
index 0000000..553bc3c
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
@@ -0,0 +1,106 @@
+/*
+ * 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.tag.dao;
+
+import com.google.inject.Inject;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.tag.Tag;
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+public class MockTagDao implements TagDao {
+    private Map<UUID, List<Tag>> tagStore = new HashMap<UUID, List<Tag>>();
+    private final Clock clock;
+
+    @Inject
+    public MockTagDao(Clock clock) {
+        this.clock = clock;
+    }
+
+    @Override
+    public void saveTagsFromTransaction(final Transmogrifier dao, final UUID objectId, final String objectType,
+                                        final List<Tag> tags, final CallContext context) {
+        tagStore.put(objectId, tags);
+    }
+
+    @Override
+    public void saveTags(UUID objectId, String objectType, List<Tag> tags, CallContext context) {
+        tagStore.put(objectId, tags);
+    }
+
+    @Override
+    public List<Tag> loadTags(UUID objectId, String objectType) {
+        return tagStore.get(objectId);
+    }
+
+    @Override
+    public List<Tag> loadTagsFromTransaction(Transmogrifier dao, UUID objectId, String objectType) {
+        return tagStore.get(objectId);
+    }
+
+    @Override
+    public void addTag(final String tagName, final UUID objectId, final String objectType, final CallContext context) {
+        Tag tag = new Tag() {
+            private UUID id = UUID.randomUUID();
+            private DateTime createdDate = clock.getUTCNow();
+
+            @Override
+            public String getTagDefinitionName() {
+                return tagName;
+            }
+
+            @Override
+            public UUID getId() {
+                return id;
+            }
+
+            @Override
+            public String getCreatedBy() {
+                return context.getUserName();
+            }
+
+            @Override
+            public DateTime getCreatedDate() {
+                return createdDate;
+            }
+        };
+
+
+        tagStore.get(objectId).add(tag);
+    }
+
+    @Override
+    public void removeTag(String tagName, UUID objectId, String objectType, CallContext context) {
+        List<Tag> tags = tagStore.get(objectId);
+        if (tags != null) {
+            Iterator<Tag> tagIterator = tags.iterator();
+            while (tagIterator.hasNext()) {
+                Tag tag = tagIterator.next();
+                if (tag.getTagDefinitionName().equals(tagName)) {
+                    tagIterator.remove();
+                }
+            }
+        }
+    }
+}
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..adcb848 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.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/MockTagStoreModuleMemory.java b/util/src/test/java/com/ning/billing/util/tag/MockTagStoreModuleMemory.java
new file mode 100644
index 0000000..3acc3a1
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/tag/MockTagStoreModuleMemory.java
@@ -0,0 +1,31 @@
+/*
+ * 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.tag;
+
+import com.ning.billing.util.glue.TagStoreModule;
+import com.ning.billing.util.tag.dao.MockTagDao;
+import com.ning.billing.util.tag.dao.MockTagDefinitionDao;
+import com.ning.billing.util.tag.dao.TagDao;
+import com.ning.billing.util.tag.dao.TagDefinitionDao;
+
+public class MockTagStoreModuleMemory extends TagStoreModule {
+    @Override
+    protected void installDaos() {
+        bind(TagDefinitionDao.class).to(MockTagDefinitionDao.class).asEagerSingleton();
+        bind(TagDao.class).to(MockTagDao.class).asEagerSingleton();
+    }
+}
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 90ccaea..a8d353f 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
@@ -18,12 +18,22 @@ package com.ning.billing.util.tag;
 
 import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.tag.dao.TagDao;
 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.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.skife.jdbi.v2.tweak.HandleCallback;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -36,10 +46,7 @@ import com.google.inject.Inject;
 import com.ning.billing.dbi.MysqlTestingHelper;
 
 import com.ning.billing.util.api.TagDefinitionApiException;
-import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.tag.dao.TagDefinitionDao;
-import com.ning.billing.util.tag.dao.TagStoreSqlDao;
-
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
@@ -48,10 +55,8 @@ import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
 @Test(groups={"slow"})
-@Guice(modules = TagStoreModuleMock.class)
+@Guice(modules = MockTagStoreModuleSql.class)
 public class TestTagStore {
-    private final static String ACCOUNT_TYPE = "ACCOUNT";
-
     @Inject
     private MysqlTestingHelper helper;
 
@@ -59,19 +64,19 @@ public class TestTagStore {
     private IDBI dbi;
 
     @Inject
-    private TagStoreSqlDao tagStoreSqlDao;
-
+    private TagDao tagDao;
+    
     @Inject
     private TagDefinitionDao tagDefinitionDao;
 
     @Inject
     private Clock clock;
 
-    private TagDefinition tag1;
-    private TagDefinition tag2;
-
+    private TagDefinition testTag;
+    private TestSqlDao dao;
 
     private final Logger log = LoggerFactory.getLogger(TestTagStore.class);
+    private CallContext context;
 
     @BeforeClass(groups="slow")
     protected void setup() throws IOException {
@@ -81,12 +86,13 @@ public class TestTagStore {
 
             helper.startMysql();
             helper.initDb(utilDdl);
-            tagStoreSqlDao.test();
 
+            context = new DefaultCallContextFactory(clock).createCallContext("Tag store test", CallOrigin.TEST, UserType.TEST);
+            dao = dbi.onDemand(TestSqlDao.class);
+            
             cleanupTags();
-            tag1 = tagDefinitionDao.create("tag1", "First tag", "test");
-            tag2 = tagDefinitionDao.create("tag2", "Second tag", "test");
-
+            tagDefinitionDao.create("tag1", "First tag", context);
+            testTag = tagDefinitionDao.create("testTag", "Second tag", context);
         }
         catch (Throwable t) {
             log.error("Failed to start tag store tests", t);
@@ -100,18 +106,6 @@ public class TestTagStore {
         helper.stopMysql();
     }
 
-    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 cleanupTags() {
         try {
             helper.getDBI().withHandle(new HandleCallback<Void>() {
@@ -131,19 +125,16 @@ public class TestTagStore {
     public void testTagCreationAndRetrieval() {
         UUID accountId = UUID.randomUUID();
 
-        TagStore tagStore = new DefaultTagStore(accountId, ACCOUNT_TYPE);
-        Tag tag = new DescriptiveTag(tag2, "test", clock.getUTCNow());
+        TagStore tagStore = new DefaultTagStore(accountId, Account.ObjectType);
+        Tag tag = new DescriptiveTag(testTag);
         tagStore.add(tag);
 
-        TagStoreSqlDao dao = dbi.onDemand(TagStoreSqlDao.class);
-        saveTags(dao, ACCOUNT_TYPE, accountId.toString(), tagStore.getEntityList());
+        tagDao.saveTagsFromTransaction(dao, accountId, Account.ObjectType, tagStore.getEntityList(), context);
 
-        List<Tag> savedTags = dao.load(accountId.toString(), ACCOUNT_TYPE);
+        List<Tag> savedTags = tagDao.loadTags(accountId, Account.ObjectType);
         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());
     }
@@ -152,19 +143,19 @@ public class TestTagStore {
     @Test(groups="slow")
     public void testControlTagCreation() {
         UUID accountId = UUID.randomUUID();
-        TagStore tagStore = new DefaultTagStore(accountId, ACCOUNT_TYPE);
+        TagStore tagStore = new DefaultTagStore(accountId, Account.ObjectType);
 
-        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);
+        tagDao.saveTagsFromTransaction(dao, accountId, Account.ObjectType, tagList, context);
 
         tagStore.clear();
         assertEquals(tagStore.getEntityList().size(), 0);
 
-        tagList = tagStoreSqlDao.load(accountId.toString(), ACCOUNT_TYPE);
+        tagList = tagDao.loadTags(accountId, Account.ObjectType);
         tagStore.add(tagList);
         assertEquals(tagList.size(), 1);
 
@@ -174,27 +165,26 @@ public class TestTagStore {
     @Test(groups="slow")
     public void testDescriptiveTagCreation() {
         UUID accountId = UUID.randomUUID();
-        TagStore tagStore = new DefaultTagStore(accountId, ACCOUNT_TYPE);
+        TagStore tagStore = new DefaultTagStore(accountId, Account.ObjectType);
 
         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);
+        tagDao.saveTagsFromTransaction(dao, accountId, Account.ObjectType, tagStore.getEntityList(), context);
 
         tagStore.clear();
         assertEquals(tagStore.getEntityList().size(), 0);
 
-        tagList = tagStoreSqlDao.load(accountId.toString(), ACCOUNT_TYPE);
+        List<Tag> tagList = tagDao.loadTags(accountId, Account.ObjectType);
         tagStore.add(tagList);
         assertEquals(tagList.size(), 1);
 
@@ -204,31 +194,30 @@ public class TestTagStore {
     @Test(groups="slow")
     public void testMixedTagCreation() {
         UUID accountId = UUID.randomUUID();
-        TagStore tagStore = new DefaultTagStore(accountId, ACCOUNT_TYPE);
+        TagStore tagStore = new DefaultTagStore(accountId, Account.ObjectType);
 
         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);
+        tagDao.saveTagsFromTransaction(dao, accountId, Account.ObjectType, tagStore.getEntityList(), context);
 
         tagStore.clear();
         assertEquals(tagStore.getEntityList().size(), 0);
 
-        tagList = tagStoreSqlDao.load(accountId.toString(), ACCOUNT_TYPE);
+        List<Tag> tagList = tagDao.loadTags(accountId, Account.ObjectType);
         tagStore.add(tagList);
         assertEquals(tagList.size(), 2);
 
@@ -238,16 +227,16 @@ public class TestTagStore {
     @Test(groups="slow")
     public void testControlTags() {
         UUID accountId = UUID.randomUUID();
-        TagStore tagStore = new DefaultTagStore(accountId, ACCOUNT_TYPE);
+        TagStore tagStore = new DefaultTagStore(accountId, Account.ObjectType);
         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_PAY_OFF);
+        ControlTag paymentTag = new DefaultControlTag(ControlTagType.AUTO_PAY_OFF);
         tagStore.add(paymentTag);
         assertEquals(tagStore.generateInvoice(), false);
         assertEquals(tagStore.processPayment(), false);
@@ -256,18 +245,18 @@ public class TestTagStore {
     @Test(groups="slow", expectedExceptions = TagDefinitionApiException.class)
     public void testTagDefinitionCreationWithControlTagName() throws TagDefinitionApiException {
         String definitionName = ControlTagType.AUTO_PAY_OFF.toString();
-        tagDefinitionDao.create(definitionName, "This should break", "test");
+        tagDefinitionDao.create(definitionName, "This should break", context);
     }
 
     @Test(groups="slow")
     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);
     }
@@ -275,30 +264,29 @@ public class TestTagStore {
     @Test(groups="slow", 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);
 
         UUID objectId = UUID.randomUUID();
-        String objectType = "TestType";
-        TagStore tagStore = new DefaultTagStore(objectId, objectType);
-        Tag tag = new DescriptiveTag(tagDefinition, "test", clock.getUTCNow());
+        TagStore tagStore = new DefaultTagStore(objectId, Account.ObjectType);
+        Tag tag = new DescriptiveTag(tagDefinition);
         tagStore.add(tag);
 
-        saveTags(tagStoreSqlDao, objectType, objectId.toString(), tagStore.getEntityList());
+        tagDao.saveTags(objectId, Account.ObjectType, tagStore.getEntityList(), context);
 
-        List<Tag> tags = tagStoreSqlDao.load(objectId.toString(), objectType);
+        List<Tag> tags = tagDao.loadTags(objectId, Account.ObjectType);
         assertEquals(tags.size(), 1);
 
-        tagDefinitionDao.deleteTagDefinition(definitionName);
+        tagDefinitionDao.deleteTagDefinition(definitionName, context);
     }
 
     @Test(groups="slow")
     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);
         }
@@ -307,24 +295,23 @@ public class TestTagStore {
         assertNotNull(tagDefinition);
 
         UUID objectId = UUID.randomUUID();
-        String objectType = "TestType";
-        TagStore tagStore = new DefaultTagStore(objectId, objectType);
-        Tag tag = new DescriptiveTag(tagDefinition, "test", clock.getUTCNow());
+        TagStore tagStore = new DefaultTagStore(objectId, Account.ObjectType);
+        Tag tag = new DescriptiveTag(tagDefinition);
         tagStore.add(tag);
 
-        saveTags(tagStoreSqlDao, objectType, objectId.toString(), tagStore.getEntityList());
+        tagDao.saveTags(objectId, Account.ObjectType, tagStore.getEntityList(), context);
 
-        List<Tag> tags = tagStoreSqlDao.load(objectId.toString(), objectType);
+        List<Tag> tags = tagDao.loadTags(objectId, Account.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);
         }
@@ -334,7 +321,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);
         }
@@ -343,13 +330,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);
         }
@@ -360,7 +347,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);
         }
@@ -368,10 +355,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);
         }
@@ -382,4 +369,64 @@ 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.ObjectType);
+        Tag tag = new DescriptiveTag(testTag);
+        tagStore.add(tag);
+
+        tagDao.saveTagsFromTransaction(dao, accountId, Account.ObjectType, tagStore.getEntityList(), context);
+
+        List<Tag> savedTags = tagDao.loadTags(accountId, Account.ObjectType);
+        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 a inner join tag_history th on a.record_id = th.history_record_id where a.table_name = 'tag_history' and th.id='%s' and a.change_type='INSERT'",
+                                     tag.getId().toString());
+        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.getCreatedDate()).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.ObjectType);
+        Tag tag = new DescriptiveTag(testTag);
+        tagStore.add(tag);
+
+        tagDao.saveTags(accountId, Account.ObjectType, tagStore.getEntityList(), context);
+
+        tagStore.remove(tag);
+        tagDao.saveTags(accountId, Account.ObjectType, tagStore.getEntityList(), context);
+
+        List<Tag> savedTags = tagDao.loadTags(accountId, Account.ObjectType);
+        assertEquals(savedTags.size(), 0);
+
+        Handle handle = dbi.open();
+        String query = String.format("select * from audit_log a inner join tag_history th on a.record_id = th.history_record_id where a.table_name = 'tag_history' and th.id='%s' and a.change_type='DELETE'",
+                                     tag.getId().toString());
+        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.getUpdatedDate()).getSeconds() < 2);
+        assertEquals(result.get(0).get("changed_by"), context.getUserName());
+    }
+    
+    public interface TestSqlDao extends Transmogrifier {}
 }