killbill-memoizeit

writeoff tag for invoices; added accountId to invoice items;

3/26/2012 7:53:48 PM

Changes

util/src/main/java/com/ning/billing/util/entity/ChangeType.java 7(+0 -7)

Details

diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java b/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
index a74d3b1..31f0a37 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
@@ -21,6 +21,7 @@ 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;
 
@@ -30,18 +31,17 @@ 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;
@@ -76,8 +76,9 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
 	 * @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 String createdBy, final DateTime createdDate,
-                          final String updatedBy, final DateTime updatedDate, 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(),
@@ -136,7 +137,6 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
 		this.postalCode = postalCode;
 		this.country = country;
 		this.phone = phone;
-		this.tags = new DefaultTagStore(id, getObjectName());
         this.updatedBy = updatedBy;
         this.updatedDate = updatedDate;
 	}
@@ -158,7 +158,7 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
 
     @Override
 	public String getObjectName() {
-		return "Account";
+		return ObjectType;
 	}
 
 	@Override
@@ -257,49 +257,6 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
 	}
 
 	@Override
-	public List<Tag> getTagList() {
-		return tags.getEntityList();
-	}
-
-	@Override
-	public boolean hasTag(String tagName) {
-		return tags.containsTag(tagName);
-	}
-
-	@Override
-	public void addTag(TagDefinition definition) {
-		Tag tag = new DescriptiveTag(definition);
-		tags.add(tag) ;
-	}
-
-	@Override
-	public void addTags(List<Tag> tags) {
-		if (tags != null) {
-			this.tags.add(tags);
-		}
-	}
-
-	@Override
-	public void clearTags() {
-		this.tags.clear();
-	}
-
-	@Override
-	public void removeTag(TagDefinition definition) {
-		tags.remove(definition.getName());
-	}
-
-	@Override
-	public boolean generateInvoice() {
-		return tags.generateInvoice();
-	}
-
-	@Override
-	public boolean processPayment() {
-		return tags.processPayment();
-	}
-
-	@Override
 	public String toString() {
 		return "DefaultAccount [externalKey=" + externalKey +
                 ", email=" + email +
@@ -319,6 +276,7 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
 				", 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/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/test/java/com/ning/billing/account/glue/AccountModuleWithEmbeddedDb.java b/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithEmbeddedDb.java
index 9ed15a3..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,8 +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;
@@ -45,6 +48,8 @@ public class AccountModuleWithEmbeddedDb extends AccountModule {
         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 128ef23..5292161 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
@@ -20,6 +20,8 @@ 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.glue.TagStoreModule;
 
 public class AccountModuleWithMocks extends AccountModule {
     @Override
@@ -33,5 +35,7 @@ public class AccountModuleWithMocks extends AccountModule {
         super.configure();
         install(new MockClockModule());
         install(new CallContextModule());
+        install(new TagStoreModule());
+        install(new FieldStoreModule());
     }
 }
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 a820bc2..22c667f 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
@@ -247,7 +247,7 @@ public class TestAnalyticsService {
     private void createInvoiceAndPaymentCreationEvents(final Account account) {
         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),
+                UUID.randomUUID(), account.getId(), invoice.getId(), UUID.randomUUID(), "somePlan", "somePhase", clock.getUTCNow(), clock.getUTCNow().plusDays(1),
                 INVOICE_AMOUNT, ACCOUNT_CURRENCY, context.getUserName(), clock.getUTCNow());
         invoice.addInvoiceItem(invoiceItem);
 
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 0ef5df8..3901426 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
@@ -27,6 +27,8 @@ 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;
 
@@ -201,4 +203,44 @@ public class MockSubscription implements Subscription
     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/api/src/main/java/com/ning/billing/account/api/Account.java b/api/src/main/java/com/ning/billing/account/api/Account.java
index d93864a..3ffcf94 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
@@ -18,8 +18,9 @@ package com.ning.billing.account.api;
 
 import com.ning.billing.util.entity.UpdatableEntity;
 
-import com.ning.billing.util.customfield.CustomizableEntity;
+import com.ning.billing.util.customfield.Customizable;
 import com.ning.billing.util.tag.Taggable;
 
-public interface Account extends AccountData, CustomizableEntity, UpdatableEntity, Taggable {
+public interface Account extends AccountData, Customizable, UpdatableEntity, Taggable {
+    public static String ObjectType = "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..dcfe2df 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,15 +21,14 @@ 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.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)
     throws EntitlementUserApiException;
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index 4dcf09f..7423d0c 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -134,6 +134,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/InvoiceUserApi.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
index 9c9ee4f..909f3c0 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
@@ -38,4 +38,8 @@ public interface InvoiceUserApi {
     public Collection<Invoice> getUnpaidInvoicesByAccountId(UUID accountId, DateTime upToDate);
     
     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/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/ExtendedEntity.java b/api/src/main/java/com/ning/billing/util/entity/ExtendedEntity.java
new file mode 100644
index 0000000..48d26d5
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/entity/ExtendedEntity.java
@@ -0,0 +1,7 @@
+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/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/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 272bdb6..91f663b 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
@@ -38,8 +38,8 @@ 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;
@@ -51,7 +51,7 @@ 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);
 
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 3730d72..cd688d3 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
@@ -25,7 +25,7 @@ import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.util.callcontext.CallContext;
-import com.ning.billing.util.customfield.dao.AuditedCustomFieldDao;
+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;
@@ -79,10 +79,12 @@ public class EntitlementSqlDao implements EntitlementDao {
     private final SubscriptionFactory factory;
     private final NotificationQueueService notificationQueueService;
     private final AddonUtils addonUtils;
+    private final CustomFieldDao customFieldDao;
 
     @Inject
     public EntitlementSqlDao(final IDBI dbi, final Clock clock, final SubscriptionFactory factory,
-            final AddonUtils addonUtils, final NotificationQueueService notificationQueueService) {
+                             final AddonUtils addonUtils, final NotificationQueueService notificationQueueService,
+                             final CustomFieldDao customFieldDao) {
         this.clock = clock;
         this.factory = factory;
         this.subscriptionsDao = dbi.onDemand(SubscriptionSqlDao.class);
@@ -90,6 +92,7 @@ public class EntitlementSqlDao implements EntitlementDao {
         this.bundlesDao = dbi.onDemand(BundleSqlDao.class);
         this.notificationQueueService = notificationQueueService;
         this.addonUtils = addonUtils;
+        this.customFieldDao = customFieldDao;
     }
 
     @Override
@@ -119,7 +122,6 @@ public class EntitlementSqlDao implements EntitlementDao {
         });
     }
 
-
     @Override
     public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId) {
         Subscription subscription = subscriptionsDao.getSubscriptionFromId(subscriptionId.toString());
@@ -148,7 +150,6 @@ public class EntitlementSqlDao implements EntitlementDao {
         return getBaseSubscription(bundleId, true);
     }
 
-
     @Override
     public Subscription getSubscriptionFromId(final UUID subscriptionId) {
         return buildSubscription(subscriptionsDao.getSubscriptionFromId(subscriptionId.toString()));
@@ -410,8 +411,7 @@ public class EntitlementSqlDao implements EntitlementDao {
     private void updateCustomFieldsFromTransaction(final SubscriptionSqlDao transactionalDao,
                                                    final SubscriptionData subscription,
                                                    final CallContext context) {
-        AuditedCustomFieldDao auditedDao = new AuditedCustomFieldDao();
-        auditedDao.saveFields(transactionalDao, subscription.getId(), subscription.getObjectName(), subscription.getFieldList(), context);
+        customFieldDao.saveFields(transactionalDao, subscription.getId(), subscription.getObjectName(), subscription.getFieldList(), context);
     }
 
     private Subscription buildSubscription(Subscription input) {
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..65d8495 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, SubscriptionFactory factory, AddonUtils addonUtils, NotificationQueueService notificationQueueService,
+                                 CustomFieldDao customFieldDao) {
+        super(dbi, clock, factory, addonUtils, notificationQueueService, customFieldDao);
         this.resetDao = dbi.onDemand(ResetSqlDao.class);
     }
 
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 3cbf002..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
@@ -49,7 +49,7 @@ public class DefaultInvoiceMigrationApi implements InvoiceMigrationApi {
 	public UUID createMigrationInvoice(UUID accountId, DateTime targetDate, BigDecimal balance, Currency currency) {
         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(), targetDate, balance, currency );
+		InvoiceItem migrationInvoiceItem = new MigrationInvoiceItem(migrationInvoice.getId(), accountId, targetDate, balance, currency );
 		migrationInvoice.addInvoiceItem(migrationInvoiceItem);
 		dao.create(migrationInvoice, context);
 		return migrationInvoice.getId();
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 dba195b..6109ca2 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
@@ -21,6 +21,7 @@ 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;
@@ -78,4 +79,14 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
             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 7b4ed71..3c32f4e 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
@@ -24,6 +24,12 @@ import java.util.Map;
 import java.util.UUID;
 
 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 com.ning.billing.util.tag.dao.TagSqlDao;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
@@ -49,20 +55,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 final  NextBillingDatePoster nextBillingDatePoster;
+	private final NextBillingDatePoster nextBillingDatePoster;
 
     @Inject
     public DefaultInvoiceDao(final IDBI dbi, final Bus eventBus,
                              final EntitlementBillingApi entitlementBillingApi,
-                             final 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
@@ -72,8 +81,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;
             }
@@ -87,8 +95,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;
     		}
@@ -102,8 +109,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;
             }
@@ -117,8 +123,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;
              }
@@ -133,8 +138,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;
@@ -195,8 +199,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;
             }
@@ -220,8 +223,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;
             }
@@ -239,10 +241,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);
@@ -265,13 +291,39 @@ 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 InvoiceSqlDao invoiceSqlDao) {
+        TagSqlDao tagSqlDao = invoiceSqlDao.become(TagSqlDao.class);
+        String invoiceId = invoice.getId().toString();
+        List<Tag> tags = tagSqlDao.load(invoiceId, 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) {
@@ -312,5 +364,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 de93098..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
@@ -79,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());
@@ -97,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");
@@ -107,7 +109,7 @@ public interface FixedPriceInvoiceItemSqlDao extends EntityDao<InvoiceItem> {
             String createdBy = result.getString("created_by");
             DateTime createdDate = new DateTime(result.getTimestamp("created_date"));
 
-            return new FixedPriceInvoiceItem(id, invoiceId, subscriptionId, planName, phaseName,
+            return new FixedPriceInvoiceItem(id, invoiceId, accountId, subscriptionId, planName, phaseName,
                                             startDate, endDate, amount, currency, createdBy, createdDate);
         }
     }
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 1a338ae..6dc7d9e 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
@@ -19,13 +19,15 @@ package com.ning.billing.invoice.dao;
 import com.ning.billing.invoice.api.Invoice;
 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 {
+public interface InvoiceDao extends TaggableDao {
     void create(Invoice invoice, CallContext context);
 
     Invoice getById(final UUID id);
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 35063d6..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
@@ -78,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());
@@ -98,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");
@@ -111,7 +113,7 @@ public interface RecurringInvoiceItemSqlDao extends EntityDao<InvoiceItem> {
             String createdBy = result.getString("created_by");
             DateTime createdDate = new DateTime(result.getTimestamp("created_date"));
 
-            return new RecurringInvoiceItem(id, invoiceId, subscriptionId, planName, phaseName, startDate, endDate,
+            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/model/DefaultInvoice.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
index 0e62345..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,15 +23,17 @@ 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.entity.EntityBase;
 
-public class DefaultInvoice extends EntityBase 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 accountId;
@@ -206,5 +208,24 @@ public class DefaultInvoice extends EntityBase 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 f1643d7..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
@@ -81,7 +81,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
 
         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);
@@ -165,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>();
 
@@ -176,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);
         }
@@ -211,7 +211,8 @@ 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(),
@@ -234,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;
@@ -245,7 +246,7 @@ 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);
             } else {
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 d712ffb..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,15 +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,
+    public FixedPriceInvoiceItem(UUID invoiceId, UUID accountId, UUID subscriptionId, String planName, String phaseName,
                                  DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
-        super(invoiceId, subscriptionId, planName, phaseName, startDate, endDate, amount, 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,
                                  String createdBy, DateTime createdDate) {
-        super(id, invoiceId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, createdBy, createdDate);
+        super(id, invoiceId, accountId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, createdBy, createdDate);
     }
 
     @Override
@@ -47,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;
     }
 
@@ -64,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;
         }
     }
 
@@ -78,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(", ");
@@ -101,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 7d2b517..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
@@ -21,11 +21,13 @@ 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 extends EntityBase implements InvoiceItem {
     protected final UUID invoiceId;
+    protected final UUID accountId;
     protected final UUID subscriptionId;
     protected final String planName;
     protected final String phaseName;
@@ -34,17 +36,18 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem 
     protected final BigDecimal amount;
     protected final Currency currency;
 
-    public InvoiceItemBase(UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
+    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, subscriptionId, planName, phaseName,
+        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,
-                           String createdBy, DateTime createdDate) {
+                           @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;
@@ -69,6 +72,11 @@ public abstract class InvoiceItemBase extends EntityBase 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/MigrationInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/MigrationInvoiceItem.java
index 8d654fd..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,13 +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) {
-		super(invoiceId, MIGRATION_SUBSCRIPTION_ID, MigrationPlan.MIGRATION_PLAN_NAME, MigrationPlan.MIGRATION_PLAN_PHASE_NAME,
+	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 c47d53e..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,42 +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) {
-        super(invoiceId, subscriptionId, planName, phaseName, startDate, endDate, amount, 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) {
-        super(invoiceId, subscriptionId, planName, phaseName, startDate, endDate,
+        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,
                                 String createdBy, DateTime createdDate) {
-        super(id, invoiceId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, createdBy, 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,
                                 String createdBy, DateTime createdDate) {
-        super(id, invoiceId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, createdBy, createdDate);
+        super(id, invoiceId, accountId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, createdBy, createdDate);
 
         this.rate = rate;
         this.reversedItemId = reversedItemId;
@@ -71,7 +71,7 @@ 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,
+        return new RecurringInvoiceItem(invoiceId, accountId, subscriptionId, planName, phaseName, startDate, endDate,
                 amountNegated, rate, currency, id);
     }
 
@@ -102,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;
         }
     }
 
@@ -123,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;
@@ -139,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();
@@ -162,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/resources/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.sql.stg
index efd7522..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,
@@ -41,13 +42,13 @@ getInvoiceItemsBySubscription() ::= <<
 
 create() ::= <<
   INSERT INTO fixed_invoice_items(<fields()>)
-  VALUES(:id, :invoiceId, :subscriptionId, :planName, :phaseName,
+  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,
+  VALUES(:id, :invoiceId, :accountId, :subscriptionId, :planName, :phaseName,
          :startDate, :endDate, :amount, :currency, :userName, :createdDate);
 >>
 
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 ec7688e..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,
@@ -43,13 +44,13 @@ getInvoiceItemsBySubscription() ::= <<
 
 create() ::= <<
   INSERT INTO recurring_invoice_items(<fields()>)
-  VALUES(:id, :invoiceId, :subscriptionId, :planName, :phaseName, :startDate, :endDate,
+  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,
+  VALUES(:id, :invoiceId, :accountId, :subscriptionId, :planName, :phaseName, :startDate, :endDate,
          :amount, :rate, :currency, :reversedItemId, :userName, :createdDate);
 >>
 
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 60bfaab..28e0c1f 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,
@@ -23,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,
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 435ba21..ff35bcd 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
@@ -41,6 +41,7 @@ import com.ning.billing.invoice.model.DefaultInvoicePayment;
 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.tag.ControlTagType;
 import org.joda.time.DateTime;
 import org.testng.annotations.Test;
 
@@ -51,6 +52,7 @@ 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;
@@ -84,7 +86,8 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         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,
+        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, context);
@@ -174,19 +177,19 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         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,
+        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,
+        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,
+        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,
+        RecurringInvoiceItem item4 = new RecurringInvoiceItem(invoiceId1, accountId, subscriptionId4, "test plan", "test D", startDate, endDate,
                 rate4, rate4, Currency.USD);
         recurringInvoiceItemDao.create(item4, context);
 
@@ -199,15 +202,15 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         startDate = endDate;
         endDate = startDate.plusMonths(1);
 
-        RecurringInvoiceItem item5 = new RecurringInvoiceItem(invoiceId2, subscriptionId1, "test plan", "test phase A", startDate, endDate,
+        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,
+        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,
+        RecurringInvoiceItem item7 = new RecurringInvoiceItem(invoiceId2, accountId, subscriptionId3, "test plan", "test phase C", startDate, endDate,
                 rate3, rate3, Currency.USD);
         recurringInvoiceItemDao.create(item7, context);
 
@@ -267,11 +270,11 @@ 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,
+        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,
+        RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, UUID.randomUUID(), "test plan", "test phase B", startDate,
                 endDate, rate2, rate2, Currency.USD);
         recurringInvoiceItemDao.create(item2, context);
 
@@ -296,11 +299,11 @@ 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,
+        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,
+        RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, UUID.randomUUID(), "test plan", "test phase B", startDate, endDate,
                 rate2, rate2, Currency.USD);
         recurringInvoiceItemDao.create(item2, context);
 
@@ -336,11 +339,11 @@ 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,
+        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,
+        RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, UUID.randomUUID(), "test plan", "test phase B", startDate, endDate,
                 rate2, rate2, Currency.USD);
         recurringInvoiceItemDao.create(item2, context);
 
@@ -364,7 +367,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
 
         BigDecimal rate3 = new BigDecimal("21.0");
 
-        RecurringInvoiceItem item3 = new RecurringInvoiceItem(invoice2.getId(), UUID.randomUUID(), "test plan", "test phase C", startDate2, endDate2,
+        RecurringInvoiceItem item3 = new RecurringInvoiceItem(invoice2.getId(), accountId, UUID.randomUUID(), "test plan", "test phase C", startDate2, endDate2,
                 rate3, rate3, Currency.USD);
         recurringInvoiceItemDao.create(item3, context);
 
@@ -489,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);
@@ -503,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);
@@ -511,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);
@@ -611,4 +615,70 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         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 0a682bb..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
@@ -34,13 +34,14 @@ import static org.testng.Assert.assertNotNull;
 public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
     @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");
 
-        RecurringInvoiceItem item = new RecurringInvoiceItem(invoiceId, subscriptionId, "test plan", "test phase", startDate, endDate,
+        RecurringInvoiceItem item = new RecurringInvoiceItem(invoiceId, accountId, subscriptionId, "test plan", "test phase", startDate, endDate,
                 rate, rate, Currency.USD);
         recurringInvoiceItemDao.create(item, context);
 
@@ -60,13 +61,15 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
 
     @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),
+            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);
         }
@@ -77,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");
@@ -84,7 +88,8 @@ 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),
+            RecurringInvoiceItem item = new RecurringInvoiceItem(invoiceId, accountId, subscriptionId,
+                    "test plan", "test phase", startDate, startDate.plusMonths(1),
                     amount, amount, Currency.USD);
             recurringInvoiceItemDao.create(item, context);
         }
@@ -106,7 +111,8 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         BigDecimal rate = new BigDecimal("20.00");
 
         UUID subscriptionId = UUID.randomUUID();
-        RecurringInvoiceItem item = new RecurringInvoiceItem(invoiceId, subscriptionId, "test plan", "test phase", startDate, startDate.plusMonths(1),
+        RecurringInvoiceItem item = new RecurringInvoiceItem(invoiceId, accountId, subscriptionId,
+                "test plan", "test phase", startDate, startDate.plusMonths(1),
                 rate, rate, Currency.USD);
         recurringInvoiceItemDao.create(item, context);
 
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 739d8fc..67a4833 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
@@ -26,6 +26,7 @@ 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;
@@ -200,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 49e2263..f74a72f 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
@@ -30,7 +30,9 @@ import com.ning.billing.invoice.notification.NextBillingDatePoster;
 import com.ning.billing.util.callcontext.CallContextFactory;
 import com.ning.billing.util.callcontext.DefaultCallContextFactory;
 import com.ning.billing.util.glue.CallContextModule;
+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.glue.AccountModule;
 import com.ning.billing.catalog.glue.CatalogModule;
@@ -91,6 +93,9 @@ public class InvoiceModuleWithEmbeddedDb extends InvoiceModule {
 
         bind(Clock.class).to(DefaultClock.class).asEagerSingleton();
         bind(CallContextFactory.class).to(DefaultCallContextFactory.class).asEagerSingleton();
+                install(new FieldStoreModule());
+        install(new TagStoreModule());
+
         installNotificationQueue();
         install(new AccountModule());
         install(new CatalogModule());
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 336ec10..73f3b7b 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
@@ -21,6 +21,8 @@ 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;
 
 
@@ -60,5 +62,8 @@ public class InvoiceModuleWithMocks extends InvoiceModule {
     @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 93aa35a..a3d2e5f 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/MockModule.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/MockModule.java
@@ -18,6 +18,8 @@ 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;
 
@@ -44,6 +46,8 @@ public class MockModule extends AbstractModule {
         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/TestNextBillingDateNotifier.java b/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
index ad1916a..6e1e9b2 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
@@ -27,7 +27,7 @@ import java.util.concurrent.Callable;
 import com.ning.billing.account.api.AccountUserApi;
 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;
 import com.ning.billing.entitlement.api.billing.DefaultEntitlementBillingApi;
 import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
 import com.ning.billing.invoice.InvoiceDispatcher;
@@ -133,7 +133,7 @@ public class TestNextBillingDateNotifier {
                 bind(InvoiceGenerator.class).to(DefaultInvoiceGenerator.class).asEagerSingleton();
                 bind(InvoiceDao.class).to(DefaultInvoiceDao.class);
                 bind(NextBillingDatePoster.class).to(DefaultNextBillingDatePoster.class).asEagerSingleton();
-                bind(AccountDao.class).to(DefaultAccountDao.class).asEagerSingleton();
+                bind(AccountDao.class).to(AuditedAccountDao.class).asEagerSingleton();
                 bind(AccountUserApi.class).to(DefaultAccountUserApi.class).asEagerSingleton();
                 bind(EntitlementBillingApi.class).to(DefaultEntitlementBillingApi.class).asEagerSingleton();
 			}
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 6c6f6c1..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
@@ -294,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();
@@ -344,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
@@ -698,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/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
index 7f3a5c2..e2ec321 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
@@ -71,7 +71,7 @@ public abstract class TestPaymentApi {
         final BigDecimal amount = new BigDecimal("10.00");
         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,
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 df54db7..6112fc2 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestHelper.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestHelper.java
@@ -92,6 +92,7 @@ public class TestHelper {
             if (item instanceof RecurringInvoiceItem) {
                 RecurringInvoiceItem recurringInvoiceItem = (RecurringInvoiceItem) item;
                 invoice.addInvoiceItem(new RecurringInvoiceItem(invoice.getId(),
+                                                               account.getId(),
                                                                recurringInvoiceItem.getSubscriptionId(),
                                                                recurringInvoiceItem.getPlanName(),
                                                                recurringInvoiceItem.getPhaseName(),
@@ -110,7 +111,7 @@ 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),
+        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/TestRetryService.java b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
index d8ec6e9..9c319ec 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
@@ -114,6 +114,7 @@ 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,
@@ -154,6 +155,7 @@ public class TestRetryService {
         final DateTime now = clock.getUTCNow();
 
         invoice.addInvoiceItem(new RecurringInvoiceItem(invoice.getId(),
+                                                       account.getId(),
                                                        subscriptionId,
                                                        "test plan", "test phase",
                                                        now,
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
index af3e929..b4bb413 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/CustomFieldHistory.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/CustomFieldHistory.java
@@ -16,7 +16,7 @@
 
 package com.ning.billing.util.customfield;
 
-import com.ning.billing.util.entity.ChangeType;
+import com.ning.billing.util.ChangeType;
 import org.joda.time.DateTime;
 
 import java.util.UUID;
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 8be9c43..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
@@ -21,7 +21,7 @@ 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() {
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
index 7a3e94d..b7d2de0 100644
--- 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
@@ -16,8 +16,8 @@
 
 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.entity.ChangeType;
 import com.ning.billing.util.customfield.CustomField;
 import com.ning.billing.util.customfield.CustomFieldHistory;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
@@ -27,7 +27,8 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.UUID;
 
-public class AuditedCustomFieldDao {
+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);
 
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..9e62a80
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldDao.java
@@ -0,0 +1,12 @@
+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
index 6c08532..6124ff4 100644
--- 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
@@ -1,3 +1,19 @@
+/*
+ * 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;
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/glue/FieldStoreModule.java b/util/src/main/java/com/ning/billing/util/glue/FieldStoreModule.java
index 014fe55..2d579fc 100644
--- a/util/src/main/java/com/ning/billing/util/glue/FieldStoreModule.java
+++ b/util/src/main/java/com/ning/billing/util/glue/FieldStoreModule.java
@@ -17,10 +17,12 @@
 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/TagStoreModule.java b/util/src/main/java/com/ning/billing/util/glue/TagStoreModule.java
index 6c3856b..42cdeff 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,7 +19,9 @@ package com.ning.billing.util.glue;
 import com.google.inject.AbstractModule;
 import com.ning.billing.util.api.TagDefinitionUserApi;
 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.TagSqlDao;
@@ -29,7 +31,7 @@ public class TagStoreModule extends AbstractModule
     protected void installDaos() {
         bind(TagDefinitionSqlDao.class).toProvider(TagDescriptionDaoProvider.class).asEagerSingleton();
         bind(TagDefinitionDao.class).to(DefaultTagDefinitionDao.class).asEagerSingleton();
-        bind(TagSqlDao.class).toProvider(TagStoreDaoProvider.class).asEagerSingleton();
+        bind(TagDao.class).to(AuditedTagDao.class).asEagerSingleton();
     }
 
     @Override
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
index 257a531..8cf19cb 100644
--- 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
@@ -16,16 +16,31 @@
 
 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.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.Iterator;
 import java.util.List;
 import java.util.UUID;
 
-public class AuditedTagDao {
-    public void saveTags(Transmogrifier dao, UUID objectId, String objectType, List<Tag> tags, CallContext context) {
+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 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
@@ -52,7 +67,44 @@ public class AuditedTagDao {
         tagSqlDao.batchDeleteFromTransaction(objectId.toString(), objectType, existingTags, context);
 
         TagAuditSqlDao auditDao = dao.become(TagAuditSqlDao.class);
-        auditDao.batchInsertFromTransaction(objectId.toString(), objectType, tags, context);
-        auditDao.batchDeleteFromTransaction(objectId.toString(), objectType, existingTags, context);
+        auditDao.batchInsertFromTransaction(tags, context);
+        auditDao.batchDeleteFromTransaction(existingTags, context);
+    }
+
+    @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/TagAuditSqlDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagAuditSqlDao.java
index 55cc732..02fb33d 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagAuditSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagAuditSqlDao.java
@@ -21,6 +21,7 @@ import com.ning.billing.util.callcontext.CallContextBinder;
 import com.ning.billing.util.tag.Tag;
 import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.SqlBatch;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
 import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
@@ -31,14 +32,18 @@ import java.util.List;
 @RegisterMapper(TagMapper.class)
 public interface TagAuditSqlDao extends Transactional<TagAuditSqlDao> {
     @SqlBatch(transactional=false)
-    public void batchInsertFromTransaction(@Bind("objectId") final String objectId,
-                                           @Bind("objectType") final String objectType,
-                                           @TagBinder final List<Tag> entities,
+    public void batchInsertFromTransaction(@TagBinder final List<Tag> tag,
                                            @CallContextBinder final CallContext context);
 
     @SqlBatch(transactional=false)
-    public void batchDeleteFromTransaction(@Bind("objectId") final String objectId,
-                                           @Bind("objectType") final String objectType,
-                                           @TagBinder final List<Tag> entities,
+    public void batchDeleteFromTransaction(@TagBinder final List<Tag> tag,
                                            @CallContextBinder final CallContext context);
+
+    @SqlUpdate
+    public void addTagFromTransaction(@Bind("id") final String tagId,
+                                      @CallContextBinder final CallContext context);
+
+    @SqlUpdate
+    public void removeTagFromTransaction(@Bind("id") final String tagId,
+                                         @CallContextBinder final CallContext context);
 }
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..b4a8223
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
@@ -0,0 +1,17 @@
+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 saveTags(Transmogrifier dao, UUID objectId, String objectType, List<Tag> tags, CallContext context);
+
+    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/TaggableDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TaggableDao.java
new file mode 100644
index 0000000..f2e1f23
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TaggableDao.java
@@ -0,0 +1,12 @@
+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/TagSqlDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagSqlDao.java
index cfd552f..79b68b0 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagSqlDao.java
@@ -16,13 +16,14 @@
 
 package com.ning.billing.util.tag.dao;
 
-
 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.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;
@@ -46,4 +47,22 @@ public interface TagSqlDao extends EntityCollectionDao<Tag>, Transactional<TagSq
                                            @Bind("objectType") final String objectType,
                                            @TagBinder final List<Tag> entities,
                                            @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/resources/com/ning/billing/util/tag/dao/TagAuditSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/tag/dao/TagAuditSqlDao.sql.stg
index 3daa932..c591fba 100644
--- 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
@@ -18,4 +18,14 @@ batchInsertFromTransaction() ::= <<
 batchDeleteFromTransaction() ::= <<
     INSERT INTO audit_log(<fields()>)
     VALUES('tag', :id, 'DELETE', :updatedDate, :userName, NULL, NULL);
+>>
+
+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/TagSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/tag/dao/TagSqlDao.sql.stg
index 442d374..e950d75 100644
--- a/util/src/main/resources/com/ning/billing/util/tag/dao/TagSqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/tag/dao/TagSqlDao.sql.stg
@@ -1,7 +1,16 @@
 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(id, tag_definition_name, object_id, object_type, created_by, created_date)
+  INSERT INTO tags(<fields()>)
   VALUES (:id, :tagDefinitionName, :objectId, :objectType, :userName, :createdDate);
 >>
 
@@ -11,6 +20,23 @@ batchDeleteFromTransaction() ::= <<
         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,
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 429b5ab..568c974 100644
--- a/util/src/test/java/com/ning/billing/util/customfield/TestFieldStore.java
+++ b/util/src/test/java/com/ning/billing/util/customfield/TestFieldStore.java
@@ -29,6 +29,7 @@ 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;
@@ -49,6 +50,7 @@ public class TestFieldStore {
     private final MysqlTestingHelper helper = new MysqlTestingHelper();
     private CallContext context;
     private IDBI dbi;
+    private CustomFieldDao customFieldDao;
 
     @BeforeClass(alwaysRun = true)
     protected void setup() throws IOException {
@@ -60,6 +62,7 @@ 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());
@@ -91,8 +94,7 @@ public class TestFieldStore {
         fieldStore1.setValue(fieldName, fieldValue);
 
         CustomFieldSqlDao customFieldSqlDao = dbi.onDemand(CustomFieldSqlDao.class);
-        AuditedCustomFieldDao auditedDao = new AuditedCustomFieldDao();
-        auditedDao.saveFields(customFieldSqlDao, id, objectType, fieldStore1.getEntityList(), context);
+        customFieldDao.saveFields(customFieldSqlDao, id, objectType, fieldStore1.getEntityList(), context);
 
         final FieldStore fieldStore2 = DefaultFieldStore.create(id, objectType);
         fieldStore2.add(customFieldSqlDao.load(id.toString(), objectType));
@@ -102,7 +104,7 @@ public class TestFieldStore {
         fieldValue = "Cape Canaveral";
         fieldStore2.setValue(fieldName, fieldValue);
         assertEquals(fieldStore2.getValue(fieldName), fieldValue);
-        auditedDao.saveFields(customFieldSqlDao, id, objectType, fieldStore2.getEntityList(), context);
+        customFieldDao.saveFields(customFieldSqlDao, id, objectType, fieldStore2.getEntityList(), context);
 
         final FieldStore fieldStore3 = DefaultFieldStore.create(id, objectType);
         assertEquals(fieldStore3.getValue(fieldName), null);
diff --git a/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java b/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java
index 8af9761..8138bbe 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
@@ -28,6 +28,7 @@ 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.AuditedTagDao;
+import com.ning.billing.util.tag.dao.TagDao;
 import org.apache.commons.io.IOUtils;
 import org.joda.time.DateTime;
 import org.joda.time.Seconds;
@@ -66,7 +67,6 @@ public class TestTagStore {
     @Inject
     private IDBI dbi;
 
-    @Inject
     private TagSqlDao tagSqlDao;
 
     @Inject
@@ -93,6 +93,7 @@ public class TestTagStore {
             tagSqlDao.test();
 
             context = new DefaultCallContextFactory(clock).createCallContext("Tag store test", CallOrigin.TEST, UserType.TEST);
+            tagSqlDao = dbi.onDemand(TagSqlDao.class);
 
             cleanupTags();
             tag1 = tagDefinitionDao.create("tag1", "First tag", context);
@@ -112,8 +113,8 @@ public class TestTagStore {
 
     private void saveTags(final TagSqlDao dao, final String objectType, final UUID accountId,
                           final List<Tag> tagList, final CallContext context)  {
-        AuditedTagDao auditedTagDao = new AuditedTagDao();
-        auditedTagDao.saveTags(dao, accountId, objectType, tagList, context);
+        TagDao tagDao = new AuditedTagDao(dbi);
+        tagDao.saveTags(dao, accountId, objectType, tagList, context);
     }