killbill-aplcache

start of invoice credits; fix for TagMapper bug (manifests

5/29/2012 4:47:18 PM

Changes

Details

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 cd7decd..9f898c7 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
@@ -46,5 +46,7 @@ public interface InvoiceItem extends Entity, Comparable<InvoiceItem> {
 
     Currency getCurrency();
 
+    InvoiceItemType getInvoiceItemType();
+
     InvoiceItem asReversingItem();
 }
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceItemType.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceItemType.java
index ce066a4..aaae9df 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoiceItemType.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceItemType.java
@@ -23,6 +23,5 @@ public enum InvoiceItemType {
     MIGRATION,
     REFUND,
     CHARGE_BACK,
-    ADD_CREDIT,
-    USE_CREDIT
+    CREDIT
 }
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 66016df..e0f6ed0 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
@@ -41,5 +41,5 @@ public interface InvoiceUserApi {
 
     public void tagInvoiceAsWrittenOff(UUID invoiceId, CallContext context);
 
-    public void tagInvoiceAsNotWrittenOff(UUID invoiceId, CallContext context);
+    public void tagInvoiceAsNotWrittenOff(UUID invoiceId, CallContext context) throws InvoiceApiException;
 }
diff --git a/api/src/main/java/com/ning/billing/util/api/TagUserApi.java b/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
index 16b8a30..fed7517 100644
--- a/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
+++ b/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
@@ -20,6 +20,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.util.callcontext.CallContext;
 
 import com.ning.billing.util.dao.ObjectType;
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 c122766..65d32c7 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
@@ -48,7 +48,9 @@ public enum ControlTagType {
 
     public TagDefinition toTagDefinition() {
         return new TagDefinition() {
-            @Override public String getName() {return this.toString();}
+            @Override public String getName() {
+                return name();
+            }
 
             @Override public String getDescription() {return description;}
 
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 1e45646..ebb1216 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
@@ -85,7 +85,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
     }
 
     @Override
-    public void tagInvoiceAsNotWrittenOff(final UUID invoiceId, final CallContext context) {
+    public void tagInvoiceAsNotWrittenOff(final UUID invoiceId, final CallContext context) throws InvoiceApiException {
         dao.removeWrittenOff(invoiceId, context);
     }
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/CreditInvoiceItemSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/CreditInvoiceItemSqlDao.java
new file mode 100644
index 0000000..d60e813
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/CreditInvoiceItemSqlDao.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.invoice.dao;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.model.CreditInvoiceItem;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BinderFactory;
+import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+import org.skife.jdbi.v2.sqlobject.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.stringtemplate.ExternalizedSqlViaStringTemplate3;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.math.BigDecimal;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.UUID;
+
+@ExternalizedSqlViaStringTemplate3
+@RegisterMapper(CreditInvoiceItemSqlDao.CreditInvoiceItemMapper.class)
+public interface CreditInvoiceItemSqlDao extends EntitySqlDao<InvoiceItem> {
+    @SqlQuery
+    List<Long> getRecordIds(@Bind("invoiceId") final String invoiceId);
+
+    @SqlQuery
+    List<InvoiceItem> getInvoiceItemsByInvoice(@Bind("invoiceId") final String invoiceId);
+
+    @SqlQuery
+    List<InvoiceItem> getInvoiceItemsByAccount(@Bind("accountId") final String accountId);
+
+    @Override
+    @SqlUpdate
+    void create(@CreditInvoiceItemBinder final InvoiceItem invoiceItem, @CallContextBinder final CallContext context);
+
+    @SqlBatch
+    void create(@CreditInvoiceItemBinder final List<InvoiceItem> items, @CallContextBinder final CallContext context);
+
+    @SqlBatch(transactional=false)
+    void batchCreateFromTransaction(@CreditInvoiceItemBinder final List<InvoiceItem> items, @CallContextBinder final CallContext context);
+
+    @BindingAnnotation(CreditInvoiceItemBinder.CreditInvoiceItemBinderFactory.class)
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.PARAMETER})
+    public @interface CreditInvoiceItemBinder {
+        public static class CreditInvoiceItemBinderFactory implements BinderFactory {
+            public Binder build(Annotation annotation) {
+                return new Binder<CreditInvoiceItemBinder, CreditInvoiceItem>() {
+                    public void bind(SQLStatement q, CreditInvoiceItemBinder bind, CreditInvoiceItem item) {
+                        q.bind("id", item.getId().toString());
+                        q.bind("invoiceId", item.getInvoiceId().toString());
+                        q.bind("accountId", item.getAccountId().toString());
+                        q.bind("creditDate", item.getStartDate().toDate());
+                        q.bind("amount", item.getAmount());
+                        q.bind("currency", item.getCurrency().toString());
+                    }
+                };
+            }
+        }
+    }
+    public class CreditInvoiceItemMapper implements ResultSetMapper<InvoiceItem> {
+        @Override
+        public InvoiceItem map(int index, ResultSet result, StatementContext ctx) 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"));
+            DateTime creditDate = new DateTime(result.getTimestamp("credit_date"));
+            BigDecimal amount = result.getBigDecimal("amount");
+            Currency currency = Currency.valueOf(result.getString("currency"));
+            return new CreditInvoiceItem(id, invoiceId, accountId, creditDate, amount, currency);
+        }
+    }
+}
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 4b262db..3a4ffe2 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
@@ -21,6 +21,8 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.model.CreditInvoiceItem;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
@@ -117,16 +119,16 @@ public class DefaultInvoiceDao implements InvoiceDao {
     @Override
     public Invoice getById(final UUID invoiceId) {
         return invoiceSqlDao.inTransaction(new Transaction<Invoice, InvoiceSqlDao>() {
-             @Override
-             public Invoice inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
-                 Invoice invoice = invoiceDao.getById(invoiceId.toString());
+            @Override
+            public Invoice inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
+                Invoice invoice = invoiceDao.getById(invoiceId.toString());
 
-                 if (invoice != null) {
-                     populateChildren(invoice, invoiceDao);
-                 }
+                if (invoice != null) {
+                    populateChildren(invoice, invoiceDao);
+                }
 
-                 return invoice;
-             }
+                return invoice;
+            }
         });
     }
 
@@ -163,6 +165,12 @@ public class DefaultInvoiceDao implements InvoiceDao {
                     recordIdList = fixedPriceInvoiceItemDao.getRecordIds(invoice.getId().toString());
                     audits.addAll(createAudits(TableName.FIXED_INVOICE_ITEMS, recordIdList));
 
+                    List<InvoiceItem> creditInvoiceItems = invoice.getInvoiceItems(CreditInvoiceItem.class);
+                    CreditInvoiceItemSqlDao creditInvoiceItemSqlDao = transactional.become(CreditInvoiceItemSqlDao.class);
+                    creditInvoiceItemSqlDao.batchCreateFromTransaction(creditInvoiceItems, context);
+                    recordIdList = creditInvoiceItemSqlDao.getRecordIds(invoice.getId().toString());
+                    audits.addAll(createAudits(TableName.CREDIT_INVOICE_ITEMS, recordIdList));
+
                     List<InvoicePayment> invoicePayments = invoice.getPayments();
                     InvoicePaymentSqlDao invoicePaymentSqlDao = transactional.become(InvoicePaymentSqlDao.class);
                     invoicePaymentSqlDao.batchCreateFromTransaction(invoicePayments, context);
@@ -252,7 +260,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public void removeWrittenOff(UUID invoiceId, CallContext context) {
+    public void removeWrittenOff(UUID invoiceId, CallContext context) throws InvoiceApiException {
         tagDao.deleteTag(invoiceId, ObjectType.INVOICE, ControlTagType.WRITTEN_OFF.toTagDefinition(), context);
     }
 
@@ -278,13 +286,19 @@ public class DefaultInvoiceDao implements InvoiceDao {
     }
 
     private void getInvoiceItemsWithinTransaction(final Invoice invoice, final InvoiceSqlDao invoiceDao) {
+        String invoiceId = invoice.getId().toString();
+
         RecurringInvoiceItemSqlDao recurringInvoiceItemDao = invoiceDao.become(RecurringInvoiceItemSqlDao.class);
-        List<InvoiceItem> recurringInvoiceItems = recurringInvoiceItemDao.getInvoiceItemsByInvoice(invoice.getId().toString());
+        List<InvoiceItem> recurringInvoiceItems = recurringInvoiceItemDao.getInvoiceItemsByInvoice(invoiceId);
         invoice.addInvoiceItems(recurringInvoiceItems);
 
         FixedPriceInvoiceItemSqlDao fixedPriceInvoiceItemDao = invoiceDao.become(FixedPriceInvoiceItemSqlDao.class);
-        List<InvoiceItem> fixedPriceInvoiceItems = fixedPriceInvoiceItemDao.getInvoiceItemsByInvoice(invoice.getId().toString());
+        List<InvoiceItem> fixedPriceInvoiceItems = fixedPriceInvoiceItemDao.getInvoiceItemsByInvoice(invoiceId);
         invoice.addInvoiceItems(fixedPriceInvoiceItems);
+
+        CreditInvoiceItemSqlDao creditInvoiceItemSqlDao = invoiceDao.become(CreditInvoiceItemSqlDao.class);
+        List<InvoiceItem> creditInvoiceItems = creditInvoiceItemSqlDao.getInvoiceItemsByInvoice(invoiceId);
+        invoice.addInvoiceItems(creditInvoiceItems);
     }
 
     private void getInvoicePaymentsWithinTransaction(final List<Invoice> invoices, final InvoiceSqlDao invoiceDao) {
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 17006d3..59de677 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
@@ -17,6 +17,7 @@
 package com.ning.billing.invoice.dao;
 
 import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.util.callcontext.CallContext;
 import org.joda.time.DateTime;
@@ -54,6 +55,6 @@ public interface InvoiceDao {
 
     void setWrittenOff(UUID invoiceId, CallContext context);
 
-    void removeWrittenOff(UUID invoiceId, CallContext context);
+    void removeWrittenOff(UUID invoiceId, CallContext context) throws InvoiceApiException;
 
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/CreditInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/CreditInvoiceItem.java
index ac800a7..651a717 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/CreditInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/CreditInvoiceItem.java
@@ -18,6 +18,7 @@ package com.ning.billing.invoice.model;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
 import org.joda.time.DateTime;
 
 import java.math.BigDecimal;
@@ -29,7 +30,7 @@ public class CreditInvoiceItem extends InvoiceItemBase {
     }
 
     public CreditInvoiceItem(UUID id, UUID invoiceId, UUID accountId, DateTime date, BigDecimal amount, Currency currency) {
-        super(id, invoiceId, accountId, null, null, null, null, date, date, amount, currency);
+        super(id, invoiceId, accountId, null, null, null, null, date, date, amount, currency, InvoiceItemType.CREDIT);
     }
 
     @Override
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 c24215c..04d6caf 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
@@ -98,6 +98,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
         }
 
         //addCreditItems(accountId, proposedItems, existingInvoices, targetCurrency);
+        useExistingCredits(existingItems, proposedItems);
 
         if (proposedItems == null || proposedItems.size() == 0) {
             return null;
@@ -108,7 +109,20 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
         }
     }
 
-   /*
+    private void useExistingCredits(List<InvoiceItem> existingItems, List<InvoiceItem> proposedItems) {
+        BigDecimal totalUnusedCreditAmount = BigDecimal.ZERO;
+
+        if (existingItems.size() > 0) {
+            for (InvoiceItem item : existingItems) {
+                if (item instanceof CreditInvoiceItem) {
+                    CreditInvoiceItem creditInvoiceItem = (CreditInvoiceItem) item;
+
+                }
+            }
+        }
+    }
+
+    /*
     * ensures that the balance of all invoices are zero or positive, adding an adjusting credit item if needed
     */
     private void addCreditItems(UUID accountId, List<InvoiceItem> invoiceItems, List<Invoice> invoices, Currency currency) {
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 b5a79ab..60a39a6 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
@@ -19,6 +19,7 @@ package com.ning.billing.invoice.model;
 import java.math.BigDecimal;
 import java.util.UUID;
 
+import com.ning.billing.invoice.api.InvoiceItemType;
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.Currency;
@@ -28,12 +29,12 @@ public class FixedPriceInvoiceItem extends InvoiceItemBase {
 
     public FixedPriceInvoiceItem(UUID invoiceId, UUID accountId, UUID bundleId, UUID subscriptionId, String planName, String phaseName,
                                  DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
-        super(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+        super(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, InvoiceItemType.FIXED);
     }
 
     public FixedPriceInvoiceItem(UUID id, UUID invoiceId, UUID accountId, UUID bundleId, UUID subscriptionId, String planName, String phaseName,
                                  DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
-        super(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+        super(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, InvoiceItemType.FIXED);
     }
 
     @Override
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java
index 5ccaa8c..016184d 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java
@@ -18,6 +18,7 @@ package com.ning.billing.invoice.model;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.util.entity.EntityBase;
 import org.joda.time.DateTime;
 
@@ -36,16 +37,18 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem 
     protected final DateTime endDate;
     protected final BigDecimal amount;
     protected final Currency currency;
+    protected InvoiceItemType invoiceItemType;
 
     public InvoiceItemBase(UUID invoiceId, UUID accountId, UUID bundleId, UUID subscriptionId, String planName, String phaseName,
-            DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
+            DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency, InvoiceItemType invoiceItemType) {
         this(UUID.randomUUID(), invoiceId, accountId, bundleId, subscriptionId, planName, phaseName,
-                startDate, endDate, amount, currency);
+                startDate, endDate, amount, currency, invoiceItemType);
     }
 
     public InvoiceItemBase(UUID id, UUID invoiceId, UUID accountId, @Nullable UUID bundleId,
                            @Nullable UUID subscriptionId, String planName, String phaseName,
-                           DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
+                           DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency,
+                           InvoiceItemType invoiceItemType) {
         super(id);
         this.invoiceId = invoiceId;
         this.accountId = accountId;
@@ -57,6 +60,7 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem 
         this.endDate = endDate;
         this.amount = amount;
         this.currency = currency;
+        this.invoiceItemType = invoiceItemType;
     }
 
     @Override
@@ -109,6 +113,11 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem 
     }
 
     @Override
+    public InvoiceItemType getInvoiceItemType() {
+        return invoiceItemType;
+    }
+
+    @Override
     public abstract InvoiceItem asReversingItem();
 
     @Override
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 bc813fc..9dbbae0 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
@@ -18,6 +18,7 @@ package com.ning.billing.invoice.model;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
 import org.joda.time.DateTime;
 
 import java.math.BigDecimal;
@@ -30,8 +31,8 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
     public RecurringInvoiceItem(UUID invoiceId, UUID accountId, UUID bundleId, UUID subscriptionId, String planName, String phaseName,
                                 DateTime startDate, DateTime endDate,
                                 BigDecimal amount, BigDecimal rate,
-                                Currency currency) { 
-        super(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+                                Currency currency) {
+        super(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, InvoiceItemType.RECURRING);
         this.rate = rate;
         this.reversedItemId = null;
     }
@@ -41,7 +42,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
                                 BigDecimal amount, BigDecimal rate,
                                 Currency currency, UUID reversedItemId) {
         super(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate,
-                amount, currency);
+                amount, currency, InvoiceItemType.REVERSAL);
         this.rate = rate;
         this.reversedItemId = reversedItemId;
     }
@@ -51,7 +52,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
                                 DateTime startDate, DateTime endDate,
                                 BigDecimal amount, BigDecimal rate,
                                 Currency currency) {
-        super(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+        super(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, InvoiceItemType.RECURRING);
         this.rate = rate;
         this.reversedItemId = null;
     }
@@ -61,7 +62,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
                                 DateTime startDate, DateTime endDate,
                                 BigDecimal amount, BigDecimal rate,
                                 Currency currency, UUID reversedItemId) {
-        super(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+        super(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, InvoiceItemType.REVERSAL);
         this.rate = rate;
         this.reversedItemId = reversedItemId;
     }
@@ -132,6 +133,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
 
         RecurringInvoiceItem that = (RecurringInvoiceItem) o;
 
+        // do not include invoice item type, since a reversing item can be equal to the original item
         if (accountId.compareTo(that.accountId) != 0) return false;
         if (amount.compareTo(that.amount) != 0) return false;
         if (currency != that.currency) return false;
@@ -162,6 +164,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
         result = 31 * result + amount.hashCode();
         result = 31 * result + rate.hashCode();
         result = 31 * result + currency.hashCode();
+        result = 31 * result + invoiceItemType.hashCode();
         result = 31 * result + (reversedItemId != null ? reversedItemId.hashCode() : 0);
         return result;
     }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
index f1a2e8d..9f49eb5 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
@@ -18,6 +18,7 @@ package com.ning.billing.invoice.template.formatters;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.invoice.api.formatters.InvoiceItemFormatter;
 import com.ning.billing.util.template.translation.DefaultCatalogTranslator;
 import com.ning.billing.util.template.translation.Translator;
@@ -55,6 +56,11 @@ public class DefaultInvoiceItemFormatter implements InvoiceItemFormatter {
     }
 
     @Override
+    public InvoiceItemType getInvoiceItemType() {
+        return item.getInvoiceItemType();
+    }
+
+    @Override
     public InvoiceItem asReversingItem() {
         return item.asReversingItem();
     }
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/CreditInvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/CreditInvoiceItemSqlDao.sql.stg
new file mode 100644
index 0000000..d3bef75
--- /dev/null
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/CreditInvoiceItemSqlDao.sql.stg
@@ -0,0 +1,69 @@
+group CreditInvoiceItemSqlDao;
+
+fields(prefix) ::= <<
+  <prefix>id,
+  <prefix>invoice_id,
+  <prefix>account_id,
+  <prefix>credit_date,
+  <prefix>amount,
+  <prefix>currency,
+  <prefix>created_by,
+  <prefix>created_date
+>>
+
+getById() ::= <<
+  SELECT <fields()>
+  FROM credit_invoice_items
+  WHERE id = :id;
+>>
+
+getInvoiceItemsByInvoice() ::= <<
+  SELECT <fields()>
+  FROM credit_invoice_items
+  WHERE invoice_id = :invoiceId;
+>>
+
+getInvoiceItemsByAccount() ::= <<
+  SELECT <fields("cii.")>
+  FROM credit_invoice_items cii
+  INNER JOIN invoices i ON i.id = cii.invoice_id
+  WHERE i.account_id = :accountId;
+>>
+
+create() ::= <<
+  INSERT INTO credit_invoice_items(<fields()>)
+  VALUES(:id, :invoiceId, :accountId, :creditDate, :amount, :currency, :userName, :createdDate);
+>>
+
+batchCreateFromTransaction() ::= <<
+  INSERT INTO credit_invoice_items(<fields()>)
+  VALUES(:id, :invoiceId, :accountId, :creditDate, :amount, :currency, :userName, :createdDate);
+>>
+
+getRecordIds() ::= <<
+    SELECT record_id, id
+    FROM credit_invoice_items
+    WHERE invoice_id = :invoiceId;
+>>
+
+auditFields(prefix) ::= <<
+    <prefix>table_name,
+    <prefix>record_id,
+    <prefix>change_type,
+    <prefix>change_date,
+    <prefix>changed_by,
+    <prefix>reason_code,
+    <prefix>comments,
+    <prefix>user_token
+>>
+
+insertAuditFromTransaction() ::= <<
+    INSERT INTO audit_log(<auditFields()>)
+    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken);
+>>
+
+test() ::= <<
+  SELECT 1
+  FROM credit_invoice_items;
+>>
+;
\ No newline at end of file
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
index 6db5db5..fe942a8 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
@@ -39,11 +39,11 @@ getInvoicesByAccountAfterDate() ::= <<
 >>
 
 getInvoicesBySubscription() ::= <<
-  SELECT record_id as invoice_number, <invoiceFields("i.")>
+  SELECT i.record_id as invoice_number, <invoiceFields("i.")>
   FROM invoices i
   LEFT JOIN recurring_invoice_items rii ON i.id = rii.invoice_id
-  WHERE rii.subscription_id = :subscriptionId  AND migrated = 'FALSE'
-  GROUP BY record_id as invoice_number, <invoiceFields("i.")>;
+  WHERE rii.subscription_id = :subscriptionId AND migrated = 'FALSE'
+  GROUP BY i.record_id, <invoiceFields("i.")>;
 >>
 
 getById() ::= <<
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 8bda35c..061d8a0 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
@@ -45,6 +45,22 @@ CREATE UNIQUE INDEX fixed_invoice_items_id ON fixed_invoice_items(id);
 CREATE INDEX fixed_invoice_items_subscription_id ON fixed_invoice_items(subscription_id ASC);
 CREATE INDEX fixed_invoice_items_invoice_id ON fixed_invoice_items(invoice_id ASC);
 
+DROP TABLE IF EXISTS credit_invoice_items;
+CREATE TABLE credit_invoice_items (
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    id char(36) NOT NULL,
+    invoice_id char(36) NOT NULL,
+    account_id char(36) NOT NULL,
+    credit_date datetime NOT NULL,
+    amount numeric(10,4) NULL,
+    currency char(3) NOT NULL,
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
+    PRIMARY KEY(record_id)
+) ENGINE=innodb;
+CREATE UNIQUE INDEX credit_invoice_items_id ON credit_invoice_items(id);
+CREATE INDEX credit_invoice_items_invoice_id ON credit_invoice_items(invoice_id ASC);
+
 DROP TABLE IF EXISTS invoice_locking;
 
 DROP TABLE IF EXISTS invoices;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
index 0e99dab..afba58b 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
@@ -20,6 +20,14 @@ import static org.testng.Assert.assertTrue;
 
 import java.io.IOException;
 
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.invoice.notification.MockNextBillingDatePoster;
+import com.ning.billing.invoice.notification.NextBillingDatePoster;
+import com.ning.billing.util.callcontext.TestCallContext;
+import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.tag.dao.AuditedTagDao;
+import com.ning.billing.util.tag.dao.MockTagDao;
+import com.ning.billing.util.tag.dao.TagDao;
 import org.apache.commons.io.IOUtils;
 import org.skife.jdbi.v2.Handle;
 import org.skife.jdbi.v2.IDBI;
@@ -29,28 +37,21 @@ import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.google.inject.Stage;
 import com.ning.billing.config.InvoiceConfig;
-import com.ning.billing.invoice.glue.InvoiceModuleWithEmbeddedDb;
 import com.ning.billing.invoice.model.DefaultInvoiceGenerator;
 import com.ning.billing.invoice.model.InvoiceGenerator;
 import com.ning.billing.invoice.tests.InvoicingTestBase;
-import com.ning.billing.util.bus.BusService;
-import com.ning.billing.util.bus.DefaultBusService;
 import com.ning.billing.util.callcontext.CallContext;
-import com.ning.billing.util.callcontext.CallOrigin;
-import com.ning.billing.util.callcontext.DefaultCallContextFactory;
-import com.ning.billing.util.callcontext.UserType;
 import com.ning.billing.util.clock.Clock;
 
 public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
     protected IDBI dbi;
+    private MysqlTestingHelper mysqlTestingHelper;
     protected InvoiceDao invoiceDao;
     protected RecurringInvoiceItemSqlDao recurringInvoiceItemDao;
+    protected FixedPriceInvoiceItemSqlDao fixedPriceInvoiceItemSqlDao;
+    protected CreditInvoiceItemSqlDao creditInvoiceItemSqlDao;
     protected InvoicePaymentSqlDao invoicePaymentDao;
-    protected InvoiceModuleWithEmbeddedDb module;
     protected Clock clock;
     protected CallContext context;
     protected InvoiceGenerator generator;
@@ -66,44 +67,44 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
 
     @BeforeClass(alwaysRun = true)
     protected void setup() throws IOException {
-        module = new InvoiceModuleWithEmbeddedDb();
-        dbi = module.getDbi();
+        mysqlTestingHelper = new MysqlTestingHelper();
+        dbi = mysqlTestingHelper.getDBI();
 
         final String invoiceDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
         final String utilDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
 
-        module.startDb();
-        module.initDb(invoiceDdl);
-        module.initDb(utilDdl);
+        mysqlTestingHelper.startMysql();
+        mysqlTestingHelper.initDb(invoiceDdl);
+        mysqlTestingHelper.initDb(utilDdl);
 
-        final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module);
-
-        invoiceDao = injector.getInstance(InvoiceDao.class);
+        NextBillingDatePoster nextBillingDatePoster = new MockNextBillingDatePoster();
+        TagDao tagDao = new AuditedTagDao(dbi);
+        invoiceDao = new DefaultInvoiceDao(dbi, nextBillingDatePoster, tagDao);
         invoiceDao.test();
 
-        recurringInvoiceItemDao = module.getInvoiceItemSqlDao();
+        recurringInvoiceItemDao = dbi.onDemand(RecurringInvoiceItemSqlDao.class);
+        fixedPriceInvoiceItemSqlDao = dbi.onDemand(FixedPriceInvoiceItemSqlDao.class);
+        creditInvoiceItemSqlDao = dbi.onDemand(CreditInvoiceItemSqlDao.class);
+        invoicePaymentDao = dbi.onDemand(InvoicePaymentSqlDao.class);
 
-        invoicePaymentDao = module.getInvoicePaymentSqlDao();
-        clock = injector.getInstance(Clock.class);
-        context = new DefaultCallContextFactory(clock).createCallContext("Count Rogan", CallOrigin.TEST, UserType.TEST);
+        clock = new ClockMock();
+        context = new TestCallContext("Invoice Dao Tests");
         generator = new DefaultInvoiceGenerator(clock, invoiceConfig);
 
-        BusService busService = injector.getInstance(BusService.class);
-        ((DefaultBusService) busService).startBus();
-
         assertTrue(true);
-       
     }
 
     @BeforeMethod(alwaysRun = true)
     public void cleanupData() {
-        module.getDbi().inTransaction(new TransactionCallback<Void>() {
+        dbi.inTransaction(new TransactionCallback<Void>() {
             @Override
             public Void inTransaction(Handle h, TransactionStatus status)
                     throws Exception {
                 h.execute("truncate table invoices");
                 h.execute("truncate table fixed_invoice_items");
                 h.execute("truncate table recurring_invoice_items");
+                h.execute("truncate table credit_invoice_items");
+                h.execute("truncate table invoice_payments");
 
                 return null;
             }
@@ -112,7 +113,7 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
 
     @AfterClass(alwaysRun = true)
     protected void tearDown() {
-        module.stopDb();
+        mysqlTestingHelper.stopMysql();
         assertTrue(true);
     }
 }
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 9acde8e..c97e775 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
@@ -18,11 +18,15 @@ package com.ning.billing.invoice.dao;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
 
 import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.invoice.model.CreditInvoiceItem;
+import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
 import org.joda.time.DateTime;
 import org.testng.annotations.Test;
 
@@ -31,9 +35,9 @@ import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
 
-@Test(groups = {"invoicing", "invoicing-invoiceDao"})
+@Test(groups = {"slow", "invoicing", "invoicing-invoiceDao"})
 public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
-    @Test(groups = "slow")
+    @Test
     public void testInvoiceItemCreation() {
         UUID accountId = UUID.randomUUID();
         UUID invoiceId = UUID.randomUUID();
@@ -52,8 +56,8 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         assertEquals(thisItem.getId(), item.getId());
         assertEquals(thisItem.getInvoiceId(), item.getInvoiceId());
         assertEquals(thisItem.getSubscriptionId(), item.getSubscriptionId());
-        assertEquals(thisItem.getStartDate(), item.getStartDate());
-        assertEquals(thisItem.getEndDate(), item.getEndDate());
+        assertTrue(thisItem.getStartDate().compareTo(item.getStartDate()) == 0);
+        assertTrue(thisItem.getEndDate().compareTo(item.getEndDate()) == 0);
         assertEquals(thisItem.getAmount().compareTo(item.getRate()), 0);
         assertEquals(thisItem.getRate().compareTo(item.getRate()), 0);
         assertEquals(thisItem.getCurrency(), item.getCurrency());
@@ -61,7 +65,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         // assertEquals(thisItem.getCreatedDate().compareTo(item.getCreatedDate()), 0);
     }
 
-    @Test(groups = "slow")
+    @Test
     public void testGetInvoiceItemsBySubscriptionId() {
         UUID accountId = UUID.randomUUID();
         UUID subscriptionId = UUID.randomUUID();
@@ -82,7 +86,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         assertEquals(items.size(), 3);
     }
 
-    @Test(groups = "slow")
+    @Test
     public void testGetInvoiceItemsByInvoiceId() {
         UUID accountId = UUID.randomUUID();
         UUID invoiceId = UUID.randomUUID();
@@ -104,7 +108,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         assertEquals(items.size(), 5);
     }
 
-    @Test(groups = "slow")
+    @Test
     public void testGetInvoiceItemsByAccountId() {
         UUID accountId = UUID.randomUUID();
         UUID bundleId = UUID.randomUUID();
@@ -127,4 +131,31 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         List<InvoiceItem> items = recurringInvoiceItemDao.getInvoiceItemsByAccount(accountId.toString());
         assertEquals(items.size(), 1);
     }
+
+    @Test
+    public void testCreditInvoiceSqlDao() {
+        UUID invoiceId = UUID.randomUUID();
+        UUID accountId = UUID.randomUUID();
+        DateTime creditDate = new DateTime(2012, 4, 1, 0, 10, 22, 0);
+
+        InvoiceItem creditInvoiceItem = new CreditInvoiceItem(invoiceId, accountId, creditDate, TEN, Currency.USD);
+        creditInvoiceItemSqlDao.create(creditInvoiceItem, context);
+
+        InvoiceItem savedItem = creditInvoiceItemSqlDao.getById(creditInvoiceItem.getId().toString());
+        assertEquals(savedItem, creditInvoiceItem);
+    }
+
+    @Test
+    public void testFixedPriceInvoiceSqlDao() {
+        UUID invoiceId = UUID.randomUUID();
+        UUID accountId = UUID.randomUUID();
+        DateTime startDate = new DateTime(2012, 4, 1, 0, 10, 22, 0);
+
+        InvoiceItem fixedPriceInvoiceItem = new FixedPriceInvoiceItem(invoiceId, accountId, UUID.randomUUID(),
+                UUID.randomUUID(), "test plan", "test phase", startDate, startDate.plusMonths(1), TEN, Currency.USD);
+        fixedPriceInvoiceItemSqlDao.create(fixedPriceInvoiceItem, context);
+
+        InvoiceItem savedItem = fixedPriceInvoiceItemSqlDao.getById(fixedPriceInvoiceItem.getId().toString());
+        assertEquals(savedItem, fixedPriceInvoiceItem);
+    }
 }
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 75b57ec..dfbd528 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
@@ -28,6 +28,9 @@ import java.util.UUID;
 
 import javax.annotation.Nullable;
 
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.model.CreditInvoiceItem;
+import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.invoice.model.DefaultInvoicePayment;
 import org.joda.time.DateTime;
 import org.testng.annotations.Test;
@@ -774,7 +777,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         assertEquals(invoice3.getTotalAmount().compareTo(FIFTEEN.negate()), 0);
     }
 
-    @Test
+    @Test(enabled=false)
     public void testRepairForPaidInvoice() throws CatalogApiException, InvoiceApiException {
         // create an invoice
         DateTime april25 = new DateTime(2012, 4, 25, 0, 0, 0, 0);
@@ -830,17 +833,15 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
     test scenario: create invoice, pay invoice, change entitlement, generate invoices to use up credits
 
     test scenario: create invoice, pay invoice, add account-level credit, generate invoice
-
-
      */
 
-    @Test
+    @Test(enabled=false)
     public void testAutoInvoiceOff() {
         BillingEventSet eventSet = new BillingEventSet();
         fail();
     }
 
-    @Test(enabled = false)
+    @Test(enabled=true)
     public void testAccountCredit() throws CatalogApiException, InvoiceApiException {
         BillingEventSet billingEventSet = new BillingEventSet();
 
@@ -853,9 +854,23 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         BillingEvent creation = createBillingEvent(subscriptionId, startDate, plan, planPhase, 1);
         billingEventSet.add(creation);
 
-        Invoice invoice = generator.generateInvoice(accountId, billingEventSet, null, startDate, Currency.USD);
-        assertNotNull(invoice);
-        assertEquals(invoice.getNumberOfItems(), 1);
-        assertEquals(invoice.getTotalAmount().compareTo(TEN), 0);
+        List<Invoice> invoices = new ArrayList<Invoice>();
+
+        Invoice initialInvoice = generator.generateInvoice(accountId, billingEventSet, null, startDate, Currency.USD);
+        assertNotNull(initialInvoice);
+        assertEquals(initialInvoice.getNumberOfItems(), 1);
+        assertEquals(initialInvoice.getTotalAmount().compareTo(TEN), 0);
+        invoices.add(initialInvoice);
+
+        // add account-level credit
+        DateTime creditDate = startDate.plusDays(5);
+        Invoice invoiceWithCredit = new DefaultInvoice(accountId, creditDate, creditDate, Currency.USD);
+        InvoiceItem accountCredit = new CreditInvoiceItem(invoiceWithCredit.getId(), accountId, creditDate, FIVE, Currency.USD);
+        invoiceWithCredit.addInvoiceItem(accountCredit);
+        invoices.add(invoiceWithCredit);
+
+        // invoice one month after the initial subscription
+        Invoice finalInvoice = generator.generateInvoice(accountId, billingEventSet, invoices, startDate.plusMonths(1), Currency.USD);
+        assertEquals(finalInvoice.getTotalAmount().compareTo(FIVE), 0);
     }
 }
\ No newline at end of file
diff --git a/payment/src/test/java/com/ning/billing/payment/MockRecurringInvoiceItem.java b/payment/src/test/java/com/ning/billing/payment/MockRecurringInvoiceItem.java
index 93c90fa..e55c7e4 100644
--- a/payment/src/test/java/com/ning/billing/payment/MockRecurringInvoiceItem.java
+++ b/payment/src/test/java/com/ning/billing/payment/MockRecurringInvoiceItem.java
@@ -18,6 +18,7 @@ package com.ning.billing.payment;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.util.entity.EntityBase;
 
 import org.apache.commons.lang.NotImplementedException;
@@ -151,6 +152,12 @@ public class MockRecurringInvoiceItem extends EntityBase implements InvoiceItem 
     public Currency getCurrency() {
         return currency;
     }
+
+    @Override
+    public InvoiceItemType getInvoiceItemType() {
+        return InvoiceItemType.RECURRING;
+    }
+
     @Override
     public InvoiceItem asReversingItem() {
         throw new NotImplementedException();
diff --git a/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java b/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java
index ba7084c..6c09c85 100644
--- a/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java
+++ b/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java
@@ -53,7 +53,7 @@ public abstract class AuditedCollectionDaoBase<T extends Entity> implements Audi
                     entityIterator.remove();
                     existingEntityIterator.remove();
 
-                    // if the entities have the same hashcode (e.g. same data), don't bother updating
+                    // if the entities have the same hash code (e.g. same data), don't bother updating
                     if (entity.hashCode() != existingEntity.hashCode()) {
                         entitiesToUpdate.add(entity);
                     }
diff --git a/util/src/main/java/com/ning/billing/util/dao/TableName.java b/util/src/main/java/com/ning/billing/util/dao/TableName.java
index 9253107..03e2c11 100644
--- a/util/src/main/java/com/ning/billing/util/dao/TableName.java
+++ b/util/src/main/java/com/ning/billing/util/dao/TableName.java
@@ -21,6 +21,7 @@ public enum TableName {
     ACCOUNT_HISTORY("account_history"),
     ACCOUNT_EMAIL_HISTORY("account_email_history"),
     BUNDLES("bundles"),
+    CREDIT_INVOICE_ITEMS("credit_invoice_items"),
     CUSTOM_FIELD_HISTORY("custom_field_history"),
     FIXED_INVOICE_ITEMS("fixed_invoice_items"),
     INVOICE_PAYMENTS("invoice_payments"),
diff --git a/util/src/main/java/com/ning/billing/util/entity/collection/dao/EntityCollectionSqlDao.java b/util/src/main/java/com/ning/billing/util/entity/collection/dao/EntityCollectionSqlDao.java
index 6fe2ec6..475ae8d 100644
--- a/util/src/main/java/com/ning/billing/util/entity/collection/dao/EntityCollectionSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/collection/dao/EntityCollectionSqlDao.java
@@ -58,7 +58,7 @@ public interface EntityCollectionSqlDao<T extends Entity> {
     @RegisterMapper(RecordIdMapper.class)
     @SqlQuery
     public List<Mapper<UUID, Long>> getRecordIds(@Bind("objectId") final String objectId,
-                                                @ObjectTypeBinder final ObjectType objectType);
+                                                 @ObjectTypeBinder final ObjectType objectType);
 
     @SqlUpdate
     public void test();
diff --git a/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java b/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
index 9b67ede..b988bde 100644
--- a/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
+++ b/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
@@ -20,6 +20,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.util.dao.ObjectType;
 import com.ning.billing.util.tag.dao.TagDao;
 
@@ -84,7 +85,7 @@ public class DefaultTagUserApi implements TagUserApi {
     }
 
     @Override
-    public void removeTags(UUID objectId, ObjectType objectType, List<TagDefinition> tagDefinitions, CallContext context) {
+    public void removeTags(UUID objectId, ObjectType objectType, List<TagDefinition> tagDefinitions, CallContext context)  {
         // TODO: consider making this batch
         for (TagDefinition tagDefinition : tagDefinitions) {
             tagDao.deleteTag(objectId, objectType, tagDefinition, 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
index 91cac4d..4e29fc2 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.util.tag.dao;
 
+import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.dao.AuditedCollectionDao;
 import com.ning.billing.util.dao.ObjectType;
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java
index 986e49d..bf08dcd 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java
@@ -42,12 +42,11 @@ public class TagMapper extends MapperBase implements ResultSetMapper<Tag> {
             }
         }
 
+        UUID id = UUID.fromString(result.getString("id"));
         if (thisTagType == null) {
-            UUID id = UUID.fromString(result.getString("id"));
-
             return new DescriptiveTag(id, name);
         } else {
-            return new DefaultControlTag(thisTagType);
+            return new DefaultControlTag(id, thisTagType);
         }
     }
 }
diff --git a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
index f37b636..e67ef75 100644
--- a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
+++ b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
@@ -22,6 +22,7 @@ import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.TagDefinition;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -77,6 +78,10 @@ public class MockTagDao implements TagDao {
             }
         };
 
+        if (tagStore.get(objectId) == null) {
+            tagStore.put(objectId, new ArrayList<Tag>());
+        }
+
         tagStore.get(objectId).add(tag);
     }
 
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 1bded44..b452203 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
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallOrigin;
 import com.ning.billing.util.callcontext.UserType;
@@ -417,7 +418,7 @@ public class TestTagStore {
 
         Handle handle = dbi.open();
         String query = String.format("select * from audit_log a inner join tag_history th on a.record_id = th.history_record_id where a.table_name = 'tag_history' and th.id='%s' and a.change_type='DELETE'",
-                                     tag.getId().toString());
+                tag.getId().toString());
         List<Map<String, Object>> result = handle.select(query);
         handle.close();
 
@@ -440,7 +441,7 @@ public class TestTagStore {
     }
 
     @Test
-    public void testRemoveTag() {
+    public void testRemoveTag() throws InvoiceApiException {
         UUID objectId = UUID.randomUUID();
         ObjectType objectType = ObjectType.INVOICE;
         TagDefinition tagDefinition = new DefaultTagDefinition("test tag", "test", false);