killbill-aplcache

Rework CBA logics Fix Refund issues Fix date issues in InvoiceGenerator Re-enable

7/1/2012 2:35:28 AM

Changes

invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java 970(+0 -970)

Details

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 2cfec3f..e20ffeb 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
@@ -26,6 +26,7 @@ import java.util.UUID;
 import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
+import org.joda.time.field.ZeroIsMaxDateTimeField;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
@@ -215,12 +216,15 @@ public class DefaultInvoiceDao implements InvoiceDao {
             @Override
             public BigDecimal inTransaction(final InvoiceSqlDao transactional, final TransactionStatus status) throws Exception {
 
+                BigDecimal cba = BigDecimal.ZERO;
+
                 BigDecimal accountBalance = BigDecimal.ZERO;
                 List<Invoice> invoices = getAllInvoicesByAccountFromTransaction(accountId, transactional);
                 for (Invoice cur : invoices) {
                     accountBalance = accountBalance.add(cur.getBalance());
+                    cba = cba.add(cur.getCBAAmount());
                 }
-                return accountBalance;
+                return accountBalance.subtract(cba);
             }
         });
     }
@@ -345,7 +349,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
                     final InvoiceItem cbaAdjItem = new CreditBalanceAdjInvoiceItem(invoice.getId(), invoice.getAccountId(), context.getCreatedDate(), cbaAdjAmount, invoice.getCurrency());
                     transInvoiceItemDao.create(cbaAdjItem, context);
                 }
-                final BigDecimal requestedPositiveAmountAfterCbaAdj = requestedPositiveAmount.subtract(cbaAdjAmount);
+                final BigDecimal requestedPositiveAmountAfterCbaAdj = requestedPositiveAmount.add(cbaAdjAmount);
 
                 if (isInvoiceAdjusted) {
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/generator/DefaultInvoiceGenerator.java b/invoice/src/main/java/com/ning/billing/invoice/generator/DefaultInvoiceGenerator.java
index e3955b3..118bf8a 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/generator/DefaultInvoiceGenerator.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/generator/DefaultInvoiceGenerator.java
@@ -175,7 +175,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
         for (final InvoiceItem item : proposedItems) {
             if (item.getInvoiceItemType() == InvoiceItemType.CBA_ADJ) {
                 totalUnusedCreditAmount = totalUnusedCreditAmount.add(item.getAmount());
-            } else {
+            } else if (item.getInvoiceId().equals(invoiceId)) {
                 totalAmountOwed = totalAmountOwed.add(item.getAmount());
             }
         }
@@ -310,7 +310,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
             final BillingMode billingMode = instantiateBillingMode(thisEvent.getBillingMode());
             final DateTime startDate = thisEvent.getEffectiveDate();
             final DateTime tzAdjustedStartDate = startDate.toDateTime(thisEvent.getTimeZone());
-            final DateTime roundedStartDate = new DateTime(tzAdjustedStartDate.getYear(), tzAdjustedStartDate.getMonthOfYear(), tzAdjustedStartDate.getDayOfMonth(), 0, 0);
+            final DateTime roundedStartDate = new DateTime(tzAdjustedStartDate.getYear(), tzAdjustedStartDate.getMonthOfYear(), tzAdjustedStartDate.getDayOfMonth(), 0, 0, thisEvent.getTimeZone());
             log.info(String.format("start = %s, rounded = %s, target = %s, in = %s", startDate, roundedStartDate, targetDate, (!roundedStartDate.isAfter(targetDate)) ? "in" : "out"));
             if (!roundedStartDate.isAfter(targetDate)) {
                 final DateTime endDate = (nextEvent == null) ? null : nextEvent.getEffectiveDate();
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
index b2d134a..9d551cc 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
@@ -56,7 +56,7 @@ import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.globallocker.GlobalLock;
 import com.ning.billing.util.globallocker.GlobalLocker;
-import com.ning.billing.util.globallocker.GlobalLocker.LockerService;
+import com.ning.billing.util.globallocker.GlobalLocker.LockerType;
 import com.ning.billing.util.globallocker.LockFailedException;
 
 public class InvoiceDispatcher {
@@ -123,7 +123,7 @@ public class InvoiceDispatcher {
                                   final boolean dryRun, final CallContext context) throws InvoiceApiException {
         GlobalLock lock = null;
         try {
-            lock = locker.lockWithNumberOfTries(LockerService.INVOICE, accountId.toString(), NB_LOCK_TRY);
+            lock = locker.lockWithNumberOfTries(LockerType.ACCOUNT, accountId.toString(), NB_LOCK_TRY);
 
             return processAccountWithLock(accountId, targetDate, dryRun, context);
         } catch (LockFailedException e) {
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 bb25497..797b228 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
@@ -198,7 +198,7 @@ public class DefaultInvoice extends EntityBase implements Invoice {
     }
     @Override
     public BigDecimal getBalance() {
-        final BigDecimal balance = getChargedAmount().add(getTotalAdjAmount()).subtract(getPaidAmount());;
+        final BigDecimal balance = getChargedAmount().add(getTotalAdjAmount()).add(getCBAAmount()).subtract(getPaidAmount());;
         return balance;
     }
 
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 a4ed003..842e4d9 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
@@ -43,10 +43,22 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem 
     protected final String planName;
     protected final String phaseName;
 
-    /* Recurring specific STEPH */
+    /* Recurring specific */
     protected final BigDecimal rate;
-    protected final UUID reversedItemId;
 
+    /* RepairAdjInvoiceItem */
+   protected final UUID linkedItemId;
+
+
+    @Override
+    public String toString() {
+        return getInvoiceItemType() + ": [startDate=" + startDate + ", endDate="
+                + endDate + ", amount=" + amount + ", currency=" + currency
+                + ", invoiceId=" + invoiceId
+                + ", subscriptionId=" + subscriptionId + ", planName="
+                + planName + ", phaseName=" + phaseName + ", rate=" + rate
+                + ", linkedItemId=" + linkedItemId + "]";
+    }
 
     /*
      * CTOR without ID; called from generator when creating invoice item
@@ -115,7 +127,7 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem 
         this.amount = amount;
         this.currency = currency;
         this.rate = rate;
-        this.reversedItemId = reversedItemId;
+        this.linkedItemId = reversedItemId;
     }
 
     @Override
@@ -175,7 +187,7 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem 
 
     @Override
     public UUID getLinkedItemId() {
-        return reversedItemId;
+        return linkedItemId;
     }
 
     @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 8683b9a..23ac57b 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
@@ -63,11 +63,11 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
 
     @Override
     public UUID getLinkedItemId() {
-        return reversedItemId;
+        return linkedItemId;
     }
 
     public boolean reversesItem() {
-        return (reversedItemId != null);
+        return (linkedItemId != null);
     }
 
     @Override
@@ -145,7 +145,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
         if (rate.compareTo(that.rate) != 0) {
             return false;
         }
-        if (reversedItemId != null ? !reversedItemId.equals(that.reversedItemId) : that.reversedItemId != null) {
+        if (linkedItemId != null ? !linkedItemId.equals(that.linkedItemId) : that.linkedItemId != null) {
             return false;
         }
         if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
@@ -171,25 +171,11 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
         result = 31 * result + rate.hashCode();
         result = 31 * result + currency.hashCode();
         result = 31 * result + getInvoiceItemType().hashCode();
-        result = 31 * result + (reversedItemId != null ? reversedItemId.hashCode() : 0);
+        result = 31 * result + (linkedItemId != null ? linkedItemId.hashCode() : 0);
         return result;
     }
 
     @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-
-        sb.append(phaseName).append(", ");
-        sb.append(startDate.toString()).append(", ");
-        sb.append(endDate.toString()).append(", ");
-        sb.append(amount.toString()).append(", ");
-        sb.append("subscriptionId = ").append(subscriptionId == null ? null : subscriptionId.toString()).append(", ");
-        sb.append("bundleId = ").append(bundleId == null ? null : bundleId.toString()).append(", ");
-
-        return sb.toString();
-    }
-
-    @Override
     public InvoiceItemType getInvoiceItemType() {
         return InvoiceItemType.RECURRING;
     }
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 6a5631d..c1c40af 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
@@ -53,7 +53,7 @@ import com.ning.billing.util.tag.dao.TagDefinitionDao;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
-public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
+public class InvoiceDaoTestBase extends InvoicingTestBase {
     protected final TagEventBuilder tagEventBuilder = new TagEventBuilder();
 
     protected IDBI dbi;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGenerator.java b/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGenerator.java
index 7a55724..b46edc7 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGenerator.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGenerator.java
@@ -13,324 +13,998 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package com.ning.billing.invoice.generator;
 
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertEquals;
+package com.ning.billing.invoice.generator;
 
+import javax.annotation.Nullable;
 import java.math.BigDecimal;
-import java.util.LinkedList;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-import org.testng.annotations.BeforeClass;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.testng.annotations.Test;
 
+import com.ning.billing.catalog.DefaultPrice;
+import com.ning.billing.catalog.MockInternationalPrice;
+import com.ning.billing.catalog.MockPlan;
+import com.ning.billing.catalog.MockPlanPhase;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.config.InvoiceConfig;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.billing.BillingEvent;
+import com.ning.billing.entitlement.api.billing.BillingModeType;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.invoice.MockBillingEventSet;
 import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoiceItem;
-import com.ning.billing.invoice.api.InvoiceItemType;
+import com.ning.billing.invoice.api.InvoicePayment.InvoicePaymentType;
+import com.ning.billing.invoice.generator.DefaultInvoiceGenerator;
+import com.ning.billing.invoice.generator.InvoiceGenerator;
 import com.ning.billing.invoice.model.CreditBalanceAdjInvoiceItem;
+import com.ning.billing.invoice.model.DefaultInvoice;
+import com.ning.billing.invoice.model.DefaultInvoicePayment;
 import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
-import com.ning.billing.invoice.model.RepairAdjInvoiceItem;
+import com.ning.billing.invoice.tests.InvoicingTestBase;
+import com.ning.billing.junction.api.BillingEventSet;
+import com.ning.billing.mock.BrainDeadProxyFactory;
+import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.clock.DefaultClock;
 
-public class TestDefaultInvoiceGenerator  {
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
 
-    private DefaultInvoiceGenerator gen;
-    private Clock clock;
+public class TestDefaultInvoiceGenerator extends InvoicingTestBase {
 
-    private final UUID invoiceId = UUID.randomUUID();
-    private final UUID accountId = UUID.randomUUID();
-    private final UUID subscriptionId = UUID.randomUUID();
-    private final UUID bundleId = UUID.randomUUID();
-    private final String planName = "my-plan";
-    private final String phaseName = "my-phase";
-    private final Currency currency = Currency.USD;
+    private static final Logger log = LoggerFactory.getLogger(TestDefaultInvoiceGenerator.class);
 
-    public static final class TestDefaultInvoiceGeneratorMock extends DefaultInvoiceGenerator {
-        public TestDefaultInvoiceGeneratorMock(final Clock clock, final InvoiceConfig config) {
-            super(clock, config);
-        }
-    }
+    private final Clock clock = new ClockMock();
+
+    private final InvoiceGenerator generator;
 
-    @BeforeClass(groups = {"fast"})
-    public void setup() {
-        clock = new ClockMock();
-        gen = new TestDefaultInvoiceGeneratorMock(clock, new InvoiceConfig() {
+    public TestDefaultInvoiceGenerator() {
+        super();
+
+        final Clock clock = new DefaultClock();
+        final InvoiceConfig invoiceConfig = new InvoiceConfig() {
             @Override
-            public boolean isNotificationProcessingOff() {
-                return false;
+            public long getSleepTimeMs() {
+                throw new UnsupportedOperationException();
             }
+
             @Override
-            public boolean isEmailNotificationsEnabled() {
-                return false;
+            public int getNumberOfMonthsInFuture() {
+                return 36;
             }
+
             @Override
-            public long getSleepTimeMs() {
-                return 100;
+            public boolean isEmailNotificationsEnabled() {
+                return false;
             }
+
             @Override
-            public int getNumberOfMonthsInFuture() {
-                return 5;
+            public boolean isNotificationProcessingOff() {
+                throw new UnsupportedOperationException();
             }
-        });
+        };
+        this.generator = new DefaultInvoiceGenerator(clock, invoiceConfig);
     }
 
-    @Test(groups = {"fast"}, enabled= true)
-    public void testRemoveCancellingInvoiceItemsFixedPrice() {
-
-        DateTime startDate = clock.getUTCNow();
-        DateTime endDate = startDate.plusDays(30);
-        DateTime nextEndDate = startDate.plusMonths(1);
+    @Test(groups = {"fast"})
+    public void testWithNullEventSetAndNullInvoiceSet() throws InvoiceApiException {
+        final UUID accountId = UUID.randomUUID();
+        final Invoice invoice = generator.generateInvoice(accountId, null, null, new DateTime(), Currency.USD);
 
-        BigDecimal amount = new BigDecimal("12.00");
-        BigDecimal rate2 = new BigDecimal("14.85");
-        BigDecimal amount2 = rate2;
-        List<InvoiceItem> items =  new LinkedList<InvoiceItem>();
-        InvoiceItem item1 = new FixedPriceInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
-        items.add(item1);
-        items.add(new RepairAdjInvoiceItem(invoiceId, accountId, startDate, endDate, amount.negate(), currency, item1.getId()));
-        items.add(new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, endDate, nextEndDate, amount2, rate2, currency));
-        gen.removeCancellingInvoiceItems(items);
-        assertEquals(items.size(), 1);
-        InvoiceItem leftItem = items.get(0);
-        assertEquals(leftItem.getInvoiceItemType(), InvoiceItemType.RECURRING);
-        assertEquals(leftItem.getAmount(), amount2);
+        assertNull(invoice);
     }
 
     @Test(groups = {"fast"})
-    public void testRemoveCancellingInvoiceItemsRecurringPrice() {
-
-        DateTime startDate = clock.getUTCNow();
-        DateTime endDate = startDate.plusDays(30);
-        DateTime nextEndDate = startDate.plusMonths(1);
-
-        BigDecimal rate1 = new BigDecimal("12.00");
-        BigDecimal amount1 = rate1;
-        BigDecimal rate2 = new BigDecimal("14.85");
-        BigDecimal amount2 = rate2;
-        List<InvoiceItem> items =  new LinkedList<InvoiceItem>();
-        InvoiceItem item1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount1, rate1, currency, null);
-        items.add(item1);
-        items.add(new RepairAdjInvoiceItem(invoiceId, accountId, startDate, endDate, amount1.negate(), currency, item1.getId()));
-        items.add(new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, endDate, nextEndDate, amount2, rate2, currency));
-        gen.removeCancellingInvoiceItems(items);
-        assertEquals(items.size(), 1);
-        InvoiceItem leftItem = items.get(0);
-        assertEquals(leftItem.getInvoiceItemType(), InvoiceItemType.RECURRING);
-        assertEquals(leftItem.getAmount(), amount2);
+    public void testWithEmptyEventSet() throws InvoiceApiException {
+        final BillingEventSet events = new MockBillingEventSet();
+
+        final UUID accountId = UUID.randomUUID();
+        final Invoice invoice = generator.generateInvoice(accountId, events, null, new DateTime(), Currency.USD);
+
+        assertNull(invoice);
     }
 
     @Test(groups = {"fast"})
-    public void testRemoveDuplicatedInvoiceItemsFixedPrice() {
+    public void testWithSingleMonthlyEvent() throws InvoiceApiException, CatalogApiException {
+        final BillingEventSet events = new MockBillingEventSet();
+
+        final Subscription sub = createZombieSubscription();
+        final DateTime startDate = buildDateTime(2011, 9, 1);
 
-        DateTime startDate = clock.getUTCNow();
-        DateTime endDate = startDate.plusDays(30);
-        DateTime nextEndDate = startDate.plusMonths(1);
+        final Plan plan = new MockPlan();
+        final BigDecimal rate1 = TEN;
+        final PlanPhase phase = createMockMonthlyPlanPhase(rate1);
 
-        BigDecimal amount1 = new BigDecimal("12.00");
+        final BillingEvent event = createBillingEvent(sub.getId(), startDate, plan, phase, 1);
+        events.add(event);
 
-        BigDecimal amount2 = new BigDecimal("14.85");
+        final DateTime targetDate = buildDateTime(2011, 10, 3);
+        final UUID accountId = UUID.randomUUID();
+        final Invoice invoice = generator.generateInvoice(accountId, events, null, targetDate, Currency.USD);
 
-        BigDecimal rate3 = new BigDecimal("14.85");
-        BigDecimal amount3 = rate3;
+        assertNotNull(invoice);
+        assertEquals(invoice.getNumberOfItems(), 2);
+        assertEquals(invoice.getBalance(), TWENTY);
+        assertEquals(invoice.getInvoiceItems().get(0).getSubscriptionId(), sub.getId());
+    }
+
+    private Subscription createZombieSubscription() {
+        return createZombieSubscription(UUID.randomUUID());
+    }
+
+    private Subscription createZombieSubscription(final UUID subscriptionId) {
+        final Subscription sub = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
+        ((ZombieControl) sub).addResult("getId", subscriptionId);
+        ((ZombieControl) sub).addResult("getBundleId", UUID.randomUUID());
+
+        return sub;
+    }
+
+    @Test(groups = {"fast"})
+    public void testWithSingleMonthlyEventWithLeadingProRation() throws InvoiceApiException, CatalogApiException {
+        final BillingEventSet events = new MockBillingEventSet();
+
+        final Subscription sub = createZombieSubscription();
+        final DateTime startDate = buildDateTime(2011, 9, 1);
 
-        List<InvoiceItem> existing =  new LinkedList<InvoiceItem>();
-        InvoiceItem item1 = new FixedPriceInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount1, currency);
-        existing.add(item1);
+        final Plan plan = new MockPlan();
+        final BigDecimal rate = TEN;
+        final PlanPhase phase = createMockMonthlyPlanPhase(rate);
+        final BillingEvent event = createBillingEvent(sub.getId(), startDate, plan, phase, 15);
+        events.add(event);
 
-        List<InvoiceItem> proposed =  new LinkedList<InvoiceItem>();
-        InvoiceItem other = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, endDate, nextEndDate, amount3, rate3, currency, null);
-        proposed.add(item1);
-        proposed.add(other);
+        final DateTime targetDate = buildDateTime(2011, 10, 3);
+        final UUID accountId = UUID.randomUUID();
+        final Invoice invoice = generator.generateInvoice(accountId, events, null, targetDate, Currency.USD);
 
-        gen.removeDuplicatedInvoiceItems(proposed, existing);
-        assertEquals(existing.size(), 0);
-        assertEquals(proposed.size(), 1);
-        InvoiceItem leftItem = proposed.get(0);
-        assertEquals(leftItem.getInvoiceItemType(), InvoiceItemType.RECURRING);
-        assertEquals(leftItem.getAmount(), amount2);
+        assertNotNull(invoice);
+        assertEquals(invoice.getNumberOfItems(), 2);
+
+        final BigDecimal expectedNumberOfBillingCycles;
+        expectedNumberOfBillingCycles = ONE.add(FOURTEEN.divide(THIRTY_ONE, 2 * NUMBER_OF_DECIMALS, ROUNDING_METHOD));
+        final BigDecimal expectedAmount = expectedNumberOfBillingCycles.multiply(rate).setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
+        assertEquals(invoice.getBalance(), expectedAmount);
     }
 
     @Test(groups = {"fast"})
-    public void testRemoveDuplicatedInvoiceItemsRecuringPrice() {
+    public void testTwoMonthlySubscriptionsWithAlignedBillingDates() throws InvoiceApiException, CatalogApiException {
+        final BillingEventSet events = new MockBillingEventSet();
+
+        final Plan plan1 = new MockPlan();
+        final BigDecimal rate1 = FIVE;
+        final PlanPhase phase1 = createMockMonthlyPlanPhase(rate1);
 
-        DateTime startDate = clock.getUTCNow();
-        DateTime endDate = startDate.plusDays(30);
-        DateTime nextEndDate = startDate.plusMonths(1);
+        final Plan plan2 = new MockPlan();
+        final BigDecimal rate2 = TEN;
+        final PlanPhase phase2 = createMockMonthlyPlanPhase(rate2);
 
-        BigDecimal rate1 = new BigDecimal("12.00");
-        BigDecimal amount1 = rate1;
+        final Subscription sub = createZombieSubscription();
 
-        BigDecimal rate2 = new BigDecimal("14.85");
-        BigDecimal amount2 = rate2;
+        final BillingEvent event1 = createBillingEvent(sub.getId(), buildDateTime(2011, 9, 1), plan1, phase1, 1);
+        events.add(event1);
 
-        List<InvoiceItem> existing =  new LinkedList<InvoiceItem>();
-        InvoiceItem item1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount1, rate1, currency, null);
-        existing.add(item1);
+        final BillingEvent event2 = createBillingEvent(sub.getId(), buildDateTime(2011, 10, 1), plan2, phase2, 1);
+        events.add(event2);
 
-        List<InvoiceItem> proposed =  new LinkedList<InvoiceItem>();
-        InvoiceItem other = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, endDate, nextEndDate, amount2, rate2, currency, null);
-        proposed.add(item1);
-        proposed.add(other);
+        final DateTime targetDate = buildDateTime(2011, 10, 3);
+        final UUID accountId = UUID.randomUUID();
+        final Invoice invoice = generator.generateInvoice(accountId, events, null, targetDate, Currency.USD);
 
-        gen.removeDuplicatedInvoiceItems(proposed, existing);
-        assertEquals(existing.size(), 0);
-        assertEquals(proposed.size(), 1);
-        InvoiceItem leftItem = proposed.get(0);
-        assertEquals(leftItem.getInvoiceItemType(), InvoiceItemType.RECURRING);
-        assertEquals(leftItem.getAmount(), amount2);
+        assertNotNull(invoice);
+        assertEquals(invoice.getNumberOfItems(), 2);
+        assertEquals(invoice.getBalance(), rate1.add(rate2).setScale(NUMBER_OF_DECIMALS));
     }
 
-    // STEPH same as testRemoveCancellingInvoiceItemsFixedPrice: should we have one for FixedPrice?
     @Test(groups = {"fast"})
-    public void testAddRepairedItemsItemsRecurringPrice() {
-        DateTime startDate = clock.getUTCNow();
-        DateTime endDate = startDate.plusDays(30);
-        DateTime nextEndDate = startDate.plusMonths(1);
+    public void testOnePlan_TwoMonthlyPhases_ChangeImmediate() throws InvoiceApiException, CatalogApiException {
+        final BillingEventSet events = new MockBillingEventSet();
 
-        BigDecimal rate1 = new BigDecimal("12.00");
-        BigDecimal amount1 = rate1;
+        final Plan plan1 = new MockPlan();
+        final BigDecimal rate1 = FIVE;
+        final PlanPhase phase1 = createMockMonthlyPlanPhase(rate1);
 
-        BigDecimal rate2 = new BigDecimal("14.85");
-        BigDecimal amount2 = rate2;
+        final Subscription sub = createZombieSubscription();
+        final BillingEvent event1 = createBillingEvent(sub.getId(), buildDateTime(2011, 9, 1), plan1, phase1, 1);
+        events.add(event1);
 
-        UUID firstInvoiceId = UUID.randomUUID();
-        List<InvoiceItem> existing =  new LinkedList<InvoiceItem>();
-        InvoiceItem item1 = new RecurringInvoiceItem(firstInvoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount1, rate1, currency, null);
-        existing.add(item1);
+        final BigDecimal rate2 = TEN;
+        final PlanPhase phase2 = createMockMonthlyPlanPhase(rate2);
+        final BillingEvent event2 = createBillingEvent(sub.getId(), buildDateTime(2011, 10, 15), plan1, phase2, 15);
+        events.add(event2);
 
-        List<InvoiceItem> proposed =  new LinkedList<InvoiceItem>();
-        InvoiceItem other = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, endDate, nextEndDate, amount2, rate2, currency, null);
-        proposed.add(other);
+        final DateTime targetDate = buildDateTime(2011, 12, 3);
+        final UUID accountId = UUID.randomUUID();
+        final Invoice invoice = generator.generateInvoice(accountId, events, null, targetDate, Currency.USD);
 
-        gen.addRepairedItems(existing, proposed);
-        assertEquals(existing.size(), 1);
-        assertEquals(proposed.size(), 2);
-        InvoiceItem leftItem1 = proposed.get(0);
-        assertEquals(leftItem1.getInvoiceId(), invoiceId);
-        assertEquals(leftItem1.getInvoiceItemType(), InvoiceItemType.RECURRING);
-        assertEquals(leftItem1.getAmount(), amount2);
+        assertNotNull(invoice);
+        assertEquals(invoice.getNumberOfItems(), 4);
 
-        InvoiceItem newItem2 = proposed.get(1);
-        assertEquals(newItem2.getInvoiceId(), firstInvoiceId);
-        assertEquals(newItem2.getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
-        assertEquals(newItem2.getAmount(), item1.getAmount().negate());
-        assertEquals(newItem2.getLinkedItemId(), item1.getId());
+        final BigDecimal numberOfCyclesEvent1;
+        numberOfCyclesEvent1 = ONE.add(FOURTEEN.divide(THIRTY_ONE, 2 * NUMBER_OF_DECIMALS, ROUNDING_METHOD));
 
+        final BigDecimal numberOfCyclesEvent2 = TWO;
+
+        BigDecimal expectedValue;
+        expectedValue = numberOfCyclesEvent1.multiply(rate1);
+        expectedValue = expectedValue.add(numberOfCyclesEvent2.multiply(rate2));
+        expectedValue = expectedValue.setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
+
+        assertEquals(invoice.getBalance(), expectedValue);
     }
 
     @Test(groups = {"fast"})
-    public void testGenerateCreditsForPastRepairedInvoices() {
+    public void testOnePlan_ThreeMonthlyPhases_ChangeEOT() throws InvoiceApiException, CatalogApiException {
+        final BillingEventSet events = new MockBillingEventSet();
+
+        final Plan plan1 = new MockPlan();
+        final BigDecimal rate1 = FIVE;
+        final PlanPhase phase1 = createMockMonthlyPlanPhase(rate1);
+
+        final Subscription sub = createZombieSubscription();
+        final BillingEvent event1 = createBillingEvent(sub.getId(), buildDateTime(2011, 9, 1), plan1, phase1, 1);
+        events.add(event1);
+
+        final BigDecimal rate2 = TEN;
+        final PlanPhase phase2 = createMockMonthlyPlanPhase(rate2);
+        final BillingEvent event2 = createBillingEvent(sub.getId(), buildDateTime(2011, 10, 1), plan1, phase2, 1);
+        events.add(event2);
+
+        final BigDecimal rate3 = THIRTY;
+        final PlanPhase phase3 = createMockMonthlyPlanPhase(rate3);
+        final BillingEvent event3 = createBillingEvent(sub.getId(), buildDateTime(2011, 11, 1), plan1, phase3, 1);
+        events.add(event3);
+
+        final DateTime targetDate = buildDateTime(2011, 12, 3);
+        final UUID accountId = UUID.randomUUID();
+        final Invoice invoice = generator.generateInvoice(accountId, events, null, targetDate, Currency.USD);
+
+        assertNotNull(invoice);
+        assertEquals(invoice.getNumberOfItems(), 4);
+        assertEquals(invoice.getBalance(), rate1.add(rate2).add(TWO.multiply(rate3)).setScale(NUMBER_OF_DECIMALS));
+    }
 
-        DateTime startDate = clock.getUTCNow();
-        DateTime endDate = startDate.plusDays(30);
-        DateTime nextEndDate = startDate.plusMonths(1);
+    @Test(groups = {"fast"})
+    public void testSingleEventWithExistingInvoice() throws InvoiceApiException, CatalogApiException {
+        final BillingEventSet events = new MockBillingEventSet();
+
+        final Subscription sub = createZombieSubscription();
+        final DateTime startDate = buildDateTime(2011, 9, 1);
+
+        final Plan plan1 = new MockPlan();
+        final BigDecimal rate = FIVE;
+        final PlanPhase phase1 = createMockMonthlyPlanPhase(rate);
+
+        final BillingEvent event1 = createBillingEvent(sub.getId(), startDate, plan1, phase1, 1);
+        events.add(event1);
 
-        BigDecimal rate1 = new BigDecimal("10.00");
-        BigDecimal amount1 = rate1;
+        DateTime targetDate = buildDateTime(2011, 12, 1);
+        final UUID accountId = UUID.randomUUID();
+        final Invoice invoice1 = generator.generateInvoice(accountId, events, null, targetDate, Currency.USD);
+        final List<Invoice> existingInvoices = new ArrayList<Invoice>();
+        existingInvoices.add(invoice1);
 
-        List<InvoiceItem> existing =  new LinkedList<InvoiceItem>();
-        InvoiceItem item1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount1, rate1, currency, null);
-        existing.add(item1);
+        targetDate = buildDateTime(2011, 12, 3);
+        final Invoice invoice2 = generator.generateInvoice(accountId, events, existingInvoices, targetDate, Currency.USD);
 
+        assertNull(invoice2);
+    }
+
+    // TODO: modify this test to keep a running total of expected invoice amount over time
+    @Test(groups = {"fast"})
+    public void testMultiplePlansWithUtterChaos() throws InvoiceApiException, CatalogApiException {
+        // plan 1: change of phase from trial to discount followed by immediate cancellation; (covers phase change, cancel, pro-ration)
+        // plan 2: single plan that moves from trial to discount to evergreen; BCD = 10 (covers phase change)
+        // 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
+        final UUID accountId = UUID.randomUUID();
+        final UUID subscriptionId1 = UUID.randomUUID();
+        final UUID subscriptionId2 = UUID.randomUUID();
+        final UUID subscriptionId3 = UUID.randomUUID();
+        final UUID subscriptionId4 = UUID.randomUUID();
+        final UUID subscriptionId5 = UUID.randomUUID();
+
+        final Plan plan1 = new MockPlan("Change from trial to discount with immediate cancellation");
+        final PlanPhase plan1Phase1 = createMockMonthlyPlanPhase(EIGHT, PhaseType.TRIAL);
+        final PlanPhase plan1Phase2 = createMockMonthlyPlanPhase(TWELVE, PhaseType.DISCOUNT);
+        final PlanPhase plan1Phase3 = createMockMonthlyPlanPhase();
+        final DateTime plan1StartDate = buildDateTime(2011, 1, 5);
+        final DateTime plan1PhaseChangeDate = buildDateTime(2011, 4, 5);
+        final DateTime plan1CancelDate = buildDateTime(2011, 4, 29);
+
+        final Plan plan2 = new MockPlan("Change phase from trial to discount to evergreen");
+        final PlanPhase plan2Phase1 = createMockMonthlyPlanPhase(TWENTY, PhaseType.TRIAL);
+        final PlanPhase plan2Phase2 = createMockMonthlyPlanPhase(THIRTY, PhaseType.DISCOUNT);
+        final PlanPhase plan2Phase3 = createMockMonthlyPlanPhase(FORTY, PhaseType.EVERGREEN);
+        final DateTime plan2StartDate = buildDateTime(2011, 3, 10);
+        final DateTime plan2PhaseChangeToDiscountDate = buildDateTime(2011, 6, 10);
+        final DateTime plan2PhaseChangeToEvergreenDate = buildDateTime(2011, 9, 10);
+
+        final Plan plan3 = new MockPlan("Upgrade with immediate change, BCD = 31");
+        final PlanPhase plan3Phase1 = createMockMonthlyPlanPhase(TEN, PhaseType.EVERGREEN);
+        final PlanPhase plan3Phase2 = createMockAnnualPlanPhase(ONE_HUNDRED, PhaseType.EVERGREEN);
+        final DateTime plan3StartDate = buildDateTime(2011, 5, 20);
+        final DateTime plan3UpgradeToAnnualDate = buildDateTime(2011, 7, 31);
+
+        final Plan plan4a = new MockPlan("Plan change effective EOT; plan 1");
+        final Plan plan4b = new MockPlan("Plan change effective EOT; plan 2");
+        final PlanPhase plan4aPhase1 = createMockMonthlyPlanPhase(FIFTEEN);
+        final PlanPhase plan4bPhase1 = createMockMonthlyPlanPhase(TWENTY_FOUR);
+
+        final DateTime plan4StartDate = buildDateTime(2011, 6, 7);
+        final DateTime plan4ChangeOfPlanDate = buildDateTime(2011, 8, 7);
+
+        final Plan plan5 = new MockPlan("Add-on");
+        final PlanPhase plan5Phase1 = createMockMonthlyPlanPhase(TWENTY);
+        final PlanPhase plan5Phase2 = createMockMonthlyPlanPhase();
+        final DateTime plan5StartDate = buildDateTime(2011, 6, 21);
+        final DateTime plan5CancelDate = buildDateTime(2011, 10, 7);
+
+        BigDecimal expectedAmount;
+        final List<Invoice> invoices = new ArrayList<Invoice>();
+        final BillingEventSet events = new MockBillingEventSet();
+
+        // on 1/5/2011, create subscription 1 (trial)
+        events.add(createBillingEvent(subscriptionId1, plan1StartDate, plan1, plan1Phase1, 5));
+        expectedAmount = EIGHT;
+        testInvoiceGeneration(accountId, events, invoices, plan1StartDate, 1, expectedAmount);
+
+        // on 2/5/2011, invoice subscription 1 (trial)
+        expectedAmount = EIGHT;
+        testInvoiceGeneration(accountId, events, invoices, buildDateTime(2011, 2, 5), 1, expectedAmount);
+
+        // on 3/5/2011, invoice subscription 1 (trial)
+        expectedAmount = EIGHT;
+        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(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(accountId, events, invoices, plan1PhaseChangeDate, 1, expectedAmount);
+
+        // on 4/10/2011, invoice subscription 2 (trial)
+        expectedAmount = TWENTY;
+        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));
+        // previous invoices are adjusted; this is the pro-ration amount only
+        expectedAmount = TWELVE.multiply(TWENTY_FOUR.divide(THIRTY, NUMBER_OF_DECIMALS, ROUNDING_METHOD)).setScale(NUMBER_OF_DECIMALS);
+        testInvoiceGeneration(accountId, events, invoices, plan1CancelDate, 2, expectedAmount);
+
+        // on 5/10/2011, invoice subscription 2 (trial)
+        expectedAmount = TWENTY;
+        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(accountId, events, invoices, plan3StartDate, 1, expectedAmount);
+
+        // on 6/7/2011, create subscription 4
+        events.add(createBillingEvent(subscriptionId4, plan4StartDate, plan4a, plan4aPhase1, 7));
+        expectedAmount = FIFTEEN;
+        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(accountId, events, invoices, plan2PhaseChangeToDiscountDate, 1, expectedAmount);
+
+        // on 6/20/2011, invoice subscription 3 (monthly)
+        expectedAmount = TEN;
+        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(accountId, events, invoices, plan5StartDate, 1, expectedAmount);
+
+        // on 7/7/2011, invoice subscription 4 (plan 1)
+        expectedAmount = FIFTEEN;
+        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(accountId, events, invoices, buildDateTime(2011, 7, 10), 2, expectedAmount);
+
+        // on 7/20/2011, invoice subscription 3 (monthly)
+        expectedAmount = TEN;
+        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.add(TEN.multiply(ELEVEN.divide(THIRTY_ONE, 2 * NUMBER_OF_DECIMALS, ROUNDING_METHOD)));
+        expectedAmount = expectedAmount.setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
+        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(accountId, events, invoices, plan4ChangeOfPlanDate, 1, expectedAmount);
+
+        // on 8/10/2011, invoice plan 2 (discount), invoice subscription 5
+        expectedAmount = THIRTY.add(TWENTY);
+        testInvoiceGeneration(accountId, events, invoices, buildDateTime(2011, 8, 10), 2, expectedAmount);
+
+        // on 9/7/2011, invoice subscription 4 (plan 2)
+        expectedAmount = TWENTY_FOUR;
+        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(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(TWENTY_SEVEN.divide(THIRTY)).setScale(NUMBER_OF_DECIMALS));
+        testInvoiceGeneration(accountId, events, invoices, plan5CancelDate, 3, expectedAmount);
+
+        // on 10/10/2011, invoice plan 2 (evergreen)
+        expectedAmount = FORTY;
+        testInvoiceGeneration(accountId, events, invoices, buildDateTime(2011, 10, 10), 1, expectedAmount);
+    }
+
+    @Test(groups = {"fast"})
+    public void testZeroDollarEvents() throws InvoiceApiException, CatalogApiException {
+        final Plan plan = new MockPlan();
+        final PlanPhase planPhase = createMockMonthlyPlanPhase(ZERO);
+        final BillingEventSet events = new MockBillingEventSet();
+        final DateTime targetDate = buildDateTime(2011, 1, 1);
+        events.add(createBillingEvent(UUID.randomUUID(), targetDate, plan, planPhase, 1));
+
+        final Invoice invoice = generator.generateInvoice(UUID.randomUUID(), events, null, targetDate, Currency.USD);
+
+        assertEquals(invoice.getNumberOfItems(), 1);
+    }
 
-        UUID existingInvoiceId = UUID.randomUUID();
-        List<Invoice> existingInvoices = new LinkedList<Invoice>();
-        Invoice existingInvoice = mock(Invoice.class);
-        when(existingInvoice.getId()).thenReturn(existingInvoiceId);
-        when(existingInvoice.getBalance()).thenReturn(BigDecimal.ZERO);
-        when(existingInvoice.getInvoiceItems()).thenReturn(existing);
+    @Test(groups = {"fast"})
+    public void testEndDateIsCorrect() throws InvoiceApiException, CatalogApiException {
+        final Plan plan = new MockPlan();
+        final PlanPhase planPhase = createMockMonthlyPlanPhase(ZERO);
+        final BillingEventSet events = new MockBillingEventSet();
+        final DateTime startDate = clock.getUTCNow().minusDays(1);
+        final DateTime targetDate = startDate.plusDays(1);
 
-        BigDecimal rate2 = new BigDecimal("20.0");
-        BigDecimal amount2 = rate2;
+        events.add(createBillingEvent(UUID.randomUUID(), startDate, plan, planPhase, startDate.getDayOfMonth()));
 
-        List<InvoiceItem> proposed =  new LinkedList<InvoiceItem>();
-        InvoiceItem reversedItem1 = new RepairAdjInvoiceItem(existingInvoiceId, accountId, startDate, nextEndDate, item1.getAmount().negate(), currency, item1.getId());
-        InvoiceItem newItem1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount2, rate2, currency, null);
-        proposed.add(reversedItem1);
-        proposed.add(newItem1);
+        final Invoice invoice = generator.generateInvoice(UUID.randomUUID(), events, null, targetDate, Currency.USD);
+        final RecurringInvoiceItem item = (RecurringInvoiceItem) invoice.getInvoiceItems().get(0);
 
-        gen.generateCBAForExistingInvoices(accountId, existingInvoices, proposed, currency);
+        // end date of the invoice item should be equal to exactly one month later
+        assertEquals(item.getEndDate().compareTo(startDate.plusMonths(1)), 0);
+    }
 
-        assertEquals(proposed.size(), 3);
-        InvoiceItem reversedItemCheck1 = proposed.get(0);
-        assertEquals(reversedItemCheck1.getInvoiceId(), existingInvoiceId);
-        assertEquals(reversedItemCheck1.getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
-        assertEquals(reversedItemCheck1.getAmount(), item1.getAmount().negate());
-        assertEquals(reversedItemCheck1.getLinkedItemId(), item1.getId());
+    @Test(groups = {"fast"})
+    public void testFixedPriceLifeCycle() throws InvoiceApiException {
+        final UUID accountId = UUID.randomUUID();
+        final Subscription subscription = createZombieSubscription();
+
+        final Plan plan = new MockPlan("plan 1");
+        final MockInternationalPrice zeroPrice = new MockInternationalPrice(new DefaultPrice(ZERO, Currency.USD));
+        final MockInternationalPrice cheapPrice = new MockInternationalPrice(new DefaultPrice(ONE, Currency.USD));
+
+        final PlanPhase phase1 = new MockPlanPhase(null, zeroPrice, BillingPeriod.NO_BILLING_PERIOD, PhaseType.TRIAL);
+        final PlanPhase phase2 = new MockPlanPhase(cheapPrice, null, BillingPeriod.MONTHLY, PhaseType.DISCOUNT);
+
+        final DateTime changeDate = new DateTime("2012-04-1T00:00:00.000-08:00");
+
+        final BillingEventSet events = new MockBillingEventSet();
+
+        final BillingEvent event1 = createMockBillingEvent(null, subscription, new DateTime("2012-01-1T00:00:00.000-08:00"),
+                                                           plan, phase1,
+                                                           ZERO, null, Currency.USD, BillingPeriod.NO_BILLING_PERIOD, 1,
+                                                           BillingModeType.IN_ADVANCE, "Test Event 1", 1L,
+                                                           SubscriptionTransitionType.CREATE);
+
+        final BillingEvent event2 = createMockBillingEvent(null, subscription, changeDate,
+                                                           plan, phase2,
+                                                           ZERO, null, Currency.USD, BillingPeriod.NO_BILLING_PERIOD, 1,
+                                                           BillingModeType.IN_ADVANCE, "Test Event 2", 2L,
+                                                           SubscriptionTransitionType.PHASE);
+
+        events.add(event2);
+        events.add(event1);
+        final Invoice invoice1 = generator.generateInvoice(accountId, events, null, new DateTime("2012-02-01T00:01:00.000-08:00"), Currency.USD);
+        assertNotNull(invoice1);
+        assertEquals(invoice1.getNumberOfItems(), 1);
+
+        final List<Invoice> invoiceList = new ArrayList<Invoice>();
+        invoiceList.add(invoice1);
+        final Invoice invoice2 = generator.generateInvoice(accountId, events, invoiceList, new DateTime("2012-04-05T00:01:00.000-08:00"), Currency.USD);
+        assertNotNull(invoice2);
+        assertEquals(invoice2.getNumberOfItems(), 1);
+        final FixedPriceInvoiceItem item = (FixedPriceInvoiceItem) invoice2.getInvoiceItems().get(0);
+        assertEquals(item.getStartDate().compareTo(changeDate), 0);
+    }
 
-        InvoiceItem newItemCheck1 = proposed.get(1);
-        assertEquals(newItemCheck1.getInvoiceId(), invoiceId);
-        assertEquals(newItemCheck1.getInvoiceItemType(), InvoiceItemType.RECURRING);
-        assertEquals(newItemCheck1.getAmount(), amount2);
+    @Test(groups = {"fast"})
+    public void testMixedModeLifeCycle() throws InvoiceApiException, CatalogApiException {
+        // create a subscription with a fixed price and recurring price
+        final Plan plan1 = new MockPlan();
+        final BigDecimal monthlyRate = FIVE;
+        final BigDecimal fixedCost = TEN;
+        final PlanPhase phase1 = createMockMonthlyPlanPhase(monthlyRate, fixedCost, PhaseType.TRIAL);
+
+        final BillingEventSet events = new MockBillingEventSet();
+        final UUID subscriptionId = UUID.randomUUID();
+        final UUID accountId = UUID.randomUUID();
+
+        final DateTime startDate = new DateTime(2011, 1, 1, 3, 40, 27, 0);
+        final BillingEvent event1 = createBillingEvent(subscriptionId, startDate, plan1, phase1, 1);
+        events.add(event1);
+
+        // ensure both components are invoiced
+        final Invoice invoice1 = generator.generateInvoice(accountId, events, null, startDate, Currency.USD);
+        assertNotNull(invoice1);
+        assertEquals(invoice1.getNumberOfItems(), 2);
+        assertEquals(invoice1.getBalance(), FIFTEEN);
+
+        final List<Invoice> invoiceList = new ArrayList<Invoice>();
+        invoiceList.add(invoice1);
+
+        // move forward in time one billing period
+        final DateTime currentDate = startDate.plusMonths(1);
+
+        // ensure that only the recurring price is invoiced
+        final Invoice invoice2 = generator.generateInvoice(accountId, events, invoiceList, currentDate, Currency.USD);
+        assertNotNull(invoice2);
+        assertEquals(invoice2.getNumberOfItems(), 1);
+        assertEquals(invoice2.getBalance(), FIVE);
+    }
 
-        InvoiceItem creditItemCheck = proposed.get(2);
-        assertEquals(creditItemCheck.getInvoiceId(), existingInvoiceId);
-        assertEquals(creditItemCheck.getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
-        assertEquals(creditItemCheck.getAmount(), amount2.add(rate1.negate()));
+    @Test(groups = {"fast"})
+    public void testFixedModePlanChange() throws InvoiceApiException, CatalogApiException {
+        // create a subscription with a fixed price and recurring price
+        final Plan plan1 = new MockPlan();
+        final BigDecimal fixedCost1 = TEN;
+        final BigDecimal fixedCost2 = TWENTY;
+        final PlanPhase phase1 = createMockMonthlyPlanPhase(null, fixedCost1, PhaseType.TRIAL);
+        final PlanPhase phase2 = createMockMonthlyPlanPhase(null, fixedCost2, PhaseType.EVERGREEN);
+
+        final BillingEventSet events = new MockBillingEventSet();
+        final UUID subscriptionId = UUID.randomUUID();
+        final UUID accountId = UUID.randomUUID();
+
+        final DateTime startDate = new DateTime(2011, 1, 1, 3, 40, 27, 0);
+        final BillingEvent event1 = createBillingEvent(subscriptionId, startDate, plan1, phase1, 1);
+        events.add(event1);
+
+        // ensure that a single invoice item is generated for the fixed cost
+        final Invoice invoice1 = generator.generateInvoice(accountId, events, null, startDate, Currency.USD);
+        assertNotNull(invoice1);
+        assertEquals(invoice1.getNumberOfItems(), 1);
+        assertEquals(invoice1.getBalance(), fixedCost1);
+
+        final List<Invoice> invoiceList = new ArrayList<Invoice>();
+        invoiceList.add(invoice1);
+
+        // move forward in time one billing period
+        final DateTime phaseChangeDate = startDate.plusMonths(1);
+        final BillingEvent event2 = createBillingEvent(subscriptionId, phaseChangeDate, plan1, phase2, 1);
+        events.add(event2);
+
+        // ensure that a single invoice item is generated for the fixed cost
+        final Invoice invoice2 = generator.generateInvoice(accountId, events, invoiceList, phaseChangeDate, Currency.USD);
+        assertNotNull(invoice2);
+        assertEquals(invoice2.getNumberOfItems(), 1);
+        assertEquals(invoice2.getBalance(), fixedCost2);
     }
 
     @Test(groups = {"fast"})
-    public void testConsumeNotEnoughExistingCredit() {
-        testConsumeCreditInternal(new BigDecimal("12.00"), new BigDecimal("-10.00"));
+    public void testInvoiceGenerationFailureScenario() throws InvoiceApiException, CatalogApiException {
+        final BillingEventSet events = new MockBillingEventSet();
+        final UUID subscriptionId = UUID.randomUUID();
+        final UUID accountId = UUID.randomUUID();
+        final int BILL_CYCLE_DAY = 15;
+
+        // create subscription with a zero-dollar trial, a monthly discount period and a monthly evergreen
+        final Plan plan1 = new MockPlan();
+        final PlanPhase phase1 = createMockMonthlyPlanPhase(null, ZERO, PhaseType.TRIAL);
+        final BigDecimal DISCOUNT_PRICE = new BigDecimal("9.95");
+        final PlanPhase phase2 = createMockMonthlyPlanPhase(DISCOUNT_PRICE, null, PhaseType.DISCOUNT);
+        final PlanPhase phase3 = createMockMonthlyPlanPhase(new BigDecimal("19.95"), null, PhaseType.EVERGREEN);
+
+        // set up billing events
+        final DateTime creationDate = new DateTime(2012, 3, 6, 21, 36, 18, 896);
+        events.add(createBillingEvent(subscriptionId, creationDate, plan1, phase1, BILL_CYCLE_DAY));
+
+        // trialPhaseEndDate = 2012/4/5
+        final DateTime trialPhaseEndDate = creationDate.plusDays(30);
+        events.add(createBillingEvent(subscriptionId, trialPhaseEndDate, plan1, phase2, BILL_CYCLE_DAY));
+
+        // discountPhaseEndDate = 2012/10/5
+        final DateTime discountPhaseEndDate = trialPhaseEndDate.plusMonths(6);
+        events.add(createBillingEvent(subscriptionId, discountPhaseEndDate, plan1, phase3, BILL_CYCLE_DAY));
+
+        final Invoice invoice1 = generator.generateInvoice(accountId, events, null, creationDate, Currency.USD);
+        assertNotNull(invoice1);
+        assertEquals(invoice1.getNumberOfItems(), 1);
+        assertEquals(invoice1.getBalance().compareTo(ZERO), 0);
+
+        final List<Invoice> invoiceList = new ArrayList<Invoice>();
+        invoiceList.add(invoice1);
+
+        final Invoice invoice2 = generator.generateInvoice(accountId, events, invoiceList, trialPhaseEndDate, Currency.USD);
+        assertNotNull(invoice2);
+        assertEquals(invoice2.getNumberOfItems(), 1);
+        assertEquals(invoice2.getInvoiceItems().get(0).getStartDate().compareTo(trialPhaseEndDate), 0);
+        assertEquals(invoice2.getBalance().compareTo(new BigDecimal("3.21")), 0);
+
+        invoiceList.add(invoice2);
+        DateTime targetDate = trialPhaseEndDate.toMutableDateTime().dayOfMonth().set(BILL_CYCLE_DAY).toDateTime();
+        final Invoice invoice3 = generator.generateInvoice(accountId, events, invoiceList, targetDate, Currency.USD);
+        assertNotNull(invoice3);
+        assertEquals(invoice3.getNumberOfItems(), 1);
+        assertEquals(invoice3.getInvoiceItems().get(0).getStartDate().compareTo(targetDate), 0);
+        assertEquals(invoice3.getBalance().compareTo(DISCOUNT_PRICE), 0);
+
+        invoiceList.add(invoice3);
+        targetDate = targetDate.plusMonths(6);
+        final Invoice invoice4 = generator.generateInvoice(accountId, events, invoiceList, targetDate, Currency.USD);
+        assertNotNull(invoice4);
+        assertEquals(invoice4.getNumberOfItems(), 7);
+    }
+
+    @Test(groups = {"fast"}, expectedExceptions = {InvoiceApiException.class})
+    public void testTargetDateRestrictionFailure() throws InvoiceApiException, CatalogApiException {
+        final DateTime targetDate = DateTime.now().plusMonths(60);
+        final BillingEventSet events = new MockBillingEventSet();
+        final Plan plan1 = new MockPlan();
+        final PlanPhase phase1 = createMockMonthlyPlanPhase(null, ZERO, PhaseType.TRIAL);
+        events.add(createBillingEvent(UUID.randomUUID(), DateTime.now(), plan1, phase1, 1));
+        generator.generateInvoice(UUID.randomUUID(), events, null, targetDate, Currency.USD);
+    }
+
+    private MockPlanPhase createMockMonthlyPlanPhase() {
+        return new MockPlanPhase(null, null, BillingPeriod.MONTHLY);
+    }
+
+    private MockPlanPhase createMockMonthlyPlanPhase(@Nullable final BigDecimal recurringRate) {
+        return new MockPlanPhase(new MockInternationalPrice(new DefaultPrice(recurringRate, Currency.USD)),
+                                 null, BillingPeriod.MONTHLY);
+    }
+
+    private MockPlanPhase createMockMonthlyPlanPhase(final BigDecimal recurringRate, final PhaseType phaseType) {
+        return new MockPlanPhase(new MockInternationalPrice(new DefaultPrice(recurringRate, Currency.USD)),
+                                 null, BillingPeriod.MONTHLY, phaseType);
+    }
+
+    private MockPlanPhase createMockMonthlyPlanPhase(@Nullable final BigDecimal recurringRate,
+                                                     @Nullable final BigDecimal fixedCost,
+                                                     final PhaseType phaseType) {
+        final MockInternationalPrice recurringPrice = (recurringRate == null) ? null : new MockInternationalPrice(new DefaultPrice(recurringRate, Currency.USD));
+        final MockInternationalPrice fixedPrice = (fixedCost == null) ? null : new MockInternationalPrice(new DefaultPrice(fixedCost, Currency.USD));
+
+        return new MockPlanPhase(recurringPrice, fixedPrice, BillingPeriod.MONTHLY, phaseType);
+    }
+
+    private MockPlanPhase createMockAnnualPlanPhase(final BigDecimal recurringRate, final PhaseType phaseType) {
+        return new MockPlanPhase(new MockInternationalPrice(new DefaultPrice(recurringRate, Currency.USD)),
+                                 null, BillingPeriod.ANNUAL, phaseType);
+    }
+
+    private BillingEvent createBillingEvent(final UUID subscriptionId, final DateTime startDate,
+                                            final Plan plan, final PlanPhase planPhase, final int billCycleDay) throws CatalogApiException {
+        final Subscription sub = createZombieSubscription(subscriptionId);
+        final Currency currency = Currency.USD;
+
+        return createMockBillingEvent(null, sub, startDate, plan, planPhase,
+                                      planPhase.getFixedPrice() == null ? null : planPhase.getFixedPrice().getPrice(currency),
+                                      planPhase.getRecurringPrice() == null ? null : planPhase.getRecurringPrice().getPrice(currency),
+                                      currency, planPhase.getBillingPeriod(),
+                                      billCycleDay, BillingModeType.IN_ADVANCE, "Test", 1L, SubscriptionTransitionType.CREATE);
+    }
+
+    private void testInvoiceGeneration(final UUID accountId, final BillingEventSet events, final List<Invoice> existingInvoices,
+                                       final DateTime targetDate, final int expectedNumberOfItems,
+                                       final BigDecimal expectedAmount) throws InvoiceApiException {
+        final Currency currency = Currency.USD;
+        final Invoice invoice = generator.generateInvoice(accountId, events, existingInvoices, targetDate, currency);
+        assertNotNull(invoice);
+        assertEquals(invoice.getNumberOfItems(), expectedNumberOfItems);
+        existingInvoices.add(invoice);
+
+        distributeItems(existingInvoices);
+        assertEquals(invoice.getBalance(), expectedAmount);
     }
 
     @Test(groups = {"fast"})
-    public void testConsumeTooMuchExistingCredit() {
-        testConsumeCreditInternal(new BigDecimal("7.00"), new BigDecimal("-7.00"));
+    public void testAddOnInvoiceGeneration() throws CatalogApiException, InvoiceApiException {
+        final DateTime april25 = new DateTime(2012, 4, 25, 0, 0, 0, 0);
+
+        // create a base plan on April 25th
+        final UUID accountId = UUID.randomUUID();
+        final Subscription baseSubscription = createZombieSubscription();
+
+        final Plan basePlan = new MockPlan("base Plan");
+        final MockInternationalPrice price5 = new MockInternationalPrice(new DefaultPrice(FIVE, Currency.USD));
+        final MockInternationalPrice price10 = new MockInternationalPrice(new DefaultPrice(TEN, Currency.USD));
+        final MockInternationalPrice price20 = new MockInternationalPrice(new DefaultPrice(TWENTY, Currency.USD));
+        final PlanPhase basePlanEvergreen = new MockPlanPhase(price10, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
+
+        final BillingEventSet events = new MockBillingEventSet();
+        events.add(createBillingEvent(baseSubscription.getId(), april25, basePlan, basePlanEvergreen, 25));
+
+        // generate invoice
+        final Invoice invoice1 = generator.generateInvoice(accountId, events, null, april25, Currency.USD);
+        assertNotNull(invoice1);
+        assertEquals(invoice1.getNumberOfItems(), 1);
+        assertEquals(invoice1.getBalance().compareTo(TEN), 0);
+
+        final List<Invoice> invoices = new ArrayList<Invoice>();
+        invoices.add(invoice1);
+
+        // create 2 add ons on April 28th
+        final DateTime april28 = new DateTime(2012, 4, 28, 0, 0, 0, 0);
+        final Subscription addOnSubscription1 = createZombieSubscription();
+        final Plan addOn1Plan = new MockPlan("add on 1");
+        final PlanPhase addOn1PlanPhaseEvergreen = new MockPlanPhase(price5, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
+        events.add(createBillingEvent(addOnSubscription1.getId(), april28, addOn1Plan, addOn1PlanPhaseEvergreen, 25));
+
+        final Subscription addOnSubscription2 = createZombieSubscription();
+        final Plan addOn2Plan = new MockPlan("add on 2");
+        final PlanPhase addOn2PlanPhaseEvergreen = new MockPlanPhase(price20, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
+        events.add(createBillingEvent(addOnSubscription2.getId(), april28, addOn2Plan, addOn2PlanPhaseEvergreen, 25));
+
+        // generate invoice
+        final Invoice invoice2 = generator.generateInvoice(accountId, events, invoices, april28, Currency.USD);
+        invoices.add(invoice2);
+        assertNotNull(invoice2);
+        assertEquals(invoice2.getNumberOfItems(), 2);
+        assertEquals(invoice2.getBalance().compareTo(TWENTY_FIVE.multiply(new BigDecimal("0.9")).setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD)), 0);
+
+        // perform a repair (change base plan; remove one add-on)
+        // event stream should include just two plans
+        final MockBillingEventSet newEvents = new MockBillingEventSet();
+        final Plan basePlan2 = new MockPlan("base plan 2");
+        final MockInternationalPrice price13 = new MockInternationalPrice(new DefaultPrice(THIRTEEN, Currency.USD));
+        final PlanPhase basePlan2Phase = new MockPlanPhase(price13, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
+        newEvents.add(createBillingEvent(baseSubscription.getId(), april25, basePlan2, basePlan2Phase, 25));
+        newEvents.add(createBillingEvent(addOnSubscription1.getId(), april28, addOn1Plan, addOn1PlanPhaseEvergreen, 25));
+
+        // generate invoice
+        final DateTime may1 = new DateTime(2012, 5, 1, 0, 0, 0, 0);
+        final Invoice invoice3 = generator.generateInvoice(accountId, newEvents, invoices, may1, Currency.USD);
+        assertNotNull(invoice3);
+        assertEquals(invoice3.getNumberOfItems(), 5);
+        // -4.50 -18 - 10 (to correct the previous 2 invoices) + 4.50 + 13
+        assertEquals(invoice3.getBalance().compareTo(FIFTEEN.negate()), 0);
+    }
+
+    @Test
+    public void testRepairForPaidInvoice() throws CatalogApiException, InvoiceApiException {
+        // create an invoice
+        final DateTime april25 = new DateTime(2012, 4, 25, 0, 0, 0, 0);
+
+        // create a base plan on April 25th
+        final UUID accountId = UUID.randomUUID();
+        final Subscription originalSubscription = createZombieSubscription();
+
+        final Plan originalPlan = new MockPlan("original plan");
+        final MockInternationalPrice price10 = new MockInternationalPrice(new DefaultPrice(TEN, Currency.USD));
+        final PlanPhase originalPlanEvergreen = new MockPlanPhase(price10, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
+
+        final BillingEventSet events = new MockBillingEventSet();
+        events.add(createBillingEvent(originalSubscription.getId(), april25, originalPlan, originalPlanEvergreen, 25));
+
+        final Invoice invoice1 = generator.generateInvoice(accountId, events, null, april25, Currency.USD);
+
+        printDetailInvoice(invoice1);
+
+        assertEquals(invoice1.getNumberOfItems(), 1);
+        final List<Invoice> invoices = new ArrayList<Invoice>();
+        invoices.add(invoice1);
+
+        // pay the invoice
+        invoice1.addPayment(new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, UUID.randomUUID(), invoice1.getId(), april25, TEN, Currency.USD));
+        assertEquals(invoice1.getBalance().compareTo(ZERO), 0);
+
+        // change the plan (i.e. repair) on start date
+        events.clear();
+        final Subscription newSubscription = createZombieSubscription();
+        final Plan newPlan = new MockPlan("new plan");
+        final MockInternationalPrice price5 = new MockInternationalPrice(new DefaultPrice(FIVE, Currency.USD));
+        final PlanPhase newPlanEvergreen = new MockPlanPhase(price5, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
+        events.add(createBillingEvent(newSubscription.getId(), april25, newPlan, newPlanEvergreen, 25));
+
+        // generate a new invoice
+        final Invoice invoice2 = generator.generateInvoice(accountId, events, invoices, april25, Currency.USD);
+
+        printDetailInvoice(invoice2);
+        assertEquals(invoice2.getNumberOfItems(), 4);
+        invoices.add(invoice2);
+
+        // move items to the correct invoice (normally, the dao calls will sort that out)
+        distributeItems(invoices);
+
+        // ensure that the original invoice balance is zero
+        assertEquals(invoice1.getBalance().compareTo(ZERO), 0);
+
+        // ensure that the account balance is correct
+        assertEquals(invoice2.getBalance().compareTo(ZERO), 0);
+
+        // ensure that the account has a credit balance
+        final BigDecimal creditBalance = invoice1.getCBAAmount().add(invoice2.getCBAAmount());
+        assertTrue(creditBalance.compareTo(FIVE) == 0);
     }
 
-    private void testConsumeCreditInternal(BigDecimal newRate, BigDecimal expectedNewCba) {
-        DateTime startDate = clock.getUTCNow();
-        DateTime endDate = startDate.plusDays(30);
-        DateTime nextEndDate = startDate.plusMonths(1);
+    private void distributeItems(final List<Invoice> invoices) {
+        final Map<UUID, Invoice> invoiceMap = new HashMap<UUID, Invoice>();
 
+        for (final Invoice invoice : invoices) {
+            invoiceMap.put(invoice.getId(), invoice);
+        }
 
-        BigDecimal rate1 = new BigDecimal("20.00");
-        BigDecimal amount1 = rate1;
+        for (final Invoice invoice : invoices) {
+            final Iterator<InvoiceItem> itemIterator = invoice.getInvoiceItems().iterator();
+            final UUID invoiceId = invoice.getId();
+
+            while (itemIterator.hasNext()) {
+                final InvoiceItem item = itemIterator.next();
+
+                if (!item.getInvoiceId().equals(invoiceId)) {
+                    final Invoice thisInvoice = invoiceMap.get(item.getInvoiceId());
+                    if (thisInvoice == null) {
+                        throw new NullPointerException();
+                    }
+                    thisInvoice.addInvoiceItem(item);
+                    itemIterator.remove();
+                }
+            }
+        }
+    }
 
-        BigDecimal rate2 = new BigDecimal("10.00");
-        BigDecimal amount2 = rate2;
+    @Test(groups = {"fast"})
+    public void testAutoInvoiceOffAccount() throws Exception {
+        final MockBillingEventSet events = new MockBillingEventSet();
+        events.setAccountInvoiceOff(true);
 
+        final Subscription sub = createZombieSubscription();
+        final DateTime startDate = buildDateTime(2011, 9, 1);
 
-        UUID firstInvoiceId  = UUID.randomUUID();
-        List<InvoiceItem> existing =  new LinkedList<InvoiceItem>();
-        BigDecimal pcba1 = new BigDecimal("10.00");
+        final Plan plan = new MockPlan();
+        final BigDecimal rate1 = TEN;
+        final PlanPhase phase = createMockMonthlyPlanPhase(rate1);
 
-        InvoiceItem item1 = new RecurringInvoiceItem(firstInvoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount1, rate1, currency, null);
-        InvoiceItem reversedItem1 = new RepairAdjInvoiceItem(firstInvoiceId, accountId, startDate, nextEndDate, amount1.negate(), currency, item1.getId());
-        InvoiceItem newItem1 = new RecurringInvoiceItem(firstInvoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount2, rate2, currency, null);
-        InvoiceItem cba1 = new CreditBalanceAdjInvoiceItem(firstInvoiceId, accountId, startDate, pcba1, currency);
-        existing.add(item1);
-        existing.add(reversedItem1);
-        existing.add(newItem1);
-        existing.add(cba1);
+        final BillingEvent event = createBillingEvent(sub.getId(), startDate, plan, phase, 1);
+        events.add(event);
 
+        final DateTime targetDate = buildDateTime(2011, 10, 3);
+        final UUID accountId = UUID.randomUUID();
+        final Invoice invoice = generator.generateInvoice(accountId, events, null, targetDate, Currency.USD);
 
-        BigDecimal newRate2 = newRate;
-        BigDecimal newAmount2 = newRate2;
+        assertNull(invoice);
+    }
 
-        List<InvoiceItem> proposed =  new LinkedList<InvoiceItem>();
-        InvoiceItem item2 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate.plusMonths(1), endDate.plusMonths(1), newAmount2, newRate2, currency, null);
-        proposed.add(item2);
+    public void testAutoInvoiceOffWithCredits() throws CatalogApiException, InvoiceApiException {
+        final Currency currency = Currency.USD;
+        final List<Invoice> invoices = new ArrayList<Invoice>();
+        final MockBillingEventSet eventSet = new MockBillingEventSet();
+        final UUID accountId = UUID.randomUUID();
+
+        final DateTime startDate = new DateTime(2012, 1, 1, 0, 12, 34, 0);
+
+        // add first subscription creation event
+        final UUID subscriptionId1 = UUID.randomUUID();
+        final Plan plan1 = new MockPlan();
+        final PlanPhase plan1phase1 = createMockMonthlyPlanPhase(FIFTEEN, null, PhaseType.DISCOUNT);
+        final BillingEvent subscription1creation = createBillingEvent(subscriptionId1, startDate, plan1, plan1phase1, 1);
+        eventSet.add(subscription1creation);
+
+        // add second subscription creation event
+        final UUID subscriptionId2 = UUID.randomUUID();
+        final Plan plan2 = new MockPlan();
+        final PlanPhase plan2phase1 = createMockMonthlyPlanPhase(TWELVE, null, PhaseType.EVERGREEN);
+        eventSet.add(createBillingEvent(subscriptionId2, startDate, plan2, plan2phase1, 1));
+
+        // generate the first invoice
+        final Invoice invoice1 = generator.generateInvoice(accountId, eventSet, invoices, startDate, currency);
+        assertNotNull(invoice1);
+        assertTrue(invoice1.getBalance().compareTo(FIFTEEN.add(TWELVE)) == 0);
+        invoices.add(invoice1);
+
+        // set auto invoice off for first subscription (i.e. remove event from BillingEventSet and add subscription id to the list
+        // generate invoice
+        eventSet.remove(subscription1creation);
+        eventSet.addSubscriptionWithAutoInvoiceOff(subscriptionId1);
+
+        final DateTime targetDate2 = startDate.plusMonths(1);
+        final Invoice invoice2 = generator.generateInvoice(accountId, eventSet, invoices, targetDate2, currency);
+        assertNotNull(invoice2);
+        assertTrue(invoice2.getBalance().compareTo(TWELVE) == 0);
+        invoices.add(invoice2);
+
+        final DateTime targetDate3 = targetDate2.plusMonths(1);
+        eventSet.clearSubscriptionsWithAutoInvoiceOff();
+        eventSet.add(subscription1creation);
+        final Invoice invoice3 = generator.generateInvoice(accountId, eventSet, invoices, targetDate3, currency);
+        assertNotNull(invoice3);
+        assertTrue(invoice3.getBalance().compareTo(FIFTEEN.multiply(TWO).add(TWELVE)) == 0);
+    }
 
-        gen.consumeExistingCredit(invoiceId, firstInvoiceId, existing, proposed, currency);
-        assertEquals(proposed.size(), 2);
-        InvoiceItem item2Check = proposed.get(0);
-        assertEquals(item2Check.getInvoiceId(), invoiceId);
-        assertEquals(item2Check.getInvoiceItemType(), InvoiceItemType.RECURRING);
-        assertEquals(item2Check.getAmount(), newAmount2);
 
-        InvoiceItem cbaCheck = proposed.get(1);
-        assertEquals(cbaCheck.getInvoiceId(), invoiceId);
-        assertEquals(cbaCheck.getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
-        assertEquals(cbaCheck.getAmount(), expectedNewCba);
+    @Test(groups = {"fast"})
+    public void testAccountCredit() throws CatalogApiException, InvoiceApiException {
+        final BillingEventSet billingEventSet = new MockBillingEventSet();
+
+        final DateTime startDate = new DateTime(2012, 3, 1, 0, 0, 0, 0);
+        final UUID accountId = UUID.randomUUID();
+        final UUID subscriptionId = UUID.randomUUID();
+        final Plan plan = new MockPlan("original plan");
+        final MockInternationalPrice price10 = new MockInternationalPrice(new DefaultPrice(TEN, Currency.USD));
+        final PlanPhase planPhase = new MockPlanPhase(price10, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
+        final BillingEvent creation = createBillingEvent(subscriptionId, startDate, plan, planPhase, 1);
+        billingEventSet.add(creation);
+
+        final List<Invoice> invoices = new ArrayList<Invoice>();
+
+        final Invoice initialInvoice = generator.generateInvoice(accountId, billingEventSet, null, startDate, Currency.USD);
+        assertNotNull(initialInvoice);
+        assertEquals(initialInvoice.getNumberOfItems(), 1);
+        assertEquals(initialInvoice.getBalance().compareTo(TEN), 0);
+        invoices.add(initialInvoice);
+
+        printDetailInvoice(initialInvoice);
+
+        // add account-level credit
+        final DateTime creditDate = startDate.plusDays(5);
+        final Invoice invoiceWithCredit = new DefaultInvoice(accountId, creditDate, creditDate, Currency.USD);
+        final InvoiceItem accountCredit = new CreditBalanceAdjInvoiceItem(invoiceWithCredit.getId(), accountId, creditDate, FIVE, Currency.USD);
+        invoiceWithCredit.addInvoiceItem(accountCredit);
+        invoices.add(invoiceWithCredit);
+
+        printDetailInvoice(invoiceWithCredit);
+
+        // invoice one month after the initial subscription
+        final Invoice finalInvoice = generator.generateInvoice(accountId, billingEventSet, invoices, startDate.plusMonths(1), Currency.USD);
+
+        printDetailInvoice(finalInvoice);
+
+        System.out.println("BALANCE = " + finalInvoice.getBalance());
+        assertEquals(finalInvoice.getBalance().compareTo(FIVE), 0);
+        System.out.println("CBA = " + finalInvoice.getCBAAmount());
+        assertEquals(finalInvoice.getCBAAmount().compareTo(FIVE.negate()), 0);
+        assertEquals(finalInvoice.getNumberOfItems(), 2);
+    }
+
+    private void printDetailInvoice(final Invoice  invoice) {
+        log.info("--------------------  START DETAIL ----------------------");
+        log.info("Invoice " + invoice.getId() + ": BALANCE = " + invoice.getBalance()
+                + ", CBA = " + invoice.getCBAAmount()
+                + ", CHARGE_AMOUNT = " + invoice.getChargedAmount()
+                + ", ADJ_AMOUNT = " + invoice.getCreditAdjAmount());
+
+        for (InvoiceItem cur : invoice.getInvoiceItems()) {
+            log.info(cur.toString());
+        }
+        log.info("--------------------  END DETAIL ----------------------");
     }
+
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGeneratorUnit.java b/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGeneratorUnit.java
new file mode 100644
index 0000000..53066ea
--- /dev/null
+++ b/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGeneratorUnit.java
@@ -0,0 +1,336 @@
+/*
+ * 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.generator;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+
+import java.math.BigDecimal;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.config.InvoiceConfig;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
+import com.ning.billing.invoice.model.CreditBalanceAdjInvoiceItem;
+import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
+import com.ning.billing.invoice.model.RecurringInvoiceItem;
+import com.ning.billing.invoice.model.RepairAdjInvoiceItem;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.ClockMock;
+
+public class TestDefaultInvoiceGeneratorUnit  {
+
+    private DefaultInvoiceGenerator gen;
+    private Clock clock;
+
+    private final UUID invoiceId = UUID.randomUUID();
+    private final UUID accountId = UUID.randomUUID();
+    private final UUID subscriptionId = UUID.randomUUID();
+    private final UUID bundleId = UUID.randomUUID();
+    private final String planName = "my-plan";
+    private final String phaseName = "my-phase";
+    private final Currency currency = Currency.USD;
+
+    public static final class TestDefaultInvoiceGeneratorMock extends DefaultInvoiceGenerator {
+        public TestDefaultInvoiceGeneratorMock(final Clock clock, final InvoiceConfig config) {
+            super(clock, config);
+        }
+    }
+
+    @BeforeClass(groups = {"fast"})
+    public void setup() {
+        clock = new ClockMock();
+        gen = new TestDefaultInvoiceGeneratorMock(clock, new InvoiceConfig() {
+            @Override
+            public boolean isNotificationProcessingOff() {
+                return false;
+            }
+            @Override
+            public boolean isEmailNotificationsEnabled() {
+                return false;
+            }
+            @Override
+            public long getSleepTimeMs() {
+                return 100;
+            }
+            @Override
+            public int getNumberOfMonthsInFuture() {
+                return 5;
+            }
+        });
+    }
+
+    @Test(groups = {"fast"}, enabled= true)
+    public void testRemoveCancellingInvoiceItemsFixedPrice() {
+
+        DateTime startDate = clock.getUTCNow();
+        DateTime endDate = startDate.plusDays(30);
+        DateTime nextEndDate = startDate.plusMonths(1);
+
+        BigDecimal amount = new BigDecimal("12.00");
+        BigDecimal rate2 = new BigDecimal("14.85");
+        BigDecimal amount2 = rate2;
+        List<InvoiceItem> items =  new LinkedList<InvoiceItem>();
+        InvoiceItem item1 = new FixedPriceInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+        items.add(item1);
+        items.add(new RepairAdjInvoiceItem(invoiceId, accountId, startDate, endDate, amount.negate(), currency, item1.getId()));
+        items.add(new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, endDate, nextEndDate, amount2, rate2, currency));
+        gen.removeCancellingInvoiceItems(items);
+        assertEquals(items.size(), 1);
+        InvoiceItem leftItem = items.get(0);
+        assertEquals(leftItem.getInvoiceItemType(), InvoiceItemType.RECURRING);
+        assertEquals(leftItem.getAmount(), amount2);
+    }
+
+    @Test(groups = {"fast"})
+    public void testRemoveCancellingInvoiceItemsRecurringPrice() {
+
+        DateTime startDate = clock.getUTCNow();
+        DateTime endDate = startDate.plusDays(30);
+        DateTime nextEndDate = startDate.plusMonths(1);
+
+        BigDecimal rate1 = new BigDecimal("12.00");
+        BigDecimal amount1 = rate1;
+        BigDecimal rate2 = new BigDecimal("14.85");
+        BigDecimal amount2 = rate2;
+        List<InvoiceItem> items =  new LinkedList<InvoiceItem>();
+        InvoiceItem item1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount1, rate1, currency, null);
+        items.add(item1);
+        items.add(new RepairAdjInvoiceItem(invoiceId, accountId, startDate, endDate, amount1.negate(), currency, item1.getId()));
+        items.add(new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, endDate, nextEndDate, amount2, rate2, currency));
+        gen.removeCancellingInvoiceItems(items);
+        assertEquals(items.size(), 1);
+        InvoiceItem leftItem = items.get(0);
+        assertEquals(leftItem.getInvoiceItemType(), InvoiceItemType.RECURRING);
+        assertEquals(leftItem.getAmount(), amount2);
+    }
+
+    @Test(groups = {"fast"})
+    public void testRemoveDuplicatedInvoiceItemsFixedPrice() {
+
+        DateTime startDate = clock.getUTCNow();
+        DateTime endDate = startDate.plusDays(30);
+        DateTime nextEndDate = startDate.plusMonths(1);
+
+        BigDecimal amount1 = new BigDecimal("12.00");
+
+        BigDecimal amount2 = new BigDecimal("14.85");
+
+        BigDecimal rate3 = new BigDecimal("14.85");
+        BigDecimal amount3 = rate3;
+
+        List<InvoiceItem> existing =  new LinkedList<InvoiceItem>();
+        InvoiceItem item1 = new FixedPriceInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount1, currency);
+        existing.add(item1);
+
+        List<InvoiceItem> proposed =  new LinkedList<InvoiceItem>();
+        InvoiceItem other = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, endDate, nextEndDate, amount3, rate3, currency, null);
+        proposed.add(item1);
+        proposed.add(other);
+
+        gen.removeDuplicatedInvoiceItems(proposed, existing);
+        assertEquals(existing.size(), 0);
+        assertEquals(proposed.size(), 1);
+        InvoiceItem leftItem = proposed.get(0);
+        assertEquals(leftItem.getInvoiceItemType(), InvoiceItemType.RECURRING);
+        assertEquals(leftItem.getAmount(), amount2);
+    }
+
+    @Test(groups = {"fast"})
+    public void testRemoveDuplicatedInvoiceItemsRecuringPrice() {
+
+        DateTime startDate = clock.getUTCNow();
+        DateTime endDate = startDate.plusDays(30);
+        DateTime nextEndDate = startDate.plusMonths(1);
+
+        BigDecimal rate1 = new BigDecimal("12.00");
+        BigDecimal amount1 = rate1;
+
+        BigDecimal rate2 = new BigDecimal("14.85");
+        BigDecimal amount2 = rate2;
+
+        List<InvoiceItem> existing =  new LinkedList<InvoiceItem>();
+        InvoiceItem item1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount1, rate1, currency, null);
+        existing.add(item1);
+
+        List<InvoiceItem> proposed =  new LinkedList<InvoiceItem>();
+        InvoiceItem other = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, endDate, nextEndDate, amount2, rate2, currency, null);
+        proposed.add(item1);
+        proposed.add(other);
+
+        gen.removeDuplicatedInvoiceItems(proposed, existing);
+        assertEquals(existing.size(), 0);
+        assertEquals(proposed.size(), 1);
+        InvoiceItem leftItem = proposed.get(0);
+        assertEquals(leftItem.getInvoiceItemType(), InvoiceItemType.RECURRING);
+        assertEquals(leftItem.getAmount(), amount2);
+    }
+
+    // STEPH same as testRemoveCancellingInvoiceItemsFixedPrice: should we have one for FixedPrice?
+    @Test(groups = {"fast"})
+    public void testAddRepairedItemsItemsRecurringPrice() {
+        DateTime startDate = clock.getUTCNow();
+        DateTime endDate = startDate.plusDays(30);
+        DateTime nextEndDate = startDate.plusMonths(1);
+
+        BigDecimal rate1 = new BigDecimal("12.00");
+        BigDecimal amount1 = rate1;
+
+        BigDecimal rate2 = new BigDecimal("14.85");
+        BigDecimal amount2 = rate2;
+
+        UUID firstInvoiceId = UUID.randomUUID();
+        List<InvoiceItem> existing =  new LinkedList<InvoiceItem>();
+        InvoiceItem item1 = new RecurringInvoiceItem(firstInvoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount1, rate1, currency, null);
+        existing.add(item1);
+
+        List<InvoiceItem> proposed =  new LinkedList<InvoiceItem>();
+        InvoiceItem other = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, endDate, nextEndDate, amount2, rate2, currency, null);
+        proposed.add(other);
+
+        gen.addRepairedItems(existing, proposed);
+        assertEquals(existing.size(), 1);
+        assertEquals(proposed.size(), 2);
+        InvoiceItem leftItem1 = proposed.get(0);
+        assertEquals(leftItem1.getInvoiceId(), invoiceId);
+        assertEquals(leftItem1.getInvoiceItemType(), InvoiceItemType.RECURRING);
+        assertEquals(leftItem1.getAmount(), amount2);
+
+        InvoiceItem newItem2 = proposed.get(1);
+        assertEquals(newItem2.getInvoiceId(), firstInvoiceId);
+        assertEquals(newItem2.getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
+        assertEquals(newItem2.getAmount(), item1.getAmount().negate());
+        assertEquals(newItem2.getLinkedItemId(), item1.getId());
+
+    }
+
+    @Test(groups = {"fast"})
+    public void testGenerateCreditsForPastRepairedInvoices() {
+
+        DateTime startDate = clock.getUTCNow();
+        DateTime endDate = startDate.plusDays(30);
+        DateTime nextEndDate = startDate.plusMonths(1);
+
+        BigDecimal rate1 = new BigDecimal("10.00");
+        BigDecimal amount1 = rate1;
+
+        List<InvoiceItem> existing =  new LinkedList<InvoiceItem>();
+        InvoiceItem item1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount1, rate1, currency, null);
+        existing.add(item1);
+
+
+        UUID existingInvoiceId = UUID.randomUUID();
+        List<Invoice> existingInvoices = new LinkedList<Invoice>();
+        Invoice existingInvoice = mock(Invoice.class);
+        when(existingInvoice.getId()).thenReturn(existingInvoiceId);
+        when(existingInvoice.getBalance()).thenReturn(BigDecimal.ZERO);
+        when(existingInvoice.getInvoiceItems()).thenReturn(existing);
+
+        BigDecimal rate2 = new BigDecimal("20.0");
+        BigDecimal amount2 = rate2;
+
+        List<InvoiceItem> proposed =  new LinkedList<InvoiceItem>();
+        InvoiceItem reversedItem1 = new RepairAdjInvoiceItem(existingInvoiceId, accountId, startDate, nextEndDate, item1.getAmount().negate(), currency, item1.getId());
+        InvoiceItem newItem1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount2, rate2, currency, null);
+        proposed.add(reversedItem1);
+        proposed.add(newItem1);
+
+        gen.generateCBAForExistingInvoices(accountId, existingInvoices, proposed, currency);
+
+        assertEquals(proposed.size(), 3);
+        InvoiceItem reversedItemCheck1 = proposed.get(0);
+        assertEquals(reversedItemCheck1.getInvoiceId(), existingInvoiceId);
+        assertEquals(reversedItemCheck1.getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
+        assertEquals(reversedItemCheck1.getAmount(), item1.getAmount().negate());
+        assertEquals(reversedItemCheck1.getLinkedItemId(), item1.getId());
+
+        InvoiceItem newItemCheck1 = proposed.get(1);
+        assertEquals(newItemCheck1.getInvoiceId(), invoiceId);
+        assertEquals(newItemCheck1.getInvoiceItemType(), InvoiceItemType.RECURRING);
+        assertEquals(newItemCheck1.getAmount(), amount2);
+
+        InvoiceItem creditItemCheck = proposed.get(2);
+        assertEquals(creditItemCheck.getInvoiceId(), existingInvoiceId);
+        assertEquals(creditItemCheck.getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
+        assertEquals(creditItemCheck.getAmount(), amount2.add(rate1.negate()));
+    }
+
+    @Test(groups = {"fast"})
+    public void testConsumeNotEnoughExistingCredit() {
+        testConsumeCreditInternal(new BigDecimal("12.00"), new BigDecimal("-10.00"));
+    }
+
+    @Test(groups = {"fast"})
+    public void testConsumeTooMuchExistingCredit() {
+        testConsumeCreditInternal(new BigDecimal("7.00"), new BigDecimal("-7.00"));
+    }
+
+    private void testConsumeCreditInternal(BigDecimal newRate, BigDecimal expectedNewCba) {
+        DateTime startDate = clock.getUTCNow();
+        DateTime endDate = startDate.plusDays(30);
+        DateTime nextEndDate = startDate.plusMonths(1);
+
+
+        BigDecimal rate1 = new BigDecimal("20.00");
+        BigDecimal amount1 = rate1;
+
+        BigDecimal rate2 = new BigDecimal("10.00");
+        BigDecimal amount2 = rate2;
+
+
+        UUID firstInvoiceId  = UUID.randomUUID();
+        List<InvoiceItem> existing =  new LinkedList<InvoiceItem>();
+        BigDecimal pcba1 = new BigDecimal("10.00");
+
+        InvoiceItem item1 = new RecurringInvoiceItem(firstInvoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount1, rate1, currency, null);
+        InvoiceItem reversedItem1 = new RepairAdjInvoiceItem(firstInvoiceId, accountId, startDate, nextEndDate, amount1.negate(), currency, item1.getId());
+        InvoiceItem newItem1 = new RecurringInvoiceItem(firstInvoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount2, rate2, currency, null);
+        InvoiceItem cba1 = new CreditBalanceAdjInvoiceItem(firstInvoiceId, accountId, startDate, pcba1, currency);
+        existing.add(item1);
+        existing.add(reversedItem1);
+        existing.add(newItem1);
+        existing.add(cba1);
+
+
+        BigDecimal newRate2 = newRate;
+        BigDecimal newAmount2 = newRate2;
+
+        List<InvoiceItem> proposed =  new LinkedList<InvoiceItem>();
+        InvoiceItem item2 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate.plusMonths(1), endDate.plusMonths(1), newAmount2, newRate2, currency, null);
+        proposed.add(item2);
+
+        gen.consumeExistingCredit(invoiceId, firstInvoiceId, existing, proposed, currency);
+        assertEquals(proposed.size(), 2);
+        InvoiceItem item2Check = proposed.get(0);
+        assertEquals(item2Check.getInvoiceId(), invoiceId);
+        assertEquals(item2Check.getInvoiceItemType(), InvoiceItemType.RECURRING);
+        assertEquals(item2Check.getAmount(), newAmount2);
+
+        InvoiceItem cbaCheck = proposed.get(1);
+        assertEquals(cbaCheck.getInvoiceId(), invoiceId);
+        assertEquals(cbaCheck.getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
+        assertEquals(cbaCheck.getAmount(), expectedNewCba);
+    }
+}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
index df94e19..b586cd7 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
@@ -64,7 +64,6 @@ import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.globallocker.GlobalLocker;
 import com.ning.billing.util.io.IOUtils;
 
-@Test(groups = "slow")
 @Guice(modules = {MockModule.class})
 public class TestInvoiceDispatcher extends InvoicingTestBase {
     private final Logger log = LoggerFactory.getLogger(TestInvoiceDispatcher.class);
@@ -95,7 +94,7 @@ public class TestInvoiceDispatcher extends InvoicingTestBase {
 
     private CallContext context;
 
-    @BeforeSuite(groups = "slow")
+    @BeforeSuite(groups = {"slow"})
     public void setup() throws Exception {
         final String invoiceDdl = IOUtils.toString(TestInvoiceDispatcher.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
         final String utilDdl = IOUtils.toString(TestInvoiceDispatcher.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
@@ -113,7 +112,7 @@ public class TestInvoiceDispatcher extends InvoicingTestBase {
         ((ZombieControl) billingApi).addResult("setChargedThroughDate", BrainDeadProxyFactory.ZOMBIE_VOID);
     }
 
-    @AfterClass(alwaysRun = true)
+    @AfterClass(groups = {"slow"})
     public void tearDown() {
         try {
             ((DefaultBusService) busService).stopBus();
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/GenericProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/GenericProRationTests.java
index 81d5347..4b37ad8 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/GenericProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/GenericProRationTests.java
@@ -23,7 +23,6 @@ import org.testng.annotations.Test;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.invoice.tests.inAdvance.GenericProRationTestBase;
 
-@Test(groups = {"fast", "invoicing", "proRation"})
 public class GenericProRationTests extends GenericProRationTestBase {
     @Override
     protected BillingPeriod getBillingPeriod() {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/GenericProRationTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/GenericProRationTestBase.java
index ea37848..ebf816e 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/GenericProRationTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/GenericProRationTestBase.java
@@ -23,7 +23,6 @@ import org.testng.annotations.Test;
 
 import com.ning.billing.invoice.model.InvalidDateSequenceException;
 
-@Test(groups = {"fast", "invoicing", "proRation"})
 public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBase {
     /**
      * used for testing cancellation in less than a single billing period
@@ -32,14 +31,14 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
      */
     protected abstract BigDecimal getDaysInTestPeriod();
 
-    @Test
+    @Test(groups = {"fast"})
     public void testSinglePlan_OnStartDate() throws InvalidDateSequenceException {
         final DateTime startDate = buildDateTime(2011, 2, 15);
 
         testCalculateNumberOfBillingCycles(startDate, startDate, 15, ONE);
     }
 
-    @Test
+    @Test(groups = {"fast"})
     public void testSinglePlan_LessThanOnePeriod() throws InvalidDateSequenceException {
         final DateTime startDate = buildDateTime(2011, 2, 15);
         final DateTime targetDate = buildDateTime(2011, 3, 1);
@@ -47,7 +46,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
         testCalculateNumberOfBillingCycles(startDate, targetDate, 15, ONE);
     }
 
-    @Test
+    @Test(groups = {"fast"})
     public void testSinglePlan_OnePeriodLessADayAfterStart() throws InvalidDateSequenceException {
         final DateTime startDate = buildDateTime(2011, 2, 15);
         final DateTime targetDate = startDate.plusMonths(getBillingPeriod().getNumberOfMonths()).plusDays(-1);
@@ -55,7 +54,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
         testCalculateNumberOfBillingCycles(startDate, targetDate, 15, ONE);
     }
 
-    @Test
+    @Test(groups = {"fast"})
     public void testSinglePlan_ExactlyOnePeriodAfterStart() throws InvalidDateSequenceException {
         final DateTime startDate = buildDateTime(2011, 2, 15);
         final DateTime targetDate = startDate.plusMonths(getBillingPeriod().getNumberOfMonths());
@@ -63,7 +62,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
         testCalculateNumberOfBillingCycles(startDate, targetDate, 15, TWO);
     }
 
-    @Test
+    @Test(groups = {"fast"})
     public void testSinglePlan_SlightlyMoreThanOnePeriodAfterStart() throws InvalidDateSequenceException {
         final DateTime startDate = buildDateTime(2011, 2, 15);
         final DateTime targetDate = startDate.plusMonths(getBillingPeriod().getNumberOfMonths()).plusDays(1);
@@ -71,7 +70,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
         testCalculateNumberOfBillingCycles(startDate, targetDate, 15, TWO);
     }
 
-    @Test
+    @Test(groups = {"fast"})
     public void testSinglePlan_CrossingYearBoundary() throws InvalidDateSequenceException {
         final DateTime startDate = buildDateTime(2011, 12, 15);
         final DateTime oneCycleLater = startDate.plusMonths(getBillingPeriod().getNumberOfMonths());
@@ -86,7 +85,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
         testCalculateNumberOfBillingCycles(startDate, oneCycleLater.plusDays(1), 15, TWO);
     }
 
-    @Test
+    @Test(groups = {"fast"})
     public void testSinglePlan_StartingMidFebruary() throws InvalidDateSequenceException {
         final DateTime startDate = buildDateTime(2011, 2, 15);
         final DateTime targetDate = startDate.plusMonths(getBillingPeriod().getNumberOfMonths());
@@ -94,7 +93,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
         testCalculateNumberOfBillingCycles(startDate, targetDate, 15, TWO);
     }
 
-    @Test
+    @Test(groups = {"fast"})
     public void testSinglePlan_StartingMidFebruaryOfLeapYear() throws InvalidDateSequenceException {
         final DateTime startDate = buildDateTime(2012, 2, 15);
         final DateTime targetDate = startDate.plusMonths(getBillingPeriod().getNumberOfMonths());
@@ -102,7 +101,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
         testCalculateNumberOfBillingCycles(startDate, targetDate, 15, TWO);
     }
 
-    @Test
+    @Test(groups = {"fast"})
     public void testSinglePlan_MovingForwardThroughTime() throws InvalidDateSequenceException {
         final DateTime startDate = buildDateTime(2011, 1, 31);
         BigDecimal expectedValue = ONE;
@@ -123,7 +122,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
     }
 
     // tests for cancellation in less than one period, beginning Jan 1
-    @Test
+    @Test(groups = {"fast"})
     public void testCancelledBeforeOnePeriod_TargetDateInStartDate() throws InvalidDateSequenceException {
         final DateTime startDate = buildDateTime(2011, 1, 1);
         final DateTime targetDate = buildDateTime(2011, 1, 1);
@@ -133,7 +132,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
         testCalculateNumberOfBillingCycles(startDate, endDate, targetDate, 1, expectedValue);
     }
 
-    @Test
+    @Test(groups = {"fast"})
     public void testCancelledBeforeOnePeriod_TargetDateInSubscriptionPeriod() throws InvalidDateSequenceException {
         final DateTime startDate = buildDateTime(2011, 1, 1);
         final DateTime targetDate = buildDateTime(2011, 1, 7);
@@ -143,7 +142,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
         testCalculateNumberOfBillingCycles(startDate, endDate, targetDate, 1, expectedValue);
     }
 
-    @Test
+    @Test(groups = {"fast"})
     public void testCancelledBeforeOnePeriod_TargetDateOnEndDate() throws InvalidDateSequenceException {
         final DateTime startDate = buildDateTime(2011, 1, 1);
         final DateTime targetDate = buildDateTime(2011, 1, 15);
@@ -153,7 +152,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
         testCalculateNumberOfBillingCycles(startDate, endDate, targetDate, 1, expectedValue);
     }
 
-    @Test
+    @Test(groups = {"fast"})
     public void testCancelledBeforeOnePeriod_TargetDateAfterEndDateButInFirstBillingPeriod() throws InvalidDateSequenceException {
         final DateTime startDate = buildDateTime(2011, 1, 1);
         final DateTime targetDate = buildDateTime(2011, 1, 17);
@@ -163,7 +162,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
         testCalculateNumberOfBillingCycles(startDate, endDate, targetDate, 1, expectedValue);
     }
 
-    @Test
+    @Test(groups = {"fast"})
     public void testCancelledBeforeOnePeriod_TargetDateAtEndOfFirstBillingPeriod() throws InvalidDateSequenceException {
         final DateTime startDate = buildDateTime(2011, 1, 1);
         final DateTime targetDate = buildDateTime(2011, 2, 1);
@@ -173,7 +172,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
         testCalculateNumberOfBillingCycles(startDate, endDate, targetDate, 1, expectedValue);
     }
 
-    @Test
+    @Test(groups = {"fast"})
     public void testCancelledBeforeOnePeriod_TargetDateAfterFirstBillingPeriod() throws InvalidDateSequenceException {
         final DateTime startDate = buildDateTime(2011, 1, 1);
         final DateTime targetDate = buildDateTime(2011, 4, 5);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/GenericProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/GenericProRationTests.java
index b749ab8..87aeac6 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/GenericProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/GenericProRationTests.java
@@ -23,7 +23,6 @@ import org.testng.annotations.Test;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.invoice.tests.inAdvance.GenericProRationTestBase;
 
-@Test(groups = {"fast", "invoicing", "proRation"})
 public class GenericProRationTests extends GenericProRationTestBase {
     @Override
     protected BillingPeriod getBillingPeriod() {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/ProRationInAdvanceTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/ProRationInAdvanceTestBase.java
index b8f3848..dca4d61 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/ProRationInAdvanceTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/ProRationInAdvanceTestBase.java
@@ -22,7 +22,6 @@ import com.ning.billing.invoice.model.BillingMode;
 import com.ning.billing.invoice.model.InAdvanceBillingMode;
 import com.ning.billing.invoice.tests.ProRationTestBase;
 
-@Test(groups = {"fast", "invoicing", "proRation"})
 public abstract class ProRationInAdvanceTestBase extends ProRationTestBase {
     @Override
     protected BillingMode getBillingMode() {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/GenericProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/GenericProRationTests.java
index 3351807..72e83d1 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/GenericProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/GenericProRationTests.java
@@ -23,7 +23,6 @@ import org.testng.annotations.Test;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.invoice.tests.inAdvance.GenericProRationTestBase;
 
-@Test(groups = {"fast", "invoicing", "proRation"})
 public class GenericProRationTests extends GenericProRationTestBase {
     @Override
     protected BillingPeriod getBillingPeriod() {
diff --git a/invoice/src/test/resources/resource.properties b/invoice/src/test/resources/resource.properties
index 85d4e42..b271d93 100644
--- a/invoice/src/test/resources/resource.properties
+++ b/invoice/src/test/resources/resource.properties
@@ -1,2 +1 @@
 com.ning.billing.invoice.maxNumberOfMonthsInFuture = 36
-com.ning.billing.dbi.test.useLocalDb=true
diff --git a/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java b/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
index 4e5c71f..9d31f67 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -36,7 +36,7 @@ import com.ning.billing.util.bus.Bus.EventBusException;
 import com.ning.billing.util.bus.BusEvent;
 import com.ning.billing.util.globallocker.GlobalLock;
 import com.ning.billing.util.globallocker.GlobalLocker;
-import com.ning.billing.util.globallocker.GlobalLocker.LockerService;
+import com.ning.billing.util.globallocker.GlobalLocker.LockerType;
 import com.ning.billing.util.globallocker.LockFailedException;
 
 public abstract class ProcessorBase {
@@ -138,7 +138,7 @@ public abstract class ProcessorBase {
                 throws PaymentApiException {
             GlobalLock lock = null;
             try {
-                lock = locker.lockWithNumberOfTries(LockerService.PAYMENT, accountExternalKey, NB_LOCK_TRY);
+                lock = locker.lockWithNumberOfTries(LockerType.ACCOUNT, accountExternalKey, NB_LOCK_TRY);
                 return callback.doOperation();
             } catch (LockFailedException e) {
                 final String format = String.format("Failed to lock account %s", accountExternalKey);
diff --git a/util/src/main/java/com/ning/billing/util/globallocker/GlobalLocker.java b/util/src/main/java/com/ning/billing/util/globallocker/GlobalLocker.java
index daddf1a..7c59a47 100644
--- a/util/src/main/java/com/ning/billing/util/globallocker/GlobalLocker.java
+++ b/util/src/main/java/com/ning/billing/util/globallocker/GlobalLocker.java
@@ -18,24 +18,12 @@ package com.ning.billing.util.globallocker;
 
 public interface GlobalLocker {
 
-    GlobalLock lockWithNumberOfTries(final LockerService service, final String lockKey, final int retry);
+    GlobalLock lockWithNumberOfTries(final LockerType service, final String lockKey, final int retry);
 
-    Boolean isFree(final LockerService service, final String lockKey);
+    Boolean isFree(final LockerType service, final String lockKey);
 
-    public enum LockerService {
-
-        INVOICE("invoice"),
-        PAYMENT("payment");
-
-        private final String svcName;
-
-        LockerService(final String svcName) {
-            this.svcName = svcName;
-        }
-
-        @Override
-        public String toString() {
-            return svcName;
-        }
+    public enum LockerType {
+        // Global ACCOUNT locking
+        ACCOUNT
     }
 }
diff --git a/util/src/main/java/com/ning/billing/util/globallocker/MySqlGlobalLocker.java b/util/src/main/java/com/ning/billing/util/globallocker/MySqlGlobalLocker.java
index 95e61bc..248aeef 100644
--- a/util/src/main/java/com/ning/billing/util/globallocker/MySqlGlobalLocker.java
+++ b/util/src/main/java/com/ning/billing/util/globallocker/MySqlGlobalLocker.java
@@ -27,7 +27,7 @@ public class MySqlGlobalLocker implements GlobalLocker {
 
     private static final Logger logger = LoggerFactory.getLogger(MySqlGlobalLocker.class);
 
-    private static final long DEFAULT_TIMEOUT = 3L; // 3 seconds
+    private static final long DEFAULT_TIMEOUT = 5L; // 5 seconds
 
     private final IDBI dbi;
     private long timeout;
@@ -43,7 +43,7 @@ public class MySqlGlobalLocker implements GlobalLocker {
     }
 
     @Override
-    public GlobalLock lockWithNumberOfTries(final LockerService service, final String lockKey, final int retry) {
+    public GlobalLock lockWithNumberOfTries(final LockerType service, final String lockKey, final int retry) {
 
         final String lockName = getLockName(service, lockKey);
         int tries_left = retry;
@@ -82,7 +82,7 @@ public class MySqlGlobalLocker implements GlobalLocker {
     }
 
     @Override
-    public Boolean isFree(final LockerService service, final String lockKey) {
+    public Boolean isFree(final LockerType service, final String lockKey) {
 
         final String lockName = getLockName(service, lockKey);
         final Handle h = dbi.open();
@@ -96,7 +96,7 @@ public class MySqlGlobalLocker implements GlobalLocker {
         }
     }
 
-    private String getLockName(final LockerService service, final String lockKey) {
+    private String getLockName(final LockerType service, final String lockKey) {
         final StringBuilder tmp = new StringBuilder()
                 .append(service.toString())
                 .append("-")
diff --git a/util/src/test/java/com/ning/billing/util/globallocker/MockGlobalLocker.java b/util/src/test/java/com/ning/billing/util/globallocker/MockGlobalLocker.java
index 4a1ff01..1e46918 100644
--- a/util/src/test/java/com/ning/billing/util/globallocker/MockGlobalLocker.java
+++ b/util/src/test/java/com/ning/billing/util/globallocker/MockGlobalLocker.java
@@ -19,7 +19,7 @@ package com.ning.billing.util.globallocker;
 public class MockGlobalLocker implements GlobalLocker {
 
     @Override
-    public GlobalLock lockWithNumberOfTries(final LockerService service,
+    public GlobalLock lockWithNumberOfTries(final LockerType service,
                                             final String lockKey, final int retry) {
         return new GlobalLock() {
             @Override
@@ -29,7 +29,7 @@ public class MockGlobalLocker implements GlobalLocker {
     }
 
     @Override
-    public Boolean isFree(final LockerService service, final String lockKey) {
+    public Boolean isFree(final LockerType service, final String lockKey) {
         return Boolean.TRUE;
     }
 }
diff --git a/util/src/test/java/com/ning/billing/util/globallocker/TestMysqlGlobalLocker.java b/util/src/test/java/com/ning/billing/util/globallocker/TestMysqlGlobalLocker.java
index 4619b42..f2fd01a 100644
--- a/util/src/test/java/com/ning/billing/util/globallocker/TestMysqlGlobalLocker.java
+++ b/util/src/test/java/com/ning/billing/util/globallocker/TestMysqlGlobalLocker.java
@@ -32,7 +32,7 @@ import org.testng.annotations.Test;
 import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
 import com.ning.billing.dbi.MysqlTestingHelper;
-import com.ning.billing.util.globallocker.GlobalLocker.LockerService;
+import com.ning.billing.util.globallocker.GlobalLocker.LockerType;
 import com.ning.billing.util.io.IOUtils;
 
 @Test(groups = "slow")
@@ -66,7 +66,7 @@ public class TestMysqlGlobalLocker {
         final String lockName = UUID.randomUUID().toString();
 
         final GlobalLocker locker = new MySqlGlobalLocker(dbi);
-        final GlobalLock lock = locker.lockWithNumberOfTries(LockerService.INVOICE, lockName, 3);
+        final GlobalLock lock = locker.lockWithNumberOfTries(LockerType.ACCOUNT, lockName, 3);
 
         dbi.inTransaction(new TransactionCallback<Void>() {
             @Override
@@ -76,11 +76,11 @@ public class TestMysqlGlobalLocker {
                 return null;
             }
         });
-        Assert.assertEquals(locker.isFree(LockerService.INVOICE, lockName), Boolean.FALSE);
+        Assert.assertEquals(locker.isFree(LockerType.ACCOUNT, lockName), Boolean.FALSE);
 
         boolean gotException = false;
         try {
-            locker.lockWithNumberOfTries(LockerService.INVOICE, lockName, 1);
+            locker.lockWithNumberOfTries(LockerType.ACCOUNT, lockName, 1);
         } catch (LockFailedException e) {
             gotException = true;
         }
@@ -88,7 +88,7 @@ public class TestMysqlGlobalLocker {
 
         lock.release();
 
-        Assert.assertEquals(locker.isFree(LockerService.INVOICE, lockName), Boolean.TRUE);
+        Assert.assertEquals(locker.isFree(LockerType.ACCOUNT, lockName), Boolean.TRUE);
     }
 
     public static final class TestMysqlGlobalLockerModule extends AbstractModule {