killbill-aplcache

Merge branch 'inv-ent-integration' of github.com:ning/killbill

1/18/2012 10:17:07 PM

Changes

invoice/pom.xml 5(+5 -0)

Details

diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java b/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
index b953707..b8c4314 100644
--- a/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
+++ b/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
@@ -41,21 +41,22 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
     private final BigDecimal balance;
     private final DefaultTagStore tags;
 
-    public DefaultAccount(AccountData data) {
+    public DefaultAccount(final AccountData data) {
         this(UUID.randomUUID(), data.getExternalKey(), data.getEmail(), data.getName(),
                 data.getFirstNameLength(), data.getPhone(), data.getCurrency(), data.getBillCycleDay(),
                 data.getPaymentProviderName(), BigDecimal.ZERO);
     }
 
-    public DefaultAccount(UUID id, AccountData data) {
+    public DefaultAccount(final UUID id, final AccountData data) {
         this(id, data.getExternalKey(), data.getEmail(), data.getName(),
                 data.getFirstNameLength(), data.getPhone(), data.getCurrency(), data.getBillCycleDay(),
                 data.getPaymentProviderName(), BigDecimal.ZERO);
     }
 
-    public DefaultAccount(UUID id, String externalKey, String email, String name, int firstNameLength,
-                          String phone, Currency currency, int billCycleDay, String paymentProviderName,
-                          BigDecimal balance) {
+    public DefaultAccount(final UUID id, final String externalKey, final String email,
+                          final String name, final int firstNameLength,
+                          final String phone, final Currency currency, final int billCycleDay, final String paymentProviderName,
+                          final BigDecimal balance) {
         super(id);
         this.externalKey = externalKey;
         this.email = email;
@@ -121,18 +122,18 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
     }
 
     @Override
-    public boolean hasTag(String tagName) {
+    public boolean hasTag(final String tagName) {
         return tags.containsTag(tagName);
     }
 
     @Override
-    public void addTag(TagDescription description, String addedBy, DateTime dateAdded) {
+    public void addTag(final TagDescription description, final String addedBy, final DateTime dateAdded) {
         Tag tag = new DefaultTag(description, addedBy, dateAdded);
         tags.add(tag) ;
     }
 
     @Override
-    public void addTags(List<Tag> tags) {
+    public void addTags(final List<Tag> tags) {
         if (tags != null) {
             this.tags.add(tags);
         }
@@ -144,7 +145,7 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
     }
 
     @Override
-    public void removeTag(TagDescription description) {
+    public void removeTag(final TagDescription description) {
         tags.remove(description.getName());
     }
 
diff --git a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
index d205439..9dcce76 100644
--- a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
+++ b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
@@ -16,18 +16,17 @@
 
 package com.ning.billing.account.dao;
 
+import java.io.IOException;
+import org.apache.commons.io.IOUtils;
+import org.skife.jdbi.v2.IDBI;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Stage;
 import com.ning.billing.account.glue.AccountModuleMock;
-import com.ning.billing.util.eventbus.DefaultEventBusService;
 import com.ning.billing.util.eventbus.BusService;
-import org.apache.commons.io.IOUtils;
-import org.skife.jdbi.v2.IDBI;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
-
-import java.io.IOException;
+import com.ning.billing.util.eventbus.DefaultEventBusService;
 
 import static org.testng.Assert.fail;
 
@@ -42,12 +41,10 @@ public abstract class AccountDaoTestBase {
         try {
             module = new AccountModuleMock();
             final String accountDdl = IOUtils.toString(AccountSqlDao.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
-            final String invoiceDdl = IOUtils.toString(AccountSqlDao.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
             final String utilDdl = IOUtils.toString(AccountSqlDao.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
 
             module.startDb();
             module.initDb(accountDdl);
-            module.initDb(invoiceDdl);
             module.initDb(utilDdl);
 
             final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module);
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java b/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java
index 2b07182..0e429ab 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java
@@ -23,6 +23,7 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 
 import com.ning.billing.account.api.Account;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 
 public interface EntitlementBillingApi {
 
@@ -35,7 +36,8 @@ public interface EntitlementBillingApi {
      */
     public SortedSet<BillingEvent> getBillingEventsForAccount(UUID accountId);
 
+    public UUID getAccountIdFromSubscriptionId(UUID subscriptionId) throws EntitlementBillingApiException;
 
-    public void setChargedThroughDate(UUID subscriptionId, DateTime ctd);
+    public void setChargedThroughDate(UUID subscriptionId, DateTime ctd) throws EntitlementBillingApiException;
 
 }
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApiException.java b/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApiException.java
index 9c22915..4c20f7c 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApiException.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApiException.java
@@ -16,25 +16,21 @@
 
 package com.ning.billing.entitlement.api.billing;
 
-public class EntitlementBillingApiException extends Exception {
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.ErrorCode;
 
+public class EntitlementBillingApiException extends BillingExceptionBase {
     private static final long serialVersionUID = 127392038L;
 
-    public EntitlementBillingApiException() {
-        super();
+    public EntitlementBillingApiException(Throwable cause, int code, final String msg) {
+        super(cause, code, msg);
     }
 
-    public EntitlementBillingApiException(String msg, Throwable arg1) {
-        super(msg, arg1);
+    public EntitlementBillingApiException(Throwable cause, ErrorCode code, final Object... args) {
+        super(cause, code, args);
     }
 
-    public EntitlementBillingApiException(String msg) {
-        super(msg);
+    public EntitlementBillingApiException(ErrorCode code, final Object... args) {
+        super(code, args);
     }
-
-    public EntitlementBillingApiException(Throwable msg) {
-        super(msg);
-    }
-
-
 }
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index 254df9f..c3238e2 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -43,7 +43,10 @@ public enum ErrorCode {
     ENT_CANCEL_BAD_STATE(1031, "Subscription %s is in state %s"),
     /* Un-cancellation */
     ENT_UNCANCEL_BAD_STATE(1070, "Subscription %s was not in a cancelled state"),
-
+    /* Fetch */
+    ENT_GET_NO_BUNDLE_FOR_SUBSCRIPTION(1080, "Could not find a bundle for subscription %s"),
+    ENT_GET_INVALID_BUNDLE_ID(1081, "Could not find a bundle matching id %s"),
+    ENT_INVALID_SUBSCRIPTION_ID(1082, "Unknown subscription %s"),
     /*
     *
     * Range 2000 : CATALOG
@@ -102,8 +105,16 @@ public enum ErrorCode {
     *
     */
     ACCOUNT_ALREADY_EXISTS(3000, "Account already exists for key %s"),
-    ACCOUNT_INVALID_NAME(3001, "An invalid name was specified when creating or updating an account.")
+    ACCOUNT_INVALID_NAME(3001, "An invalid name was specified when creating or updating an account."),
 
+   /*
+    *
+    * Range 4000: INVOICE
+    *
+    */
+    INVOICE_ACCOUNT_ID_INVALID(4001, "No account could be retrieved for id %s"),
+    INVOICE_INVALID_TRANSITION(4002, "Transition did not contain a subscription id."),
+    INVOICE_NO_ACCOUNT_ID_FOR_SUBSCRIPTION_ID(4003, "No account id was retrieved for subscription id %s")
     ;
     private int code;
     private String format;
diff --git a/api/src/main/java/com/ning/billing/invoice/api/Invoice.java b/api/src/main/java/com/ning/billing/invoice/api/Invoice.java
index f69b934..0d13363 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/Invoice.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/Invoice.java
@@ -25,14 +25,22 @@ import java.util.List;
 import java.util.UUID;
 
 public interface Invoice extends Entity {
-    boolean add(InvoiceItem item);
+    boolean addInvoiceItem(InvoiceItem item);
 
-    boolean add(List<InvoiceItem> items);
+    boolean addInvoiceItems(List<InvoiceItem> items);
 
-    List<InvoiceItem> getItems();
+    List<InvoiceItem> getInvoiceItems();
 
     int getNumberOfItems();
 
+    boolean addPayment(InvoicePayment payment);
+
+    boolean addPayments(List<InvoicePayment> payments);
+
+    List<InvoicePayment> getPayments();
+
+    int getNumberOfPayments();
+
     UUID getAccountId();
 
     DateTime getInvoiceDate();
@@ -47,7 +55,7 @@ public interface Invoice extends Entity {
 
     BigDecimal getTotalAmount();
 
-    BigDecimal getAmountOutstanding();
+    BigDecimal getBalance();
 
     boolean isDueForPayment(DateTime targetDate, int numberOfDays);
 }
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceApiException.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceApiException.java
new file mode 100644
index 0000000..a9275e8
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceApiException.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.invoice.api;
+
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.ErrorCode;
+
+public class InvoiceApiException extends BillingExceptionBase {
+    public InvoiceApiException(Throwable cause, int code, final String msg) {
+        super(cause, code, msg);
+    }
+
+    public InvoiceApiException(Throwable cause, ErrorCode code, final Object... args) {
+        super(cause, code, args);
+    }
+
+    public InvoiceApiException(ErrorCode code, final Object... args) {
+        super(code, args);
+    }
+}
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoicePayment.java b/api/src/main/java/com/ning/billing/invoice/api/InvoicePayment.java
new file mode 100644
index 0000000..a4acadb
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoicePayment.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.invoice.api;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+import org.joda.time.DateTime;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.entity.Entity;
+
+public interface InvoicePayment extends Entity {
+    UUID getInvoiceId();
+
+    DateTime getPaymentDate();
+
+    BigDecimal getAmount();
+
+    Currency getCurrency();
+}
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
index 2c8d02e..8903584 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
@@ -28,6 +28,10 @@ public interface InvoiceUserApi {
 
     public List<Invoice> getInvoicesByAccount(UUID accountId);
 
+    public List<Invoice> getInvoicesByAccount(UUID accountId, DateTime fromDate);
+
+    public List<InvoiceItem> getInvoiceItemsByAccount(UUID accountId);
+
     public Invoice getInvoice(UUID invoiceId);
 
     public void paymentAttemptFailed(UUID invoiceId, UUID paymentId, DateTime paymentAttemptDate);
diff --git a/beatrix/src/test/resources/Catalog-Entitlement-Testplan.txt b/beatrix/src/test/resources/Catalog-Entitlement-Testplan.txt
new file mode 100644
index 0000000..6d5867f
--- /dev/null
+++ b/beatrix/src/test/resources/Catalog-Entitlement-Testplan.txt
@@ -0,0 +1,112 @@
+ * 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.
+
+
+NOTES
+=====
+
+Events: Create, change, cancel, migrate 
+Validate: BillingEvents, SubscriptionTransition 
+Rules: 
+    Cancellation
+        Action Policy: When to cancel (Immediate/End-of-term)
+    Creation
+        Alignment: How to align phases in a bundle
+    Change plan behavior
+        Action Policy: When to change plan (Immediate/End-of-term)
+        Alignment: How to align phases 
+        Pricelist: Which pricelist to pick when moving between plans
+    Billing alignment
+        Subscription BCD, Bundle BCD, Account BCD 
+Phases - timing
+Prices - multi-currency, fixed vs recurring prices
+Pricelists - particularly pricelist change rules
+Catalog changes new subscriptions / existing subscriptions
+Price change
+    
+TESTS
+=====
+
+BASEPLAN TESTS
+    * Create a single phase recurring plan
+        - check for creation event (timing?)
+        - check for no termination event
+        - check pricing (different currencies)
+        - check BDC (subscription, account, timezone)
+    * Create a single phase fixed length plan 
+        - check for creation event (timing - different request dates)
+        - check for termination event (timing - different lengths?)
+        - check price (fixed vs recurring)
+    * Create a two phase event use a fixed price and a recurring price
+        - check for phase change (timing)
+        - check prices change
+    * Create a multi-phase plan 
+        - check for phase events
+        - check price changes
+    * Create multiple base plans in a single bundle - should fail
+    
+    * Change base plan once
+        - check plan change policy (immediate, eot)
+        - check alignments of new plan with old 
+        - check move between pricelists
+        - check that phases progress successfully after change
+        - check obsolete events are removed
+    * Change base plan multiple times
+        - check that alignment occurs correctly 
+        - check phases progress correctly
+        - check obsolete events are removed 
+        
+    * Cancellation of a single phase plan
+        - check creation and timing of termination event
+    * Cancellation of a multi-phase plan
+        - check creation of termination event 
+        - check removal of events beyond termination event
+    * Change a cancelled base plan - should fail
+    
+    * Migration to a single phase plan
+        - check migration event occurs when it should
+    * Migration to a multi-phase plan
+        - check migration event occurs when it should
+        - check migration into different phase
+        - check alignment of phases can be correctly controlled
+    * Migration to a fixed duration plan
+        - check migration event occurs when it should
+        - check termination event occurs when it should
+        
+
+         
+STANDALONE TEST
+    * Create multiple plans in a single bundle
+        - check plans can be created
+        - check cannot add a base plan
+        - check BCD at subscription bundle level
+            
+
+PRICE CHANGE TEST
+    * Price change on a single phase base plan
+        - check new subscriptions get price after effective date
+        - check changed subscriptions get price after effective date
+        - check existing subscriptions ONLY get it after ESED
+        - check that if ESED is missing existing subs are grandfathered for ever
+    * Price change on a multi-phase subscription
+        - check price change applies correctly to correct phases
+    * Multiple price changes
+        - check multiple price changes with overlapping dates
+
+
+ADD-ON TESTS
+    * Add-on creation alignment
+    * Add-on cancel with base plan
+    
+    
\ No newline at end of file
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java
index fd130a9..09df8ae 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java
@@ -38,6 +38,7 @@ import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.Product;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
@@ -46,14 +47,14 @@ import com.ning.billing.entitlement.api.user.SubscriptionTransition;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 
 public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
-	private Logger log = LoggerFactory.getLogger(DefaultEntitlementBillingApi.class);
+	private static final Logger log = LoggerFactory.getLogger(DefaultEntitlementBillingApi.class);
 	
     private final EntitlementDao dao;
     private final AccountUserApi accountApi;
     private final CatalogService catalogService;
 
     @Inject
-    public DefaultEntitlementBillingApi(EntitlementDao dao, AccountUserApi accountApi, CatalogService catalogService) {
+    public DefaultEntitlementBillingApi(final EntitlementDao dao, final AccountUserApi accountApi, final CatalogService catalogService) {
         super();
         this.dao = dao;
         this.accountApi = accountApi;
@@ -62,17 +63,17 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
 
     @Override
     public SortedSet<BillingEvent> getBillingEventsForAccount(
-            UUID accountId) {
+            final UUID accountId) {
         
         List<SubscriptionBundle> bundles = dao.getSubscriptionBundleForAccount(accountId);
         List<Subscription> subscriptions = new ArrayList<Subscription>();
-        for (SubscriptionBundle bundle: bundles) {
+        for (final SubscriptionBundle bundle: bundles) {
             subscriptions.addAll(dao.getSubscriptions(bundle.getId()));
         }
 
         SortedSet<BillingEvent> result = new TreeSet<BillingEvent>();        
-        for (Subscription subscription: subscriptions) {
-        	for (SubscriptionTransition transition : subscription.getAllTransitions()) {
+        for (final Subscription subscription: subscriptions) {
+        	for (final SubscriptionTransition transition : subscription.getAllTransitions()) {
         		try {
         			result.add(new DefaultBillingEvent(transition, subscription, calculateBCD(transition, accountId)));
         		} catch (CatalogApiException e) {
@@ -83,8 +84,13 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
         }
         return result;
     }
-    
-    private int calculateBCD(SubscriptionTransition transition, UUID accountId) throws CatalogApiException {
+
+    @Override
+    public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId) throws EntitlementBillingApiException {
+        return dao.getAccountIdFromSubscriptionId(subscriptionId);
+    }
+
+    private int calculateBCD(final SubscriptionTransition transition, final UUID accountId) throws CatalogApiException {
     	Catalog catalog = catalogService.getFullCatalog();
     	Plan plan = transition.getNextPlan();
     	Product product = plan.getProduct();
@@ -123,10 +129,10 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
     
 
     @Override
-    public void setChargedThroughDate(UUID subscriptionId, DateTime ctd) {
+    public void setChargedThroughDate(final UUID subscriptionId, final DateTime ctd) throws EntitlementBillingApiException {
         SubscriptionData subscription = (SubscriptionData) dao.getSubscriptionFromId(subscriptionId);
         if (subscription == null) {
-            new EntitlementBillingApiException(String.format("Unknown subscription %s", subscriptionId));
+            throw new EntitlementBillingApiException(ErrorCode.ENT_INVALID_SUBSCRIPTION_ID, subscriptionId.toString());
         }
 
         SubscriptionBuilder builder = new SubscriptionBuilder(subscription)
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
index ea62b84..c658ee6 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
@@ -16,7 +16,9 @@
 
 package com.ning.billing.entitlement.engine.dao;
 
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
@@ -40,6 +42,8 @@ public interface EntitlementDao {
 
     public Subscription getSubscriptionFromId(UUID subscriptionId);
 
+    // Account retrieval
+    public UUID getAccountIdFromSubscriptionId(UUID subscriptionId) throws EntitlementBillingApiException;
 
     // Subscription retrieval
     public Subscription getBaseSubscription(UUID bundleId);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
index 72a71d8..f67d607 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
@@ -23,10 +23,13 @@ import java.util.List;
 import java.util.UUID;
 
 import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData.SubscriptionMigrationData;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
@@ -66,7 +69,8 @@ public class EntitlementSqlDao implements EntitlementDao {
     private final NotificationQueueService notificationQueueService;
 
     @Inject
-    public EntitlementSqlDao(DBI dbi, Clock clock, SubscriptionFactory factory, NotificationQueueService notificationQueueService) {
+    public EntitlementSqlDao(final DBI dbi, final Clock clock, final SubscriptionFactory factory,
+                             final NotificationQueueService notificationQueueService) {
         this.clock = clock;
         this.factory = factory;
         this.subscriptionsDao = dbi.onDemand(SubscriptionSqlDao.class);
@@ -76,18 +80,18 @@ public class EntitlementSqlDao implements EntitlementDao {
     }
 
     @Override
-    public SubscriptionBundle getSubscriptionBundleFromKey(String bundleKey) {
+    public SubscriptionBundle getSubscriptionBundleFromKey(final String bundleKey) {
         return bundlesDao.getBundleFromKey(bundleKey);
     }
 
     @Override
     public List<SubscriptionBundle> getSubscriptionBundleForAccount(
-            UUID accountId) {
+            final UUID accountId) {
         return bundlesDao.getBundleFromAccount(accountId.toString());
     }
 
     @Override
-    public SubscriptionBundle getSubscriptionBundleFromId(UUID bundleId) {
+    public SubscriptionBundle getSubscriptionBundleFromId(final UUID bundleId) {
         return bundlesDao.getBundleFromId(bundleId.toString());
     }
 
@@ -103,11 +107,26 @@ public class EntitlementSqlDao implements EntitlementDao {
     }
 
     @Override
-    public Subscription getSubscriptionFromId(UUID subscriptionId) {
+    public Subscription getSubscriptionFromId(final UUID subscriptionId) {
         return buildSubscription(subscriptionsDao.getSubscriptionFromId(subscriptionId.toString()));
     }
 
     @Override
+    public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId) throws EntitlementBillingApiException {
+        UUID bundleId = subscriptionsDao.getSubscriptionFromId(subscriptionId.toString()).getBundleId();
+        if (bundleId == null) {
+            throw new EntitlementBillingApiException(ErrorCode.ENT_GET_NO_BUNDLE_FOR_SUBSCRIPTION, subscriptionId.toString());
+        }
+
+        SubscriptionBundle bundle = bundlesDao.getBundleFromId(bundleId.toString());
+        if (bundle == null) {
+            throw new EntitlementBillingApiException(ErrorCode.ENT_GET_INVALID_BUNDLE_ID, bundleId.toString());
+        }
+
+        return bundle.getAccountId();
+    }
+
+    @Override
     public Subscription getBaseSubscription(final UUID bundleId) {
 
         List<Subscription> subscriptions = subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString());
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadMockEntitlementDao.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadMockEntitlementDao.java
index 94fd234..a186bff 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadMockEntitlementDao.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadMockEntitlementDao.java
@@ -16,11 +16,10 @@
 
 package com.ning.billing.entitlement.api.billing;
 
-import java.util.Collection;
 import java.util.List;
 import java.util.UUID;
-
 import com.ning.billing.entitlement.api.migration.AccountMigrationData;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
@@ -32,110 +31,115 @@ class BrainDeadMockEntitlementDao implements EntitlementDao {
 
 	@Override
 	public List<SubscriptionBundle> getSubscriptionBundleForAccount(
-			UUID accountId) {
+			final UUID accountId) {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
-	public SubscriptionBundle getSubscriptionBundleFromKey(String bundleKey) {
+	public SubscriptionBundle getSubscriptionBundleFromKey(final String bundleKey) {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
-	public SubscriptionBundle getSubscriptionBundleFromId(UUID bundleId) {
+	public SubscriptionBundle getSubscriptionBundleFromId(final UUID bundleId) {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
 	public SubscriptionBundle createSubscriptionBundle(
-			SubscriptionBundleData bundle) {
+			final SubscriptionBundleData bundle) {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
-	public Subscription getSubscriptionFromId(UUID subscriptionId) {
+	public Subscription getSubscriptionFromId(final UUID subscriptionId) {
 		throw new UnsupportedOperationException();
 
 	}
 
-	@Override
-	public Subscription getBaseSubscription(UUID bundleId) {
+    @Override
+    public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId) throws EntitlementBillingApiException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+	public Subscription getBaseSubscription(final UUID bundleId) {
 		throw new UnsupportedOperationException();
 
 	}
 
 	@Override
-	public List<Subscription> getSubscriptions(UUID bundleId) {
+	public List<Subscription> getSubscriptions(final UUID bundleId) {
 		throw new UnsupportedOperationException();
 
 	}
 
 	@Override
-	public List<Subscription> getSubscriptionsForKey(String bundleKey) {
+	public List<Subscription> getSubscriptionsForKey(final String bundleKey) {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
-	public void updateSubscription(SubscriptionData subscription) {
+	public void updateSubscription(final SubscriptionData subscription) {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
-	public void createNextPhaseEvent(UUID subscriptionId,
-			EntitlementEvent nextPhase) {
+	public void createNextPhaseEvent(final UUID subscriptionId,
+			final EntitlementEvent nextPhase) {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
 	public List<EntitlementEvent> getEventsForSubscription(
-			UUID subscriptionId) {
+			final UUID subscriptionId) {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
 	public List<EntitlementEvent> getPendingEventsForSubscription(
-			UUID subscriptionId) {
+			final UUID subscriptionId) {
 		throw new UnsupportedOperationException();
 	}
 
 
 	@Override
-	public void createSubscription(SubscriptionData subscription,
-			List<EntitlementEvent> initialEvents) {
+	public void createSubscription(final SubscriptionData subscription,
+			final List<EntitlementEvent> initialEvents) {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
-	public void cancelSubscription(UUID subscriptionId,
-			EntitlementEvent cancelEvent) {
+	public void cancelSubscription(final UUID subscriptionId,
+			final EntitlementEvent cancelEvent) {
 		throw new UnsupportedOperationException();
 
 	}
 
 	@Override
-	public void uncancelSubscription(UUID subscriptionId,
-			List<EntitlementEvent> uncancelEvents) {
+	public void uncancelSubscription(final UUID subscriptionId,
+			final List<EntitlementEvent> uncancelEvents) {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
-	public void changePlan(UUID subscriptionId,
-			List<EntitlementEvent> changeEvents) {
+	public void changePlan(final UUID subscriptionId,
+			final List<EntitlementEvent> changeEvents) {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
-	public void migrate(UUID acountId, AccountMigrationData data) {
+	public void migrate(final UUID acountId, final AccountMigrationData data) {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
-	public void undoMigration(UUID accountId) {
+	public void undoMigration(final UUID accountId) {
         throw new UnsupportedOperationException();
 	}
 
     @Override
-    public EntitlementEvent getEventById(UUID eventId) {
+    public EntitlementEvent getEventById(final UUID eventId) {
         throw new UnsupportedOperationException();
     }
 }
\ No newline at end of file
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
index 77eb10b..21b982b 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
@@ -43,6 +43,7 @@ import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.glue.CatalogModule;
 import com.ning.billing.entitlement.api.TestApiBase;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
@@ -125,8 +126,13 @@ public class TestDefaultEntitlementBillingApi {
 				return subscription;
 
 			}
-			
-			@Override
+
+            @Override
+            public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId) throws EntitlementBillingApiException {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
 			public SubscriptionBundle getSubscriptionBundleFromId(UUID bundleId) {
 				return bundle;
 			}
@@ -141,8 +147,13 @@ public class TestDefaultEntitlementBillingApi {
 					UUID accountId) {
 				return new ArrayList<SubscriptionBundle>();
 			}
-			
-		};
+
+            @Override
+            public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId) throws EntitlementBillingApiException {
+                throw new UnsupportedOperationException();
+            }
+
+        };
 		AccountUserApi accountApi = new BrainDeadAccountUserApi() ;
 		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
 		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java
index 3326bc8..1e725bf 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java
@@ -22,6 +22,7 @@ import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.assertFalse;
 
 import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.util.clock.DefaultClock;
 import org.joda.time.DateTime;
 import org.testng.Assert;
@@ -80,7 +81,7 @@ public abstract class TestUserApiCancel extends TestApiBase {
     }
 
 
-    protected void testCancelSubscriptionEOTWithChargeThroughDate() {
+    protected void testCancelSubscriptionEOTWithChargeThroughDate() throws EntitlementBillingApiException {
         log.info("Starting testCancelSubscriptionEOTWithChargeThroughDate");
 
         try {
@@ -176,7 +177,7 @@ public abstract class TestUserApiCancel extends TestApiBase {
     // Similar test to testCancelSubscriptionEOTWithChargeThroughDate except we uncancel and check things
     // are as they used to be and we can move forward without hitting cancellation
     //
-    protected void testUncancel() {
+    protected void testUncancel() throws EntitlementBillingApiException {
 
         log.info("Starting testUncancel");
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelMemory.java
index 630d925..dbcc680 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelMemory.java
@@ -19,6 +19,7 @@ package com.ning.billing.entitlement.api.user;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Stage;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.entitlement.glue.MockEngineModuleMemory;
 import org.testng.annotations.Test;
 
@@ -38,7 +39,7 @@ public class TestUserApiCancelMemory extends TestUserApiCancel {
 
     @Override
     @Test(enabled=true, groups={"fast"})
-    public void testCancelSubscriptionEOTWithChargeThroughDate() {
+    public void testCancelSubscriptionEOTWithChargeThroughDate() throws EntitlementBillingApiException {
         super.testCancelSubscriptionEOTWithChargeThroughDate();
     }
 
@@ -50,7 +51,7 @@ public class TestUserApiCancelMemory extends TestUserApiCancel {
 
     @Override
     @Test(enabled=true, groups={"fast"})
-    public void testUncancel() {
+    public void testUncancel() throws EntitlementBillingApiException {
         super.testUncancel();
     }
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelSql.java
index 87491c7..840f357 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelSql.java
@@ -19,6 +19,7 @@ package com.ning.billing.entitlement.api.user;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Stage;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.entitlement.glue.MockEngineModuleSql;
 import org.testng.annotations.Test;
 
@@ -33,7 +34,7 @@ public class TestUserApiCancelSql extends TestUserApiCancel {
     }
 
     @Test(enabled= false, groups={"stress"})
-    public void stressTest() {
+    public void stressTest() throws EntitlementBillingApiException {
         for (int i = 0; i < MAX_STRESS_ITERATIONS; i++) {
             cleanupTest();
             setupTest();
@@ -55,7 +56,7 @@ public class TestUserApiCancelSql extends TestUserApiCancel {
 
     @Override
     @Test(enabled=true, groups={"sql"})
-    public void testCancelSubscriptionEOTWithChargeThroughDate() {
+    public void testCancelSubscriptionEOTWithChargeThroughDate() throws EntitlementBillingApiException {
         super.testCancelSubscriptionEOTWithChargeThroughDate();
     }
 
@@ -67,7 +68,7 @@ public class TestUserApiCancelSql extends TestUserApiCancel {
 
     @Override
     @Test(enabled=true, groups={"sql"})
-    public void testUncancel() {
+    public void testUncancel() throws EntitlementBillingApiException {
         super.testUncancel();
     }
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
index 51c3d8b..78616be 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
@@ -37,6 +37,7 @@ import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.TestApiBase;
 
 import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.events.user.ApiEvent;
 import com.ning.billing.util.clock.DefaultClock;
@@ -100,12 +101,12 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
     }
 
 
-    protected void testChangePlanBundleAlignEOTWithChargeThroughDate() {
+    protected void testChangePlanBundleAlignEOTWithChargeThroughDate() throws EntitlementBillingApiException {
         testChangePlanBundleAlignEOTWithChargeThroughDate("Shotgun", BillingPeriod.ANNUAL, "gunclubDiscount", "Pistol", BillingPeriod.ANNUAL, "gunclubDiscount");
     }
 
     private void testChangePlanBundleAlignEOTWithChargeThroughDate(String fromProd, BillingPeriod fromTerm, String fromPlanSet,
-            String toProd, BillingPeriod toTerm, String toPlanSet) {
+            String toProd, BillingPeriod toTerm, String toPlanSet) throws EntitlementBillingApiException {
 
         log.info("Starting testChangeSubscriptionEOTWithChargeThroughDate");
         try {
@@ -216,12 +217,12 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
     }
 
 
-    protected void testChangePlanChangePlanAlignEOTWithChargeThroughDate() {
+    protected void testChangePlanChangePlanAlignEOTWithChargeThroughDate() throws EntitlementBillingApiException {
         tChangePlanChangePlanAlignEOTWithChargeThroughDate("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, "Assault-Rifle", BillingPeriod.ANNUAL, "rescue");
     }
 
     private void tChangePlanChangePlanAlignEOTWithChargeThroughDate(String fromProd, BillingPeriod fromTerm, String fromPlanSet,
-            String toProd, BillingPeriod toTerm, String toPlanSet) {
+            String toProd, BillingPeriod toTerm, String toPlanSet) throws EntitlementBillingApiException {
 
         log.info("Starting testChangePlanBundleAlignEOTWithChargeThroughDate");
 
@@ -297,7 +298,7 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
         }
     }
 
-    protected void testMultipleChangeLastIMM() {
+    protected void testMultipleChangeLastIMM() throws EntitlementBillingApiException {
 
         try {
             SubscriptionData subscription = createSubscription("Assault-Rifle", BillingPeriod.MONTHLY, "gunclubDiscount");
@@ -344,7 +345,7 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
         }
     }
 
-    protected void testMultipleChangeLastEOT() {
+    protected void testMultipleChangeLastEOT() throws EntitlementBillingApiException {
 
         try {
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
index aecaaac..253da07 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
@@ -19,6 +19,7 @@ package com.ning.billing.entitlement.api.user;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Stage;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.entitlement.glue.MockEngineModuleMemory;
 import org.testng.annotations.Test;
 
@@ -38,7 +39,7 @@ public class TestUserApiChangePlanMemory extends TestUserApiChangePlan {
 
     @Override
     @Test(enabled=true, groups={"fast"})
-    public void testChangePlanBundleAlignEOTWithChargeThroughDate() {
+    public void testChangePlanBundleAlignEOTWithChargeThroughDate() throws EntitlementBillingApiException {
         super.testChangePlanBundleAlignEOTWithChargeThroughDate();
     }
 
@@ -50,20 +51,20 @@ public class TestUserApiChangePlanMemory extends TestUserApiChangePlan {
 
     @Override
     @Test(enabled=true, groups={"fast"})
-    public void testMultipleChangeLastIMM() {
+    public void testMultipleChangeLastIMM() throws EntitlementBillingApiException {
         super.testMultipleChangeLastIMM();
     }
 
     @Override
     @Test(enabled=true, groups={"fast"})
-    public void testMultipleChangeLastEOT() {
+    public void testMultipleChangeLastEOT() throws EntitlementBillingApiException {
         super.testMultipleChangeLastEOT();
     }
 
     // Set to false until we implement rescue example.
     @Override
     @Test(enabled=false, groups={"fast"})
-    public void testChangePlanChangePlanAlignEOTWithChargeThroughDate() {
+    public void testChangePlanChangePlanAlignEOTWithChargeThroughDate() throws EntitlementBillingApiException {
         super.testChangePlanChangePlanAlignEOTWithChargeThroughDate();
     }
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
index f5ad803..92aa652 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
@@ -19,6 +19,7 @@ package com.ning.billing.entitlement.api.user;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Stage;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.entitlement.glue.MockEngineModuleSql;
 import org.testng.annotations.Test;
 
@@ -32,7 +33,7 @@ public class TestUserApiChangePlanSql extends TestUserApiChangePlan {
     }
 
     @Test(enabled= true, groups={"stress"})
-    public void stressTest() {
+    public void stressTest() throws EntitlementBillingApiException {
         for (int i = 0; i < MAX_STRESS_ITERATIONS; i++) {
             cleanupTest();
             setupTest();
@@ -60,7 +61,7 @@ public class TestUserApiChangePlanSql extends TestUserApiChangePlan {
 
     @Override
     @Test(enabled=true, groups={"sql"})
-    public void testChangePlanBundleAlignEOTWithChargeThroughDate() {
+    public void testChangePlanBundleAlignEOTWithChargeThroughDate() throws EntitlementBillingApiException {
         super.testChangePlanBundleAlignEOTWithChargeThroughDate();
     }
 
@@ -72,20 +73,20 @@ public class TestUserApiChangePlanSql extends TestUserApiChangePlan {
 
     @Override
     @Test(enabled=true, groups={"sql"})
-    public void testMultipleChangeLastIMM() {
+    public void testMultipleChangeLastIMM() throws EntitlementBillingApiException {
         super.testMultipleChangeLastIMM();
     }
 
     @Override
     @Test(enabled=true, groups={"sql"})
-    public void testMultipleChangeLastEOT() {
+    public void testMultipleChangeLastEOT() throws EntitlementBillingApiException {
         super.testMultipleChangeLastEOT();
     }
 
     // rescue not implemented yet
     @Override
     @Test(enabled=false, groups={"sql"})
-    public void testChangePlanChangePlanAlignEOTWithChargeThroughDate() {
+    public void testChangePlanChangePlanAlignEOTWithChargeThroughDate() throws EntitlementBillingApiException {
         super.testChangePlanChangePlanAlignEOTWithChargeThroughDate();
     }
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java
index 4a8b4b1..7fceab9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java
@@ -29,6 +29,7 @@ import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.TestApiBase;
 
 import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.entitlement.glue.MockEngineModuleSql;
 import com.ning.billing.util.clock.DefaultClock;
 import org.joda.time.DateTime;
@@ -64,7 +65,7 @@ public class TestUserApiDemos extends TestApiBase {
      *  8. Cancel EOT
      */
     @Test(enabled=true, groups="demos")
-    public void testDemo1() {
+    public void testDemo1() throws EntitlementBillingApiException {
 
         try {
             System.out.println("DEMO 1 START");
@@ -189,7 +190,7 @@ public class TestUserApiDemos extends TestApiBase {
     }
 
     @Test(enabled= true, groups={"stress"})
-    public void stressTest() {
+    public void stressTest() throws EntitlementBillingApiException {
         for (int i = 0; i < 100; i++) {
             cleanupTest();
             setupTest();
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java
index 84d1031..f69b36a 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java
@@ -26,6 +26,7 @@ import com.ning.billing.catalog.api.PlanPhase;
 
 import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
 import com.ning.billing.entitlement.api.TestApiBase;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.entitlement.glue.MockEngineModuleSql;
 import com.ning.billing.util.clock.DefaultClock;
 import org.joda.time.DateTime;
@@ -42,7 +43,7 @@ public class TestUserApiScenarios extends TestApiBase {
     }
 
     @Test(enabled=true)
-    public void testChangeIMMCancelUncancelChangeEOT() {
+    public void testChangeIMMCancelUncancelChangeEOT() throws EntitlementBillingApiException {
 
         log.info("Starting testChangeIMMCancelUncancelChangeEOT");
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
index 86e458f..d5719e4 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
@@ -16,20 +16,30 @@
 
 package com.ning.billing.entitlement.engine.dao;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.TreeSet;
+import java.util.UUID;
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import com.google.inject.Inject;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.catalog.api.TimeUnit;
 import com.ning.billing.config.EntitlementConfig;
-
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData.SubscriptionMigrationData;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
-import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.api.user.SubscriptionFactory;
-
 import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
 import com.ning.billing.entitlement.engine.core.Engine;
 import com.ning.billing.entitlement.events.EntitlementEvent;
@@ -38,20 +48,10 @@ import com.ning.billing.entitlement.events.user.ApiEvent;
 import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.notificationq.NotificationKey;
-import com.ning.billing.util.notificationq.NotificationLifecycle;
 import com.ning.billing.util.notificationq.NotificationQueue;
 import com.ning.billing.util.notificationq.NotificationQueueService;
 import com.ning.billing.util.notificationq.NotificationQueueService.NoSuchNotificationQueue;
 
-import org.joda.time.DateTime;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.*;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-
 public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlementDao {
 
     protected final static Logger log = LoggerFactory.getLogger(EntitlementDao.class);
@@ -65,7 +65,9 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     private final NotificationQueueService notificationQueueService;
 
     @Inject
-    public MockEntitlementDaoMemory(Clock clock, EntitlementConfig config, SubscriptionFactory factory, NotificationQueueService notificationQueueService) {
+    public MockEntitlementDaoMemory(final Clock clock, final EntitlementConfig config,
+                                    final SubscriptionFactory factory,
+                                    final NotificationQueueService notificationQueueService) {
         super();
         this.clock = clock;
         this.config = config;
@@ -84,9 +86,9 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public List<SubscriptionBundle> getSubscriptionBundleForAccount(UUID accountId) {
+    public List<SubscriptionBundle> getSubscriptionBundleForAccount(final UUID accountId) {
         List<SubscriptionBundle> results = new ArrayList<SubscriptionBundle>();
-        for (SubscriptionBundle cur : bundles) {
+        for (final SubscriptionBundle cur : bundles) {
             if (cur.getAccountId().equals(accountId)) {
                 results.add(cur);
             }
@@ -95,8 +97,8 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public SubscriptionBundle getSubscriptionBundleFromId(UUID bundleId) {
-        for (SubscriptionBundle cur : bundles) {
+    public SubscriptionBundle getSubscriptionBundleFromId(final UUID bundleId) {
+        for (final SubscriptionBundle cur : bundles) {
             if (cur.getId().equals(bundleId)) {
                 return cur;
             }
@@ -105,8 +107,8 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public SubscriptionBundle getSubscriptionBundleFromKey(String bundleKey) {
-        for (SubscriptionBundle cur : bundles) {
+    public SubscriptionBundle getSubscriptionBundleFromKey(final String bundleKey) {
+        for (final SubscriptionBundle cur : bundles) {
             if (cur.getKey().equals(bundleKey)) {
                 return cur;
             }
@@ -116,14 +118,14 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
 
 
     @Override
-    public SubscriptionBundle createSubscriptionBundle(SubscriptionBundleData bundle) {
+    public SubscriptionBundle createSubscriptionBundle(final SubscriptionBundleData bundle) {
         bundles.add(bundle);
         return getSubscriptionBundleFromId(bundle.getId());
     }
 
     @Override
-    public Subscription getSubscriptionFromId(UUID subscriptionId) {
-        for (Subscription cur : subscriptions) {
+    public Subscription getSubscriptionFromId(final UUID subscriptionId) {
+        for (final Subscription cur : subscriptions) {
             if (cur.getId().equals(subscriptionId)) {
                 return buildSubscription((SubscriptionData) cur);
             }
@@ -132,9 +134,14 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public List<Subscription> getSubscriptionsForKey(String bundleKey) {
+    public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId) throws EntitlementBillingApiException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<Subscription> getSubscriptionsForKey(final String bundleKey) {
 
-        for (SubscriptionBundle cur : bundles) {
+        for (final SubscriptionBundle cur : bundles) {
             if (cur.getKey().equals(bundleKey)) {
                 return getSubscriptions(cur.getId());
             }
@@ -144,11 +151,11 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
 
 
     @Override
-    public void createSubscription(SubscriptionData subscription, List<EntitlementEvent> initalEvents) {
+    public void createSubscription(final SubscriptionData subscription, final List<EntitlementEvent> initialEvents) {
 
         synchronized(events) {
-            events.addAll(initalEvents);
-            for (final EntitlementEvent cur : initalEvents) {
+            events.addAll(initialEvents);
+            for (final EntitlementEvent cur : initialEvents) {
                 recordFutureNotificationFromTransaction(null, cur.getEffectiveDate(), new NotificationKey() {
                     @Override
                     public String toString() {
@@ -162,10 +169,10 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public List<Subscription> getSubscriptions(UUID bundleId) {
+    public List<Subscription> getSubscriptions(final UUID bundleId) {
 
         List<Subscription> results = new ArrayList<Subscription>();
-        for (Subscription cur : subscriptions) {
+        for (final Subscription cur : subscriptions) {
             if (cur.getBundleId().equals(bundleId)) {
                 results.add(buildSubscription((SubscriptionData) cur));
             }
@@ -174,10 +181,10 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public List<EntitlementEvent> getEventsForSubscription(UUID subscriptionId) {
+    public List<EntitlementEvent> getEventsForSubscription(final UUID subscriptionId) {
         synchronized(events) {
             List<EntitlementEvent> results = new LinkedList<EntitlementEvent>();
-            for (EntitlementEvent cur : events) {
+            for (final EntitlementEvent cur : events) {
                 if (cur.getSubscriptionId().equals(subscriptionId)) {
                     results.add(cur);
                 }
@@ -187,10 +194,10 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public List<EntitlementEvent> getPendingEventsForSubscription(UUID subscriptionId) {
+    public List<EntitlementEvent> getPendingEventsForSubscription(final UUID subscriptionId) {
         synchronized(events) {
             List<EntitlementEvent> results = new LinkedList<EntitlementEvent>();
-            for (EntitlementEvent cur : events) {
+            for (final EntitlementEvent cur : events) {
                 if (cur.isActive() &&
                         cur.getEffectiveDate().isAfter(clock.getUTCNow()) &&
                             cur.getSubscriptionId().equals(subscriptionId)) {
@@ -203,8 +210,8 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
 
 
     @Override
-    public Subscription getBaseSubscription(UUID bundleId) {
-        for (Subscription cur : subscriptions) {
+    public Subscription getBaseSubscription(final UUID bundleId) {
+        for (final Subscription cur : subscriptions) {
             if (cur.getBundleId().equals(bundleId) &&
                     cur.getCurrentPlan().getProduct().getCategory() == ProductCategory.BASE) {
                 return buildSubscription((SubscriptionData) cur);
@@ -214,19 +221,19 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public void createNextPhaseEvent(UUID subscriptionId, EntitlementEvent nextPhase) {
+    public void createNextPhaseEvent(final UUID subscriptionId, final EntitlementEvent nextPhase) {
         cancelNextPhaseEvent(subscriptionId);
         insertEvent(nextPhase);
     }
 
 
 
-    private Subscription buildSubscription(SubscriptionData in) {
+    private Subscription buildSubscription(final SubscriptionData in) {
         return factory.createSubscription(new SubscriptionBuilder(in), getEventsForSubscription(in.getId()));
     }
 
     @Override
-    public void updateSubscription(SubscriptionData subscription) {
+    public void updateSubscription(final SubscriptionData subscription) {
 
         boolean found = false;
         Iterator<Subscription> it = subscriptions.iterator();
@@ -244,7 +251,7 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public void cancelSubscription(UUID subscriptionId, EntitlementEvent cancelEvent) {
+    public void cancelSubscription(final UUID subscriptionId, final EntitlementEvent cancelEvent) {
         synchronized (cancelEvent) {
             cancelNextPhaseEvent(subscriptionId);
             insertEvent(cancelEvent);
@@ -252,7 +259,7 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public void changePlan(UUID subscriptionId, List<EntitlementEvent> changeEvents) {
+    public void changePlan(final UUID subscriptionId, final List<EntitlementEvent> changeEvents) {
         synchronized(events) {
             cancelNextChangeEvent(subscriptionId);
             cancelNextPhaseEvent(subscriptionId);
@@ -280,7 +287,7 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
         }
     }
 
-    private void cancelNextPhaseEvent(UUID subscriptionId) {
+    private void cancelNextPhaseEvent(final UUID subscriptionId) {
 
         Subscription curSubscription = getSubscriptionFromId(subscriptionId);
         if (curSubscription.getCurrentPhase() == null ||
@@ -307,7 +314,7 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
 
-    private void cancelNextChangeEvent(UUID subscriptionId) {
+    private void cancelNextChangeEvent(final UUID subscriptionId) {
 
         synchronized(events) {
 
@@ -328,7 +335,7 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public void uncancelSubscription(UUID subscriptionId, List<EntitlementEvent> uncancelEvents) {
+    public void uncancelSubscription(final UUID subscriptionId, final List<EntitlementEvent> uncancelEvents) {
 
         synchronized (events) {
             boolean foundCancel = false;
@@ -346,7 +353,7 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
                 }
             }
             if (foundCancel) {
-                for (EntitlementEvent cur : uncancelEvents) {
+                for (final EntitlementEvent cur : uncancelEvents) {
                     insertEvent(cur);
                 }
             }
@@ -360,9 +367,9 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
 
             undoMigration(accountId);
 
-            for (BundleMigrationData curBundle : accountData.getData()) {
+            for (final BundleMigrationData curBundle : accountData.getData()) {
                 SubscriptionBundleData bundleData = curBundle.getData();
-                for (SubscriptionMigrationData curSubscription : curBundle.getSubscriptions()) {
+                for (final SubscriptionMigrationData curSubscription : curBundle.getSubscriptions()) {
                     SubscriptionData subData = curSubscription.getData();
                     for (final EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
                         events.add(curEvent);
@@ -382,15 +389,15 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public void undoMigration(UUID accountId) {
+    public void undoMigration(final UUID accountId) {
         synchronized(events) {
 
             List<SubscriptionBundle> allBundles = getSubscriptionBundleForAccount(accountId);
-            for (SubscriptionBundle bundle : allBundles) {
+            for (final SubscriptionBundle bundle : allBundles) {
                 List<Subscription> allSubscriptions = getSubscriptions(bundle.getId());
-                for (Subscription subscription : allSubscriptions) {
+                for (final Subscription subscription : allSubscriptions) {
                     List<EntitlementEvent> allEvents = getEventsForSubscription(subscription.getId());
-                    for (EntitlementEvent event : allEvents) {
+                    for (final EntitlementEvent event : allEvents) {
                         events.remove(event);
                     }
                     subscriptions.remove(subscription);
@@ -402,9 +409,9 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
-    public EntitlementEvent getEventById(UUID eventId) {
+    public EntitlementEvent getEventById(final UUID eventId) {
         synchronized(events) {
-            for (EntitlementEvent cur : events) {
+            for (final EntitlementEvent cur : events) {
                 if (cur.getId().equals(eventId)) {
                     return cur;
                 }

invoice/pom.xml 5(+5 -0)

diff --git a/invoice/pom.xml b/invoice/pom.xml
index 2fb5e33..3cc522b 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -21,6 +21,11 @@
     <packaging>jar</packaging>
     <dependencies>
         <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>com.ning.billing</groupId>
             <artifactId>killbill-api</artifactId>
         </dependency>
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java
index fac63ba..835fce9 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java
@@ -31,27 +31,28 @@ public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
     private final InvoiceDao dao;
 
     @Inject
-    public DefaultInvoicePaymentApi(InvoiceDao dao) {
+    public DefaultInvoicePaymentApi(final InvoiceDao dao) {
         this.dao = dao;
     }
 
     @Override
-    public void paymentSuccessful(UUID invoiceId, BigDecimal amount, Currency currency, UUID paymentId, DateTime paymentAttemptDate) {
-        dao.notifySuccessfulPayment(invoiceId.toString(), amount, currency.toString(), paymentId.toString(), paymentAttemptDate.toDate());
+    public void paymentSuccessful(final UUID invoiceId, final BigDecimal amount, final Currency currency,
+                                  final UUID paymentId, final DateTime paymentAttemptDate) {
+        dao.notifySuccessfulPayment(invoiceId, amount, currency, paymentId, paymentAttemptDate);
     }
 
     @Override
-    public void paymentFailed(UUID invoiceId, UUID paymentId, DateTime paymentAttemptDate) {
-        dao.notifyFailedPayment(invoiceId.toString(), paymentId.toString(), paymentAttemptDate.toDate());
+    public void paymentFailed(final UUID invoiceId, final UUID paymentId, final DateTime paymentAttemptDate) {
+        dao.notifyFailedPayment(invoiceId, paymentId, paymentAttemptDate);
     }
 
     @Override
-    public List<Invoice> getInvoicesByAccount(UUID accountId) {
-        return dao.getInvoicesByAccount(accountId.toString());
+    public List<Invoice> getInvoicesByAccount(final UUID accountId) {
+        return dao.getInvoicesByAccount(accountId);
     }
 
     @Override
-    public Invoice getInvoice(UUID invoiceId) {
-        return dao.getById(invoiceId.toString());
+    public Invoice getInvoice(final UUID invoiceId) {
+        return dao.getById(invoiceId);
     }
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
index d9e29f0..d1f795e 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -16,50 +16,58 @@
 
 package com.ning.billing.invoice.api.user;
 
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+import org.joda.time.DateTime;
 import com.google.inject.Inject;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoiceUserApi;
-import com.ning.billing.invoice.dao.DefaultInvoiceDao;
 import com.ning.billing.invoice.dao.InvoiceDao;
-import com.ning.billing.util.eventbus.Bus;
-import org.joda.time.DateTime;
-import org.skife.jdbi.v2.IDBI;
-
-import java.math.BigDecimal;
-import java.util.List;
-import java.util.UUID;
 
 public class DefaultInvoiceUserApi implements InvoiceUserApi {
     private final InvoiceDao dao;
 
     @Inject
-    public DefaultInvoiceUserApi(InvoiceDao dao) {
+    public DefaultInvoiceUserApi(final InvoiceDao dao) {
         this.dao = dao;
     }
 
     @Override
-    public List<UUID> getInvoicesForPayment(DateTime targetDate, int numberOfDays) {
-        return dao.getInvoicesForPayment(targetDate.toDate(), numberOfDays);
+    public List<UUID> getInvoicesForPayment(final DateTime targetDate, final int numberOfDays) {
+        return dao.getInvoicesForPayment(targetDate, numberOfDays);
+    }
+
+    @Override
+    public List<Invoice> getInvoicesByAccount(final UUID accountId) {
+        return dao.getInvoicesByAccount(accountId);
+    }
+
+    @Override
+    public List<Invoice> getInvoicesByAccount(final UUID accountId, final DateTime fromDate) {
+        return dao.getInvoicesByAccount(accountId, fromDate);
     }
 
     @Override
-    public List<Invoice> getInvoicesByAccount(UUID accountId) {
-        return dao.getInvoicesByAccount(accountId.toString());
+    public List<InvoiceItem> getInvoiceItemsByAccount(final UUID accountId) {
+        return dao.getInvoiceItemsByAccount(accountId);
     }
 
     @Override
-    public Invoice getInvoice(UUID invoiceId) {
-        return dao.getById(invoiceId.toString());
+    public Invoice getInvoice(final UUID invoiceId) {
+        return dao.getById(invoiceId);
     }
 
     @Override
-    public void paymentAttemptFailed(UUID invoiceId, UUID paymentId, DateTime paymentAttemptDate) {
-        dao.notifyFailedPayment(invoiceId.toString(), paymentId.toString(), paymentAttemptDate.toDate());
+    public void paymentAttemptFailed(final UUID invoiceId, final UUID paymentId, final DateTime paymentAttemptDate) {
+        dao.notifyFailedPayment(invoiceId, paymentId, paymentAttemptDate);
     }
 
     @Override
-    public void paymentAttemptSuccessful(UUID invoiceId, BigDecimal amount, Currency currency, UUID paymentId, DateTime paymentDate) {
-        dao.notifySuccessfulPayment(invoiceId.toString(), amount, currency.toString(), paymentId.toString(), paymentDate.toDate());
+    public void paymentAttemptSuccessful(final UUID invoiceId, final BigDecimal amount, final Currency currency,
+                                         final UUID paymentId, final DateTime paymentDate) {
+        dao.notifySuccessfulPayment(invoiceId, amount, currency, paymentId, paymentDate);
     }
 }
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 8612eb4..d4415ef 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
@@ -16,54 +16,80 @@
 
 package com.ning.billing.invoice.dao;
 
-import sun.reflect.generics.reflectiveObjects.NotImplementedException;
-
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.IDBI;
+import org.skife.jdbi.v2.Transaction;
+import org.skife.jdbi.v2.TransactionStatus;
 import com.google.inject.Inject;
+import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceCreationNotification;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.invoice.api.user.DefaultInvoiceCreationNotification;
 import com.ning.billing.util.eventbus.Bus;
-import org.skife.jdbi.v2.IDBI;
-import org.skife.jdbi.v2.Transaction;
-import org.skife.jdbi.v2.TransactionStatus;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.math.BigDecimal;
-import java.util.Date;
-import java.util.List;
-import java.util.UUID;
 
 public class DefaultInvoiceDao implements InvoiceDao {
-    private final InvoiceSqlDao invoiceDao;
+    private final InvoiceSqlDao invoiceSqlDao;
+    private final InvoiceItemSqlDao invoiceItemSqlDao;
 
     private final Bus eventBus;
-    private final static Logger log = LoggerFactory.getLogger(DefaultInvoiceDao.class);
 
     @Inject
     public DefaultInvoiceDao(final IDBI dbi, final Bus eventBus) {
-        this.invoiceDao = dbi.onDemand(InvoiceSqlDao.class);
+        this.invoiceSqlDao = dbi.onDemand(InvoiceSqlDao.class);
+        this.invoiceItemSqlDao = dbi.onDemand(InvoiceItemSqlDao.class);
         this.eventBus = eventBus;
     }
 
     @Override
-    public List<Invoice> getInvoicesByAccount(final String accountId) {
-        return invoiceDao.getInvoicesByAccount(accountId);
+    public List<Invoice> getInvoicesByAccount(final UUID accountId) {
+        return invoiceSqlDao.inTransaction(new Transaction<List<Invoice>, InvoiceSqlDao>() {
+            @Override
+            public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
+                List<Invoice> invoices = invoiceDao.getInvoicesByAccount(accountId.toString());
+
+                getInvoiceItemsWithinTransaction(invoices, invoiceDao);
+
+                getInvoicePaymentsWithinTransaction(invoices, invoiceDao);
+
+                return invoices;
+            }
+        });
+    }
+
+    @Override
+    public List<Invoice> getInvoicesByAccount(final UUID accountId, final DateTime fromDate) {
+        return invoiceSqlDao.inTransaction(new Transaction<List<Invoice>, InvoiceSqlDao>() {
+            @Override
+            public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
+                List<Invoice> invoices = invoiceDao.getInvoicesByAccountAfterDate(accountId.toString(), fromDate.toDate());
+
+                getInvoiceItemsWithinTransaction(invoices, invoiceDao);
+                getInvoicePaymentsWithinTransaction(invoices, invoiceDao);
+
+                return invoices;
+            }
+        });
+    }
+
+    @Override
+    public List<InvoiceItem> getInvoiceItemsByAccount(final UUID accountId) {
+        return invoiceItemSqlDao.getInvoiceItemsByAccount(accountId.toString());
     }
 
     @Override
     public List<Invoice> get() {
-        return invoiceDao.inTransaction(new Transaction<List<Invoice>, InvoiceSqlDao>() {
+        return invoiceSqlDao.inTransaction(new Transaction<List<Invoice>, InvoiceSqlDao>() {
              @Override
              public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
                  List<Invoice> invoices = invoiceDao.get();
 
-                 InvoiceItemSqlDao invoiceItemDao = invoiceDao.become(InvoiceItemSqlDao.class);
-                 for (Invoice invoice : invoices) {
-                     List<InvoiceItem> invoiceItems = invoiceItemDao.getInvoiceItemsByInvoice(invoice.getId().toString());
-                     invoice.add(invoiceItems);
-                 }
+                 getInvoiceItemsWithinTransaction(invoices, invoiceDao);
+                 getInvoicePaymentsWithinTransaction(invoices, invoiceDao);
 
                  return invoices;
              }
@@ -71,16 +97,20 @@ public class DefaultInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public Invoice getById(final String invoiceId) {
-        return invoiceDao.inTransaction(new Transaction<Invoice, InvoiceSqlDao>() {
+    public Invoice getById(final UUID invoiceId) {
+        return invoiceSqlDao.inTransaction(new Transaction<Invoice, InvoiceSqlDao>() {
              @Override
              public Invoice inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
-                 Invoice invoice = invoiceDao.getById(invoiceId);
+                 Invoice invoice = invoiceDao.getById(invoiceId.toString());
 
                  if (invoice != null) {
                      InvoiceItemSqlDao invoiceItemDao = invoiceDao.become(InvoiceItemSqlDao.class);
-                     List<InvoiceItem> invoiceItems = invoiceItemDao.getInvoiceItemsByInvoice(invoiceId);
-                     invoice.add(invoiceItems);
+                     List<InvoiceItem> invoiceItems = invoiceItemDao.getInvoiceItemsByInvoice(invoiceId.toString());
+                     invoice.addInvoiceItems(invoiceItems);
+
+                     InvoicePaymentSqlDao invoicePaymentSqlDao = invoiceDao.become(InvoicePaymentSqlDao.class);
+                     List<InvoicePayment> invoicePayments = invoicePaymentSqlDao.getPaymentsForInvoice(invoiceId.toString());
+                     invoice.addPayments(invoicePayments);
                  }
 
                  return invoice;
@@ -90,65 +120,85 @@ public class DefaultInvoiceDao implements InvoiceDao {
 
     @Override
     public void create(final Invoice invoice) {
-         invoiceDao.inTransaction(new Transaction<Void, InvoiceSqlDao>() {
-             @Override
-             public Void inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
+        invoiceSqlDao.inTransaction(new Transaction<Void, InvoiceSqlDao>() {
+            @Override
+            public Void inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
                 Invoice currentInvoice = invoiceDao.getById(invoice.getId().toString());
 
                 if (currentInvoice == null) {
                     invoiceDao.create(invoice);
 
-                    List<InvoiceItem> invoiceItems = invoice.getItems();
+                    List<InvoiceItem> invoiceItems = invoice.getInvoiceItems();
                     InvoiceItemSqlDao invoiceItemDao = invoiceDao.become(InvoiceItemSqlDao.class);
                     invoiceItemDao.create(invoiceItems);
 
+                    List<InvoicePayment> invoicePayments = invoice.getPayments();
+                    InvoicePaymentSqlDao invoicePaymentSqlDao = invoiceDao.become(InvoicePaymentSqlDao.class);
+                    invoicePaymentSqlDao.create(invoicePayments);
+
                     InvoiceCreationNotification event;
                     event = new DefaultInvoiceCreationNotification(invoice.getId(), invoice.getAccountId(),
-                                                                  invoice.getAmountOutstanding(), invoice.getCurrency(),
+                                                                  invoice.getBalance(), invoice.getCurrency(),
                                                                   invoice.getInvoiceDate());
                     eventBus.post(event);
                 }
 
                 return null;
-             }
-         });
+            }
+        });
     }
 
     @Override
-    public List<Invoice> getInvoicesBySubscription(final String subscriptionId) {
-        return invoiceDao.inTransaction(new Transaction<List<Invoice>, InvoiceSqlDao>() {
-             @Override
-             public List<Invoice> inTransaction(InvoiceSqlDao invoiceDao, TransactionStatus status) throws Exception {
-                 List<Invoice> invoices = invoiceDao.getInvoicesBySubscription(subscriptionId);
+    public List<Invoice> getInvoicesBySubscription(final UUID subscriptionId) {
+        return invoiceSqlDao.inTransaction(new Transaction<List<Invoice>, InvoiceSqlDao>() {
+            @Override
+            public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
+                List<Invoice> invoices = invoiceDao.getInvoicesBySubscription(subscriptionId.toString());
 
-                 InvoiceItemSqlDao invoiceItemDao = invoiceDao.become(InvoiceItemSqlDao.class);
-                 for (Invoice invoice : invoices) {
-                     List<InvoiceItem> invoiceItems = invoiceItemDao.getInvoiceItemsByInvoice(invoice.getId().toString());
-                     invoice.add(invoiceItems);
-                 }
+                getInvoiceItemsWithinTransaction(invoices, invoiceDao);
+                getInvoicePaymentsWithinTransaction(invoices, invoiceDao);
 
-                 return invoices;
-             }
+                return invoices;
+            }
         });
     }
 
     @Override
-    public List<UUID> getInvoicesForPayment(Date targetDate, int numberOfDays) {
-        return invoiceDao.getInvoicesForPayment(targetDate, numberOfDays);
+    public List<UUID> getInvoicesForPayment(final DateTime targetDate, final int numberOfDays) {
+        return invoiceSqlDao.getInvoicesForPayment(targetDate.toDate(), numberOfDays);
     }
 
     @Override
-    public void notifySuccessfulPayment(String invoiceId, BigDecimal paymentAmount, String currency, String paymentId, Date paymentDate) {
-        invoiceDao.notifySuccessfulPayment(invoiceId, paymentAmount, currency, paymentId, paymentDate);
+    public void notifySuccessfulPayment(final UUID invoiceId, final BigDecimal paymentAmount,
+                                        final Currency currency, final UUID paymentId, final DateTime paymentDate) {
+        invoiceSqlDao.notifySuccessfulPayment(invoiceId.toString(), paymentAmount, currency.toString(),
+                                              paymentId.toString(), paymentDate.toDate());
     }
 
     @Override
-    public void notifyFailedPayment(String invoiceId, String paymentId, Date paymentAttemptDate) {
-        invoiceDao.notifyFailedPayment(invoiceId, paymentId, paymentAttemptDate);
+    public void notifyFailedPayment(final UUID invoiceId, final UUID paymentId, final DateTime paymentAttemptDate) {
+        invoiceSqlDao.notifyFailedPayment(invoiceId.toString(), paymentId.toString(), paymentAttemptDate.toDate());
     }
 
     @Override
     public void test() {
-        invoiceDao.test();
+        invoiceSqlDao.test();
+    }
+
+    private void getInvoiceItemsWithinTransaction(final List<Invoice> invoices, final InvoiceSqlDao invoiceDao) {
+        InvoiceItemSqlDao invoiceItemDao = invoiceDao.become(InvoiceItemSqlDao.class);
+        for (final Invoice invoice : invoices) {
+            List<InvoiceItem> invoiceItems = invoiceItemDao.getInvoiceItemsByInvoice(invoice.getId().toString());
+            invoice.addInvoiceItems(invoiceItems);
+        }
+    }
+
+    private void getInvoicePaymentsWithinTransaction(final List<Invoice> invoices, final InvoiceSqlDao invoiceDao) {
+        InvoicePaymentSqlDao invoicePaymentSqlDao = invoiceDao.become(InvoicePaymentSqlDao.class);
+        for (final Invoice invoice : invoices) {
+            String invoiceId = invoice.getId().toString();
+            List<InvoicePayment> invoicePayments = invoicePaymentSqlDao.getPaymentsForInvoice(invoiceId);
+            invoice.addPayments(invoicePayments);
+        }
     }
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
index e9306b2..77f12fe 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
@@ -17,34 +17,40 @@
 package com.ning.billing.invoice.dao;
 
 import java.math.BigDecimal;
-import java.util.Date;
 import java.util.List;
 import java.util.UUID;
+import org.joda.time.DateTime;
+import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
 
 public interface InvoiceDao {
     void create(Invoice invoice);
 
-    Invoice getById(final String id);
+    Invoice getById(final UUID id);
 
     List<Invoice> get();
 
-    List<Invoice> getInvoicesByAccount(final String accountId);
+    List<Invoice> getInvoicesByAccount(final UUID accountId);
 
-    List<Invoice> getInvoicesBySubscription(final String subscriptionId);
+    List<Invoice> getInvoicesByAccount(final UUID accountId, final DateTime fromDate);
 
-    List<UUID> getInvoicesForPayment(final Date targetDate,
+    List<InvoiceItem> getInvoiceItemsByAccount(final UUID accountId);
+
+    List<Invoice> getInvoicesBySubscription(final UUID subscriptionId);
+
+    List<UUID> getInvoicesForPayment(final DateTime targetDate,
                                      final int numberOfDays);
 
-    void notifySuccessfulPayment(final String invoiceId,
+    void notifySuccessfulPayment(final UUID invoiceId,
                                  final BigDecimal paymentAmount,
-                                 final String currency,
-                                 final String paymentId,
-                                 final Date paymentDate);
+                                 final Currency currency,
+                                 final UUID paymentId,
+                                 final DateTime paymentDate);
 
-    void notifyFailedPayment(final String invoiceId,
-                             final String paymentId,
-                             final Date paymentAttemptDate);
+    void notifyFailedPayment(final UUID invoiceId,
+                             final UUID paymentId,
+                             final DateTime paymentAttemptDate);
 
     void test();
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
new file mode 100644
index 0000000..2efc98d
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.invoice.dao;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.math.BigDecimal;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.UUID;
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BinderFactory;
+import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+import org.skife.jdbi.v2.sqlobject.SqlBatch;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.util.entity.EntityDao;
+
+@ExternalizedSqlViaStringTemplate3
+@RegisterMapper(InvoicePaymentSqlDao.InvoicePaymentMapper.class)
+public interface InvoicePaymentSqlDao extends EntityDao<InvoicePayment> {
+    @Override
+    @SqlUpdate
+    public void create(@InvoicePaymentBinder final InvoicePayment invoicePayment);
+
+    @SqlBatch
+    void create(@InvoicePaymentBinder final List<InvoicePayment> items);
+
+    @Override
+    @SqlUpdate
+    public void update(@InvoicePaymentBinder final InvoicePayment invoicePayment);
+
+    @SqlQuery
+    public List<InvoicePayment> getPaymentsForInvoice(@Bind("invoiceId") final String invoiceId);
+
+    public static class InvoicePaymentMapper implements ResultSetMapper<InvoicePayment> {
+        @Override
+        public InvoicePayment map(int index, ResultSet result, StatementContext context) throws SQLException {
+            final UUID id = UUID.fromString(result.getString("payment_id"));
+            final UUID invoiceId = UUID.fromString(result.getString("invoice_id"));
+            final DateTime paymentDate = new DateTime(result.getTimestamp("payment_date"));
+            final BigDecimal amount = result.getBigDecimal("amount");
+            final String currencyString = result.getString("currency");
+            final Currency currency = (currencyString == null) ? null : Currency.valueOf(currencyString);
+
+            return new InvoicePayment() {
+                @Override
+                public UUID getId() {
+                    return id;
+                }
+                @Override
+                public UUID getInvoiceId() {
+                    return invoiceId;
+                }
+                @Override
+                public DateTime getPaymentDate() {
+                    return paymentDate;
+                }
+                @Override
+                public BigDecimal getAmount() {
+                    return amount;
+                }
+                @Override
+                public Currency getCurrency() {
+                    return currency;
+                }
+            };
+        }
+    }
+
+    @BindingAnnotation(InvoicePaymentBinder.InvoicePaymentBinderFactory.class)
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.PARAMETER})
+    public @interface InvoicePaymentBinder {
+        public static class InvoicePaymentBinderFactory implements BinderFactory {
+            public Binder build(Annotation annotation) {
+                return new Binder<InvoicePaymentBinder, InvoicePayment>() {
+                    public void bind(SQLStatement q, InvoicePaymentBinder bind, InvoicePayment payment) {
+                        q.bind("invoiceId", payment.getInvoiceId().toString());
+                        q.bind("paymentId", payment.getId().toString());
+                        q.bind("paymentDate", payment.getAmount());
+                        q.bind("amount", payment.getAmount());
+                        Currency currency = payment.getCurrency();
+                        q.bind("currency", (currency == null) ? null : currency.toString());
+                    }
+                };
+            }
+        }
+    }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
index 9ede145..3a5ed2a 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
@@ -19,6 +19,7 @@ package com.ning.billing.invoice.dao;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.util.UuidMapper;
 import com.ning.billing.util.entity.EntityDao;
@@ -63,6 +64,10 @@ public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<Invoice
     List<Invoice> getInvoicesByAccount(@Bind("accountId") final String accountId);
 
     @SqlQuery
+    List<Invoice> getInvoicesByAccountAfterDate(@Bind("accountId") final String accountId,
+                                                @Bind("fromDate") final Date fromDate);
+
+    @SqlQuery
     List<Invoice> getInvoicesBySubscription(@Bind("subscriptionId") final String subscriptionId);
 
     @SqlQuery
@@ -93,10 +98,6 @@ public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<Invoice
                         q.bind("accountId", invoice.getAccountId().toString());
                         q.bind("invoiceDate", invoice.getInvoiceDate().toDate());
                         q.bind("targetDate", invoice.getTargetDate().toDate());
-                        q.bind("amountPaid", invoice.getAmountPaid());
-                        q.bind("amountOutstanding", invoice.getAmountOutstanding());
-                        DateTime last_payment_date = invoice.getLastPaymentAttempt();
-                        q.bind("lastPaymentAttempt", last_payment_date == null ? null : last_payment_date.toDate());
                         q.bind("currency", invoice.getCurrency().toString());
                     }
                 };
@@ -111,15 +112,9 @@ public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<Invoice
             UUID accountId = UUID.fromString(result.getString("account_id"));
             DateTime invoiceDate = new DateTime(result.getTimestamp("invoice_date"));
             DateTime targetDate = new DateTime(result.getTimestamp("target_date"));
-            BigDecimal amountPaid = result.getBigDecimal("amount_paid");
-            if (amountPaid == null) {
-                amountPaid = BigDecimal.ZERO;
-            }
-            Timestamp lastPaymentAttemptTimeStamp = result.getTimestamp("last_payment_attempt");
-            DateTime lastPaymentAttempt = lastPaymentAttemptTimeStamp == null ? null : new DateTime(lastPaymentAttemptTimeStamp);
             Currency currency = Currency.valueOf(result.getString("currency"));
 
-            return new DefaultInvoice(id, accountId, invoiceDate, targetDate, currency, lastPaymentAttempt, amountPaid, new ArrayList<InvoiceItem>());
+            return new DefaultInvoice(id, accountId, invoiceDate, targetDate, currency);
         }
     }
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
new file mode 100644
index 0000000..1a1cd13
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
@@ -0,0 +1,108 @@
+/*
+ * 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;
+
+import java.util.SortedSet;
+import java.util.UUID;
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.google.common.eventbus.Subscribe;
+import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.entitlement.api.billing.BillingEvent;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.invoice.api.BillingEventSet;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.invoice.model.InvoiceGenerator;
+import com.ning.billing.invoice.model.InvoiceItemList;
+
+public class InvoiceListener {
+    private final static Logger log = LoggerFactory.getLogger(InvoiceListener.class);
+
+    private final InvoiceGenerator generator;
+    private final EntitlementBillingApi entitlementBillingApi;
+    private final AccountUserApi accountUserApi;
+    private final InvoiceUserApi invoiceUserApi;
+    private final InvoiceDao invoiceDao;
+
+    @Inject
+    public InvoiceListener(final InvoiceGenerator generator, final AccountUserApi accountUserApi,
+                           final EntitlementBillingApi entitlementBillingApi,
+                           final InvoiceUserApi invoiceUserApi,
+                           final InvoiceDao invoiceDao) {
+        this.generator = generator;
+        this.entitlementBillingApi = entitlementBillingApi;
+        this.accountUserApi = accountUserApi;
+        this.invoiceUserApi = invoiceUserApi;
+        this.invoiceDao = invoiceDao;
+    }
+
+    @Subscribe
+    public void handleSubscriptionTransition(final SubscriptionTransition transition) {
+        UUID subscriptionId = transition.getSubscriptionId();
+        if (subscriptionId == null) {
+            log.error("Failed handling entitlement change.", new InvoiceApiException(ErrorCode.INVOICE_INVALID_TRANSITION));
+            return;
+        }
+
+        UUID accountId = null;
+        try {
+            accountId = entitlementBillingApi.getAccountIdFromSubscriptionId(subscriptionId);
+        } catch (EntitlementBillingApiException e) {
+            log.error("Failed handling entitlement change.", e);
+            return;
+        }
+
+        if (accountId == null) {
+            log.error("Failed handling entitlement change.",
+                      new InvoiceApiException(ErrorCode.INVOICE_NO_ACCOUNT_ID_FOR_SUBSCRIPTION_ID, subscriptionId.toString()));
+            return;
+        }
+
+        Account account = accountUserApi.getAccountById(accountId);
+        if (account == null) {
+            log.error("Failed handling entitlement change.",
+                      new InvoiceApiException(ErrorCode.INVOICE_ACCOUNT_ID_INVALID, accountId.toString()));
+            return;
+        }
+
+        SortedSet<BillingEvent> events = entitlementBillingApi.getBillingEventsForAccount(accountId);
+        BillingEventSet billingEvents = new BillingEventSet();
+        billingEvents.addAll(events);
+
+        DateTime targetDate = new DateTime();
+        Currency targetCurrency = account.getCurrency();
+
+        InvoiceItemList invoiceItemList = (InvoiceItemList) invoiceUserApi.getInvoiceItemsByAccount(accountId);
+        Invoice invoice = generator.generateInvoice(accountId, billingEvents, invoiceItemList, targetDate, targetCurrency);
+
+        if (invoice != null) {
+            if (invoice.getNumberOfItems() > 0) {
+                invoiceDao.create(invoice);
+            }
+        }
+    }
+}
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 97f82e0..ae10d41 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
@@ -19,6 +19,7 @@ package com.ning.billing.invoice.model;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.util.clock.DefaultClock;
 import org.joda.time.DateTime;
 
@@ -28,55 +29,65 @@ import java.util.List;
 import java.util.UUID;
 
 public class DefaultInvoice implements Invoice {
-    private final InvoiceItemList items = new InvoiceItemList();
+    private final InvoiceItemList invoiceItems = new InvoiceItemList();
+    private final List<InvoicePayment> payments = new ArrayList<InvoicePayment>();
     private final UUID id;
     private UUID accountId;
     private final DateTime invoiceDate;
     private final DateTime targetDate;
     private Currency currency;
-    private BigDecimal amountPaid;
-    private DateTime lastPaymentAttempt;
 
     public DefaultInvoice(UUID accountId, DateTime targetDate, Currency currency) {
-        this(UUID.randomUUID(), accountId, new DefaultClock().getUTCNow(), targetDate, currency, null, BigDecimal.ZERO, new ArrayList<InvoiceItem>());
+        this(UUID.randomUUID(), accountId, new DefaultClock().getUTCNow(), targetDate, currency);
     }
 
     public DefaultInvoice(UUID invoiceId, UUID accountId, DateTime invoiceDate, DateTime targetDate,
-                          Currency currency, DateTime lastPaymentAttempt, BigDecimal amountPaid) {
-        this(invoiceId, accountId, invoiceDate, targetDate, currency, lastPaymentAttempt, amountPaid, new ArrayList<InvoiceItem>());
-    }
-
-    public DefaultInvoice(UUID invoiceId, UUID accountId, DateTime invoiceDate, DateTime targetDate,
-                          Currency currency, DateTime lastPaymentAttempt, BigDecimal amountPaid,
-                          List<InvoiceItem> invoiceItems) {
+                          Currency currency) {
         this.id = invoiceId;
         this.accountId = accountId;
         this.invoiceDate = invoiceDate;
         this.targetDate = targetDate;
         this.currency = currency;
-        this.lastPaymentAttempt= lastPaymentAttempt;
-        this.amountPaid = amountPaid;
-        this.items.addAll(invoiceItems);
     }
 
     @Override
-    public boolean add(InvoiceItem item) {
-        return items.add(item);
+    public boolean addInvoiceItem(final InvoiceItem item) {
+        return invoiceItems.add(item);
     }
 
     @Override
-    public boolean add(List<InvoiceItem> items) {
-        return this.items.addAll(items);
+    public boolean addInvoiceItems(final List<InvoiceItem> items) {
+        return this.invoiceItems.addAll(items);
     }
 
     @Override
-    public List<InvoiceItem> getItems() {
-        return items;
+    public List<InvoiceItem> getInvoiceItems() {
+        return invoiceItems;
     }
 
     @Override
     public int getNumberOfItems() {
-        return items.size();
+        return invoiceItems.size();
+    }
+
+    @Override
+    public boolean addPayment(final InvoicePayment payment) {
+        return payments.add(payment);
+    }
+
+    @Override
+    public boolean addPayments(final List<InvoicePayment> payments) {
+        return this.payments.addAll(payments);
+    }
+
+    @Override
+    public List<InvoicePayment> getPayments() {
+        return payments;
+    }
+
+    @Override
+    public int getNumberOfPayments() {
+        return payments.size();
     }
 
     @Override
@@ -106,21 +117,40 @@ public class DefaultInvoice implements Invoice {
 
     @Override
     public DateTime getLastPaymentAttempt() {
+        DateTime lastPaymentAttempt = null;
+
+        for (final InvoicePayment paymentAttempt : payments) {
+            DateTime paymentAttemptDate = paymentAttempt.getPaymentDate();
+            if (lastPaymentAttempt == null) {
+                lastPaymentAttempt = paymentAttemptDate;
+            }
+
+            if (lastPaymentAttempt.isBefore(paymentAttemptDate)) {
+                lastPaymentAttempt = paymentAttemptDate;
+            }
+        }
+
         return lastPaymentAttempt;
     }
 
     @Override
     public BigDecimal getAmountPaid() {
+        BigDecimal amountPaid = BigDecimal.ZERO;
+        for (final InvoicePayment payment : payments) {
+            if (payment.getAmount() != null) {
+                amountPaid = amountPaid.add(payment.getAmount());
+            }
+        }
         return amountPaid;
     }
 
     @Override
     public BigDecimal getTotalAmount() {
-        return items.getTotalAmount();
+        return invoiceItems.getTotalAmount();
     }
 
     @Override
-    public BigDecimal getAmountOutstanding() {
+    public BigDecimal getBalance() {
         return getTotalAmount().subtract(getAmountPaid());
     }
 
@@ -130,6 +160,7 @@ public class DefaultInvoice implements Invoice {
             return false;
         }
 
+        DateTime lastPaymentAttempt = getLastPaymentAttempt();
         if (lastPaymentAttempt == null) {
             return true;
         }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
index 3b9311e..cb30d15 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
@@ -38,23 +38,27 @@ import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
 
 public class DefaultInvoiceGenerator implements InvoiceGenerator {
-    private static final Logger log = LoggerFactory.getLogger(DefaultInvoiceGenerator.class); 
+    private static final Logger log = LoggerFactory.getLogger(DefaultInvoiceGenerator.class);
+
     @Override
-    public Invoice generateInvoice(final UUID accountId, final BillingEventSet events, final InvoiceItemList existingItems, final DateTime targetDate, final Currency targetCurrency) {
+    public Invoice generateInvoice(final UUID accountId, final BillingEventSet events,
+                                   final InvoiceItemList existingItems, final DateTime targetDate,
+                                   final Currency targetCurrency) {
         if (events == null) {return new DefaultInvoice(accountId, targetDate, targetCurrency);}
         if (events.size() == 0) {return new DefaultInvoice(accountId, targetDate, targetCurrency);}
 
         DefaultInvoice invoice = new DefaultInvoice(accountId, targetDate, targetCurrency);
         InvoiceItemList currentItems = generateInvoiceItems(events, invoice.getId(), targetDate, targetCurrency);
         InvoiceItemList itemsToPost = reconcileInvoiceItems(invoice.getId(), currentItems, existingItems);
-        invoice.add(itemsToPost);
+        invoice.addInvoiceItems(itemsToPost);
 
         return invoice;
     }
 
-    private InvoiceItemList reconcileInvoiceItems(final UUID invoiceId, final InvoiceItemList currentInvoiceItems, final InvoiceItemList existingInvoiceItems) {
+    private InvoiceItemList reconcileInvoiceItems(final UUID invoiceId, final InvoiceItemList currentInvoiceItems,
+                                                  final InvoiceItemList existingInvoiceItems) {
         InvoiceItemList currentItems = new InvoiceItemList();
-        for (InvoiceItem item : currentInvoiceItems) {
+        for (final InvoiceItem item : currentInvoiceItems) {
             currentItems.add(new DefaultInvoiceItem(item, invoiceId));
         }
 
@@ -65,9 +69,9 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
 
         List<InvoiceItem> existingItemsToRemove = new ArrayList<InvoiceItem>();
 
-        for (InvoiceItem currentItem : currentItems) {
+        for (final InvoiceItem currentItem : currentItems) {
             // see if there are any existing items that are covered by the current item
-            for (InvoiceItem existingItem : existingItems) {
+            for (final InvoiceItem existingItem : existingItems) {
                 if (currentItem.duplicates(existingItem)) {
                     currentItem.subtract(existingItem);
                     existingItemsToRemove.add(existingItem);
@@ -84,14 +88,15 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
         currentItems.removeZeroDollarItems();
 
         // add existing items that aren't covered by current items as credit items
-        for (InvoiceItem existingItem : existingItems) {
+        for (final InvoiceItem existingItem : existingItems) {
             currentItems.add(existingItem.asCredit(invoiceId));
         }
 
         return currentItems;
     }
 
-    private InvoiceItemList generateInvoiceItems(BillingEventSet events, UUID invoiceId, DateTime targetDate, Currency targetCurrency) {
+    private InvoiceItemList generateInvoiceItems(final BillingEventSet events, final UUID invoiceId,
+                                                 final DateTime targetDate, final Currency targetCurrency) {
         InvoiceItemList items = new InvoiceItemList();
 
         // sort events; this relies on the sort order being by subscription id then start date
@@ -118,7 +123,8 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
         return items;
     }
 
-    private void processEvent(UUID invoiceId, BillingEvent event, List<InvoiceItem> items, DateTime targetDate, Currency targetCurrency) {
+    private void processEvent(final UUID invoiceId, final BillingEvent event, final List<InvoiceItem> items,
+                              final DateTime targetDate, final Currency targetCurrency) {
     	try {
     		//TODO: Jeff getPrice() -> getRecurringPrice()
     		BigDecimal rate = event.getRecurringPrice(targetCurrency);
@@ -134,7 +140,8 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
         }
     }
 
-    private void processEvents(UUID invoiceId, BillingEvent firstEvent, BillingEvent secondEvent, List<InvoiceItem> items, DateTime targetDate, Currency targetCurrency) {
+    private void processEvents(final UUID invoiceId, final BillingEvent firstEvent, final BillingEvent secondEvent,
+                               final List<InvoiceItem> items, final DateTime targetDate, final Currency targetCurrency) {
     	//TODO: Jeff getPrice() -> getRecurringPrice()
     	try {
     		BigDecimal rate = firstEvent.getRecurringPrice(targetCurrency);
@@ -150,14 +157,17 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
         }
     }
 
-    private void addInvoiceItem(UUID invoiceId, List<InvoiceItem> items, BillingEvent event, DateTime billThroughDate, BigDecimal amount, BigDecimal rate, Currency currency) {
+    private void addInvoiceItem(final UUID invoiceId, final List<InvoiceItem> items, final BillingEvent event,
+                                final DateTime billThroughDate, final BigDecimal amount, final BigDecimal rate,
+                                final Currency currency) {
         if (!(amount.compareTo(BigDecimal.ZERO) == 0)) {
             DefaultInvoiceItem item = new DefaultInvoiceItem(invoiceId, event.getSubscription().getId(), event.getEffectiveDate(), billThroughDate, event.getDescription(), amount, rate, currency);
             items.add(item);
         }
     }
 
-    private BigDecimal calculateInvoiceItemAmount(BillingEvent event, DateTime targetDate, BigDecimal rate){
+    private BigDecimal calculateInvoiceItemAmount(final BillingEvent event, final DateTime targetDate,
+                                                  final BigDecimal rate){
         BillingMode billingMode = getBillingMode(event.getBillingMode());
         DateTime startDate = event.getEffectiveDate();
         int billingCycleDay = event.getBillCycleDay();
@@ -173,7 +183,8 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
         }
     }
 
-    private BigDecimal calculateInvoiceItemAmount(BillingEvent firstEvent, BillingEvent secondEvent, DateTime targetDate, BigDecimal rate) {
+    private BigDecimal calculateInvoiceItemAmount(final BillingEvent firstEvent, final BillingEvent secondEvent,
+                                                  final DateTime targetDate, final BigDecimal rate) {
         BillingMode billingMode = getBillingMode(firstEvent.getBillingMode());
         DateTime startDate = firstEvent.getEffectiveDate();
         int billingCycleDay = firstEvent.getBillCycleDay();
@@ -191,7 +202,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
         }
     }
 
-    private BillingMode getBillingMode(BillingModeType billingModeType) {
+    private BillingMode getBillingMode(final BillingModeType billingModeType) {
         switch (billingModeType) {
             case IN_ADVANCE:
                 return new InAdvanceBillingMode();
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoicePayment.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoicePayment.java
new file mode 100644
index 0000000..ef1ed29
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoicePayment.java
@@ -0,0 +1,65 @@
+/*
+ * 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.model;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+import org.joda.time.DateTime;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.util.entity.EntityBase;
+
+public class DefaultInvoicePayment extends EntityBase<InvoicePayment> implements InvoicePayment {
+    private final UUID invoiceId;
+    private final DateTime paymentDate;
+    private final BigDecimal amount;
+    private final Currency currency;
+
+    public DefaultInvoicePayment(final UUID invoiceId, final DateTime paymentDate,
+                                 final BigDecimal amount, final Currency currency) {
+        this(UUID.randomUUID(), invoiceId, paymentDate, amount, currency);
+    }
+
+    public DefaultInvoicePayment(final UUID id, final UUID invoiceId, final DateTime paymentDate,
+                                 final BigDecimal amount, final Currency currency) {
+        super(id);
+        this.amount = amount;
+        this.invoiceId = invoiceId;
+        this.paymentDate = paymentDate;
+        this.currency = currency;
+    }
+
+    @Override
+    public UUID getInvoiceId() {
+        return invoiceId;
+    }
+
+    @Override
+    public DateTime getPaymentDate() {
+        return paymentDate;
+    }
+
+    @Override
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    @Override
+    public Currency getCurrency() {
+        return currency;
+    }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java
index fb82f06..2e462df 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java
@@ -16,11 +16,10 @@
 
 package com.ning.billing.invoice.model;
 
-import com.ning.billing.invoice.api.InvoiceItem;
-
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
+import com.ning.billing.invoice.api.InvoiceItem;
 
 public class InvoiceItemList extends ArrayList<InvoiceItem> {
     private static final int NUMBER_OF_DECIMALS = InvoicingConfiguration.getNumberOfDecimals();
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
new file mode 100644
index 0000000..ef91849
--- /dev/null
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
@@ -0,0 +1,34 @@
+group InvoicePayment;
+
+create() ::= <<
+  INSERT INTO invoice_payments(invoice_id, payment_id, payment_date, amount, currency)
+  VALUES(:invoiceId, :paymentId, :paymentDate, :amount, :currency)
+>>
+
+update() ::= <<
+  UPDATE invoice_payments
+  SET payment_date = :paymentDate, amount = :amount, currency = :currency
+  WHERE invoice_id = :invoiceId, payment_id = :paymentId
+>>
+
+getById() ::= <<
+  SELECT invoice_id, payment_id, payment_date, amount, currency
+  FROM invoice_payments
+  WHERE payment_id = :id
+>>
+
+get() ::= <<
+  SELECT invoice_id, payment_id, payment_date, amount, currency
+  FROM invoice_payments
+>>
+
+getPaymentsForInvoice() ::= <<
+  SELECT invoice_id, payment_id, payment_date, amount, currency
+  FROM invoice_payments
+  WHERE invoice_id = :invoiceId
+>>
+
+test() ::= <<
+  SELECT 1 FROM invoice_payments;
+>>
+;
\ No newline at end of file
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
index 8766e18..f0b8116 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
@@ -1,8 +1,7 @@
 group InvoiceDao;
 
 get() ::= <<
-  SELECT i.id, i.account_id, i.invoice_date, i.target_date, i.currency, SUM(ii.amount) AS amount,
-         SUM(ip.amount) AS amount_paid, MAX(ip.payment_date) AS last_payment_attempt
+  SELECT i.id, i.account_id, i.invoice_date, i.target_date, i.currency, SUM(ii.amount) AS amount
   FROM invoices i
   LEFT JOIN invoice_payments ip ON ip.invoice_id = i.id
   LEFT JOIN invoice_items ii ON ii.invoice_id = i.id
@@ -11,8 +10,7 @@ get() ::= <<
 >>
 
 getInvoicesByAccount() ::= <<
-  SELECT i.id, i.account_id, i.invoice_date, i.target_date, i.currency, SUM(ii.amount) AS amount,
-         SUM(ip.amount) AS amount_paid, MAX(ip.payment_date) AS last_payment_attempt
+  SELECT i.id, i.account_id, i.invoice_date, i.target_date, i.currency, SUM(ii.amount) AS amount
   FROM invoices i
   LEFT JOIN invoice_payments ip ON ip.invoice_id = i.id
   LEFT JOIN invoice_items ii ON ii.invoice_id = i.id
@@ -21,9 +19,19 @@ getInvoicesByAccount() ::= <<
   ORDER BY i.invoice_date ASC;
 >>
 
+getInvoicesByAccountAfterDate() ::= <<
+  SELECT i.id, i.account_id, i.invoice_date, i.target_date, i.currency, SUM(ii.amount) AS amount
+  FROM invoices i
+  LEFT JOIN invoice_payments ip ON ip.invoice_id = i.id
+  LEFT JOIN invoice_items ii ON ii.invoice_id = i.id
+  WHERE i.account_id = :accountId
+  AND i.target_date >= :fromDate
+  GROUP BY i.id, i.account_id, i.invoice_date, i.target_date, i.currency
+  ORDER BY i.invoice_date ASC;
+>>
+
 getInvoicesBySubscription() ::= <<
-  SELECT i.id, i.account_id, i.invoice_date, i.target_date, i.currency, SUM(ii.amount) AS amount,
-         SUM(ip.amount) AS amount_paid, MAX(ip.payment_date) AS last_payment_attempt
+  SELECT i.id, i.account_id, i.invoice_date, i.target_date, i.currency, SUM(ii.amount) AS amount
   FROM invoices i
   LEFT JOIN invoice_items ii ON i.id = ii.invoice_id
   LEFT JOIN invoice_payments ip ON ip.invoice_id = i.id
@@ -43,8 +51,7 @@ getInvoicesForPayment() ::= <<
 >>
 
 getById() ::= <<
-  SELECT i.id, i.account_id, i.invoice_date, i.target_date, i.currency, SUM(ii.amount) AS amount,
-         SUM(ip.amount) AS amount_paid, MAX(ip.payment_date) AS last_payment_attempt
+  SELECT i.id, i.account_id, i.invoice_date, i.target_date, i.currency, SUM(ii.amount) AS amount
   FROM invoices i
   LEFT JOIN invoice_items ii ON i.id = ii.invoice_id
   LEFT JOIN invoice_payments ip ON ip.invoice_id = i.id
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
index 3fe72ed..2d7d4f2 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
@@ -21,7 +21,6 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 import org.joda.time.DateTime;
-import org.joda.time.Days;
 import org.testng.annotations.Test;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
@@ -34,6 +33,7 @@ import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
 
 @Test(groups = {"invoicing", "invoicing-invoiceDao"})
 public class InvoiceDaoTests extends InvoiceDaoTestBase {
@@ -47,7 +47,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
 
         invoiceDao.create(invoice);
 
-        List<Invoice> invoices = invoiceDao.getInvoicesByAccount(accountId.toString());
+        List<Invoice> invoices = invoiceDao.getInvoicesByAccount(accountId);
         assertNotNull(invoices);
         assertEquals(invoices.size(), 1);
         Invoice thisInvoice = invoices.get(0);
@@ -67,31 +67,31 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         DateTime startDate = new DateTime(2010, 1, 1, 0, 0, 0, 0);
         DateTime endDate = new DateTime(2010, 4, 1, 0, 0, 0, 0);
         InvoiceItem invoiceItem = new DefaultInvoiceItem(invoiceId, subscriptionId, startDate, endDate, "test", new BigDecimal("21.00"), new BigDecimal("7.00"), Currency.USD);
-        invoice.add(invoiceItem);
+        invoice.addInvoiceItem(invoiceItem);
         invoiceDao.create(invoice);
 
-        Invoice savedInvoice = invoiceDao.getById(invoiceId.toString());
+        Invoice savedInvoice = invoiceDao.getById(invoiceId);
         assertNotNull(savedInvoice);
         assertEquals(savedInvoice.getTotalAmount().compareTo(new BigDecimal("21.00")), 0);
-        assertEquals(savedInvoice.getAmountOutstanding().compareTo(new BigDecimal("21.00")), 0);
+        assertEquals(savedInvoice.getBalance().compareTo(new BigDecimal("21.00")), 0);
         assertEquals(savedInvoice.getAmountPaid(), BigDecimal.ZERO);
-        assertEquals(savedInvoice.getItems().size(), 1);
+        assertEquals(savedInvoice.getInvoiceItems().size(), 1);
 
         BigDecimal paymentAmount = new BigDecimal("11.00");
-        String paymentId = UUID.randomUUID().toString();
-        invoiceDao.notifySuccessfulPayment(invoiceId.toString(), paymentAmount, Currency.USD.toString(), paymentId, new DefaultClock().getUTCNow().plusDays(12).toDate());
+        UUID paymentId = UUID.randomUUID();
+        invoiceDao.notifySuccessfulPayment(invoiceId, paymentAmount, Currency.USD, paymentId, new DefaultClock().getUTCNow().plusDays(12));
 
-        Invoice retrievedInvoice = invoiceDao.getById(invoiceId.toString());
+        Invoice retrievedInvoice = invoiceDao.getById(invoiceId);
         assertNotNull(retrievedInvoice);
-        assertEquals(retrievedInvoice.getItems().size(), 1);
+        assertEquals(retrievedInvoice.getInvoiceItems().size(), 1);
         assertEquals(retrievedInvoice.getTotalAmount().compareTo(new BigDecimal("21.00")), 0);
-        assertEquals(retrievedInvoice.getAmountOutstanding().compareTo(new BigDecimal("10.00")), 0);
+        assertEquals(retrievedInvoice.getBalance().compareTo(new BigDecimal("10.00")), 0);
         assertEquals(retrievedInvoice.getAmountPaid().compareTo(new BigDecimal("11.00")), 0);
     }
 
     @Test
     public void testRetrievalForNonExistentInvoiceId() {
-        Invoice invoice = invoiceDao.getById(UUID.randomUUID().toString());
+        Invoice invoice = invoiceDao.getById(UUID.randomUUID());
         assertNull(invoice);
     }
 
@@ -101,16 +101,17 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         DateTime targetDate = new DateTime(2011, 10, 6, 0, 0, 0, 0);
         Invoice invoice = new DefaultInvoice(accountId, targetDate, Currency.USD);
 
-        String paymentId = UUID.randomUUID().toString();
+        UUID paymentId = UUID.randomUUID();
         DateTime paymentAttemptDate = new DateTime(2011, 6, 24, 12, 14, 36, 0);
         BigDecimal paymentAmount = new BigDecimal("14.0");
 
         invoiceDao.create(invoice);
-        invoiceDao.notifySuccessfulPayment(invoice.getId().toString(), paymentAmount, Currency.USD.toString(), paymentId, paymentAttemptDate.toDate());
+        invoiceDao.notifySuccessfulPayment(invoice.getId(), paymentAmount, Currency.USD, paymentId, paymentAttemptDate);
 
-        invoice = invoiceDao.getById(invoice.getId().toString());
+        invoice = invoiceDao.getById(invoice.getId());
         assertEquals(invoice.getAmountPaid().compareTo(paymentAmount), 0);
         assertTrue(invoice.getLastPaymentAttempt().equals(paymentAttemptDate));
+        assertEquals(invoice.getNumberOfPayments(), 1);
     }
 
     @Test
@@ -122,9 +123,9 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         DateTime paymentAttemptDate = new DateTime(2011, 6, 24, 12, 14, 36, 0);
 
         invoiceDao.create(invoice);
-        invoiceDao.notifyFailedPayment(invoice.getId().toString(), UUID.randomUUID().toString(), paymentAttemptDate.toDate());
+        invoiceDao.notifyFailedPayment(invoice.getId(), UUID.randomUUID(), paymentAttemptDate);
 
-        invoice = invoiceDao.getById(invoice.getId().toString());
+        invoice = invoiceDao.getById(invoice.getId());
         assertTrue(invoice.getLastPaymentAttempt().equals(paymentAttemptDate));
     }
 
@@ -134,14 +135,14 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         DateTime targetDate = new DateTime(2011, 10, 6, 0, 0, 0, 0);
         
         // determine the number of existing invoices available for payment (to avoid side effects from other tests)
-        List<UUID> invoices = invoiceDao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        List<UUID> invoices = invoiceDao.getInvoicesForPayment(notionalDate, NUMBER_OF_DAY_BETWEEN_RETRIES);
         int existingInvoiceCount = invoices.size();
         
         UUID accountId = UUID.randomUUID();
         Invoice invoice = new DefaultInvoice(accountId, targetDate, Currency.USD);
 
         invoiceDao.create(invoice);
-        invoices = invoiceDao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        invoices = invoiceDao.getInvoicesForPayment(notionalDate, NUMBER_OF_DAY_BETWEEN_RETRIES);
         assertEquals(invoices.size(), existingInvoiceCount);
     }
 
@@ -162,70 +163,69 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         BigDecimal amount = rate.multiply(new BigDecimal("3.0"));
 
         DefaultInvoiceItem item = new DefaultInvoiceItem(invoiceId, subscriptionId, targetDate, endDate, "test", amount, rate, Currency.USD);
-        invoice.add(item);
+        invoice.addInvoiceItem(item);
         invoiceDao.create(invoice);
 
         // ensure that the number of invoices for payment has increased by 1
         int count;
-        invoices = invoiceDao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        invoices = invoiceDao.getInvoicesForPayment(notionalDate, NUMBER_OF_DAY_BETWEEN_RETRIES);
         List<Invoice> invoicesDue = getInvoicesDueForPaymentAttempt(invoiceDao.get(), notionalDate);
         count = invoicesDue.size();
         assertEquals(invoices.size(), count);
 
         // attempt a payment; ensure that the number of invoices for payment has decreased by 1
         // (no retries for NUMBER_OF_DAYS_BETWEEN_RETRIES days)
-        invoiceDao.notifyFailedPayment(invoice.getId().toString(), UUID.randomUUID().toString(), notionalDate.toDate());
-        invoices = invoiceDao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        invoiceDao.notifyFailedPayment(invoice.getId(), UUID.randomUUID(), notionalDate);
+        invoices = invoiceDao.getInvoicesForPayment(notionalDate, NUMBER_OF_DAY_BETWEEN_RETRIES);
         count = getInvoicesDueForPaymentAttempt(invoiceDao.get(), notionalDate).size();
         assertEquals(invoices.size(), count);
 
         // advance clock by NUMBER_OF_DAYS_BETWEEN_RETRIES days
         // ensure that number of invoices for payment has increased by 1 (retry)
         notionalDate = notionalDate.plusDays(NUMBER_OF_DAY_BETWEEN_RETRIES);
-        invoices = invoiceDao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        invoices = invoiceDao.getInvoicesForPayment(notionalDate, NUMBER_OF_DAY_BETWEEN_RETRIES);
         count = getInvoicesDueForPaymentAttempt(invoiceDao.get(), notionalDate).size();
         assertEquals(invoices.size(), count);
 
         // post successful partial payment; ensure that number of invoices for payment has decreased by 1
-        invoiceDao.notifySuccessfulPayment(invoiceId.toString(), new BigDecimal("22.0000"), Currency.USD.toString(), UUID.randomUUID().toString(), notionalDate.toDate());
-        invoices = invoiceDao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        invoiceDao.notifySuccessfulPayment(invoiceId, new BigDecimal("22.0000"), Currency.USD, UUID.randomUUID(), notionalDate);
+        invoices = invoiceDao.getInvoicesForPayment(notionalDate, NUMBER_OF_DAY_BETWEEN_RETRIES);
         count = getInvoicesDueForPaymentAttempt(invoiceDao.get(), notionalDate).size();
         assertEquals(invoices.size(), count);
 
         // get invoice; verify amount paid is correct
-        invoice = invoiceDao.getById(invoiceId.toString());
+        invoice = invoiceDao.getById(invoiceId);
         assertEquals(invoice.getAmountPaid().compareTo(new BigDecimal("22.0")), 0);
 
         // advance clock NUMBER_OF_DAYS_BETWEEN_RETRIES days
         // ensure that number of invoices for payment has increased by 1 (retry)
         notionalDate = notionalDate.plusDays(NUMBER_OF_DAY_BETWEEN_RETRIES);
-        invoices = invoiceDao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        invoices = invoiceDao.getInvoicesForPayment(notionalDate, NUMBER_OF_DAY_BETWEEN_RETRIES);
         count = getInvoicesDueForPaymentAttempt(invoiceDao.get(), notionalDate).size();
         assertEquals(invoices.size(), count);
 
         // post completed payment; ensure that the number of invoices for payment has decreased by 1
-        invoiceDao.notifySuccessfulPayment(invoiceId.toString(), new BigDecimal("5.0000"), Currency.USD.toString(), UUID.randomUUID().toString(), notionalDate.toDate());
-        invoices = invoiceDao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        invoiceDao.notifySuccessfulPayment(invoiceId, new BigDecimal("5.0000"), Currency.USD, UUID.randomUUID(), notionalDate);
+        invoices = invoiceDao.getInvoicesForPayment(notionalDate, NUMBER_OF_DAY_BETWEEN_RETRIES);
         count = getInvoicesDueForPaymentAttempt(invoiceDao.get(), notionalDate).size();
         assertEquals(invoices.size(), count);
 
         // get invoice; verify amount paid is correct
-        invoice = invoiceDao.getById(invoiceId.toString());
-        count = getInvoicesDueForPaymentAttempt(invoiceDao.get(), notionalDate).size();
+        invoice = invoiceDao.getById(invoiceId);
         assertEquals(invoice.getAmountPaid().compareTo(new BigDecimal("27.0")), 0);
 
         // advance clock by NUMBER_OF_DAYS_BETWEEN_RETRIES days
         // ensure that the number of invoices for payment hasn't changed
         notionalDate = notionalDate.plusDays(NUMBER_OF_DAY_BETWEEN_RETRIES);
-        invoices = invoiceDao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
+        invoices = invoiceDao.getInvoicesForPayment(notionalDate, NUMBER_OF_DAY_BETWEEN_RETRIES);
         count = getInvoicesDueForPaymentAttempt(invoiceDao.get(), notionalDate).size();
         assertEquals(invoices.size(), count);
     }
 
-    private List<Invoice> getInvoicesDueForPaymentAttempt(List<Invoice> invoices, DateTime date) {
+    private List<Invoice> getInvoicesDueForPaymentAttempt(final List<Invoice> invoices, final DateTime date) {
         List<Invoice> invoicesDue= new ArrayList<Invoice>();
 
-        for (Invoice invoice : invoices) {
+        for (final Invoice invoice : invoices) {
             if (invoice.isDueForPayment(date, NUMBER_OF_DAY_BETWEEN_RETRIES)) {
                 invoicesDue.add(invoice);
             }
@@ -286,17 +286,45 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         invoiceItemDao.create(item7);
 
         // check that each subscription returns the correct number of invoices
-        List<Invoice> items1 = invoiceDao.getInvoicesBySubscription(subscriptionId1.toString());
+        List<Invoice> items1 = invoiceDao.getInvoicesBySubscription(subscriptionId1);
         assertEquals(items1.size(), 2);
 
-        List<Invoice> items2 = invoiceDao.getInvoicesBySubscription(subscriptionId2.toString());
+        List<Invoice> items2 = invoiceDao.getInvoicesBySubscription(subscriptionId2);
         assertEquals(items2.size(), 2);
 
-        List<Invoice> items3 = invoiceDao.getInvoicesBySubscription(subscriptionId3.toString());
+        List<Invoice> items3 = invoiceDao.getInvoicesBySubscription(subscriptionId3);
         assertEquals(items3.size(), 2);
 
-        List<Invoice> items4 = invoiceDao.getInvoicesBySubscription(subscriptionId4.toString());
+        List<Invoice> items4 = invoiceDao.getInvoicesBySubscription(subscriptionId4);
         assertEquals(items4.size(), 1);
     }
-    
+
+    @Test
+    public void testGetInvoicesForAccountAfterDate() {
+        UUID accountId = UUID.randomUUID();
+        DateTime targetDate1 = new DateTime(2011, 10, 6, 0, 0, 0, 0);
+        Invoice invoice1 = new DefaultInvoice(accountId, targetDate1, Currency.USD);
+        invoiceDao.create(invoice1);
+
+        DateTime targetDate2 = new DateTime(2011, 12, 6, 0, 0, 0, 0);
+        Invoice invoice2 = new DefaultInvoice(accountId, targetDate2, Currency.USD);
+        invoiceDao.create(invoice2);
+
+
+        List<Invoice> invoices;
+        invoices = invoiceDao.getInvoicesByAccount(accountId, new DateTime(2011, 1, 1, 0, 0, 0, 0));
+        //assertEquals(invoices.size(), 2);
+
+        invoices = invoiceDao.getInvoicesByAccount(accountId, new DateTime(2011, 10, 6, 0, 0, 0, 0));
+        //assertEquals(invoices.size(), 2);
+
+        invoices = invoiceDao.getInvoicesByAccount(accountId, new DateTime(2011, 10, 11, 0, 0, 0, 0));
+        //assertEquals(invoices.size(), 1);
+
+        invoices = invoiceDao.getInvoicesByAccount(accountId, new DateTime(2011, 12, 6, 0, 0, 0, 0));
+        //assertEquals(invoices.size(), 1);
+
+        invoices = invoiceDao.getInvoicesByAccount(accountId, new DateTime(2012, 1, 1, 0, 0, 0, 0));
+        //assertEquals(invoices.size(), 0);
+    }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
index 5d36d55..67de300 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
@@ -27,6 +27,7 @@ import org.testng.annotations.Test;
 
 import com.ning.billing.catalog.MockCatalog;
 import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Catalog;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
@@ -44,9 +45,10 @@ import com.ning.billing.invoice.model.DefaultInvoiceItem;
 import com.ning.billing.invoice.model.InvoiceGenerator;
 import com.ning.billing.invoice.model.InvoiceItemList;
 
-@Test(groups = {"invoicing", "invoiceGenerator"})
+@Test(groups = {"fast", "invoicing", "invoiceGenerator"})
 public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
     private final InvoiceGenerator generator = new DefaultInvoiceGenerator();
+    private final MockCatalog catalog = new MockCatalog();
 
     @Test
     public void testWithNullEventSetAndNullInvoiceSet() {
@@ -82,8 +84,9 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         PlanPhase phase = plan.getAllPhases()[0];
         
         BillingEvent event = new DefaultBillingEvent(sub, startDate, plan, phase,
-                new InternationalPriceMock(ZERO),new InternationalPriceMock(TEN), BillingPeriod.MONTHLY,
-                                               1, BillingModeType.IN_ADVANCE, "Test");
+                                                     new InternationalPriceMock(ZERO),
+                                                     new InternationalPriceMock(TEN), BillingPeriod.MONTHLY,
+                                                     1, BillingModeType.IN_ADVANCE, "Test");
         events.add(event);
 
         InvoiceItemList existingInvoiceItems = new InvoiceItemList();
@@ -108,8 +111,9 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         PlanPhase phase = plan.getAllPhases()[0];
         BigDecimal rate = TEN;
         BillingEvent event = new DefaultBillingEvent(sub, startDate, plan, phase,
-                new InternationalPriceMock(ZERO), new InternationalPriceMock(rate), BillingPeriod.MONTHLY,
-                                               15, BillingModeType.IN_ADVANCE,"Test");
+                                                     new InternationalPriceMock(ZERO),
+                                                     new InternationalPriceMock(rate), BillingPeriod.MONTHLY,
+                                                     15, BillingModeType.IN_ADVANCE,"Test");
         events.add(event);
 
         InvoiceItemList existingInvoiceItems = new InvoiceItemList();
@@ -131,7 +135,6 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
     public void testTwoMonthlySubscriptionsWithAlignedBillingDates() {
         BillingEventSet events = new BillingEventSet();
 
-        MockCatalog catalog = new MockCatalog();
         Plan plan1 = catalog.getCurrentPlans()[0];
         PlanPhase phase1 = plan1.getAllPhases()[0];
         Plan plan2 = catalog.getCurrentPlans()[1];
@@ -139,16 +142,16 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         
         Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(UUID.randomUUID()));
 
-        BillingEvent event1 = new DefaultBillingEvent(sub, buildDateTime(2011, 9, 1),
-                                               plan1,phase1,
-                                               new InternationalPriceMock(ZERO), new InternationalPriceMock(FIVE), BillingPeriod.MONTHLY,
-                                               1, BillingModeType.IN_ADVANCE, "Test");
+        BillingEvent event1 = new DefaultBillingEvent(sub, buildDateTime(2011, 9, 1), plan1, phase1,
+                                                      new InternationalPriceMock(ZERO),
+                                                      new InternationalPriceMock(FIVE), BillingPeriod.MONTHLY,
+                                                      1, BillingModeType.IN_ADVANCE, "Test");
         events.add(event1);
 
-        BillingEvent event2 = new DefaultBillingEvent(sub, buildDateTime(2011, 10, 1),
-                                               plan2,phase2,
-                                               new InternationalPriceMock(ZERO), new InternationalPriceMock(TEN), BillingPeriod.MONTHLY,
-                                               1, BillingModeType.IN_ADVANCE,"Test");
+        BillingEvent event2 = new DefaultBillingEvent(sub, buildDateTime(2011, 10, 1), plan2, phase2,
+                                                      new InternationalPriceMock(ZERO),
+                                                      new InternationalPriceMock(TEN), BillingPeriod.MONTHLY,
+                                                      1, BillingModeType.IN_ADVANCE, "Test");
         events.add(event2);
 
         InvoiceItemList existingInvoiceItems = new InvoiceItemList();
@@ -158,7 +161,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
 
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), 2);
-        assertEquals(invoice.getTotalAmount(), FIVE.multiply(TWO).add(TEN).setScale(NUMBER_OF_DECIMALS));
+        assertEquals(invoice.getTotalAmount(), FIVE.add(TEN).setScale(NUMBER_OF_DECIMALS));
     }
 
     @Test
@@ -283,35 +286,37 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         // plan 5: addon to plan 2, with bill cycle alignment to plan; immediate cancellation
 
         UUID subscriptionId1 = UUID.randomUUID();
-        String planName1 = "Change from trial to discount with immediate cancellation";
-        String plan1PhaseName1 = "Trial"; String plan1PhaseName2 = "Discount"; String plan1phase3 = "Cancel";
+        Plan plan1 = catalog.getCurrentPlans()[0];
+        PlanPhase plan1Phase1 = plan1.getAllPhases()[0]; PlanPhase plan1Phase2 = plan1.getAllPhases()[0]; PlanPhase plan1Phase3 = plan1.getAllPhases()[0];
         DateTime plan1StartDate = buildDateTime(2011, 1, 5);
         DateTime plan1PhaseChangeDate = buildDateTime(2011, 4, 5);
         DateTime plan1CancelDate = buildDateTime(2011, 4, 29);
 
         UUID subscriptionId2 = UUID.randomUUID();
-        String planName2 = "Change phase from trial to discount to evergreen";
-        String plan2PhaseName1 = "Trial"; String plan2PhaseName2 = "Discount"; String plan2PhaseName3 = "Evergreen";
+        Plan plan2 = catalog.getCurrentPlans()[1];
+        PlanPhase plan2Phase1 = plan2.getAllPhases()[0]; PlanPhase plan2Phase2 = plan2.getAllPhases()[0]; PlanPhase plan2Phase3 = plan2.getAllPhases()[0];
         DateTime plan2StartDate = buildDateTime(2011, 3, 10);
         DateTime plan2PhaseChangeToDiscountDate = buildDateTime(2011, 6, 10);
         DateTime plan2PhaseChangeToEvergreenDate = buildDateTime(2011, 9, 10);
 
         UUID subscriptionId3 = UUID.randomUUID();
-        String planName3 = "Upgrade with immediate change, BCD = 31";
-        String plan3PhaseName1 = "Evergreen monthly"; String plan3PhaseName2 = "Evergreen annual";
+        Plan plan3 = catalog.getCurrentPlans()[2];
+        PlanPhase plan3Phase1 = plan3.getAllPhases()[0]; PlanPhase plan3Phase2 = plan3.getAllPhases()[0];
         DateTime plan3StartDate = buildDateTime(2011, 5, 20);
         DateTime plan3UpgradeToAnnualDate = buildDateTime(2011, 7, 31);
 
         UUID subscriptionId4 = UUID.randomUUID();
-        String planName4a = "Plan change effective EOT; plan 1";
-        String planName4b = "Plan change effective EOT; plan 2";
-        String plan4PhaseName = "Evergreen";
+        Plan plan4a = catalog.getCurrentPlans()[0];
+        Plan plan4b = catalog.getCurrentPlans()[1];
+        PlanPhase plan4aPhase1 = plan4a.getAllPhases()[0];
+        PlanPhase plan4bPhase1 = plan4b.getAllPhases()[0];
+
         DateTime plan4StartDate = buildDateTime(2011, 6, 7);
         DateTime plan4ChangeOfPlanDate = buildDateTime(2011, 8, 7);
 
         UUID subscriptionId5 = UUID.randomUUID();
-        String planName5 = "Add-on";
-        String plan5PhaseName1 = "Evergreen"; String plan5PhaseName2 = "Cancel";
+        Plan plan5 = catalog.getCurrentPlans()[2];
+        PlanPhase plan5Phase1 = plan5.getAllPhases()[0]; PlanPhase plan5Phase2 = plan5.getAllPhases()[0];
         DateTime plan5StartDate = buildDateTime(2011, 6, 21);
         DateTime plan5CancelDate = buildDateTime(2011, 10, 7);
 
@@ -320,7 +325,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         BillingEventSet events = new BillingEventSet();
 
         // on 1/5/2011, create subscription 1 (trial)
-        events.add(createBillingEvent(subscriptionId1, plan1StartDate, planName1, plan1PhaseName1, EIGHT, 5));
+        events.add(createBillingEvent(subscriptionId1, plan1StartDate, plan1, plan1Phase1, EIGHT, 5));
         expectedAmount = EIGHT;
         testInvoiceGeneration(events, invoiceItems, plan1StartDate, 1, expectedAmount);
 
@@ -333,12 +338,12 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         testInvoiceGeneration(events, invoiceItems, buildDateTime(2011, 3, 5), 1, expectedAmount);
 
         // on 3/10/2011, create subscription 2 (trial)
-        events.add(createBillingEvent(subscriptionId2, plan2StartDate, planName2, plan2PhaseName1, TWENTY, 10));
+        events.add(createBillingEvent(subscriptionId2, plan2StartDate, plan2, plan2Phase1, TWENTY, 10));
         expectedAmount = TWENTY;
         testInvoiceGeneration(events, invoiceItems, plan2StartDate, 1, expectedAmount);
 
         // on 4/5/2011, invoice subscription 1 (discount)
-        events.add(createBillingEvent(subscriptionId1, plan1PhaseChangeDate, planName1, plan1PhaseName2, TWELVE, 5));
+        events.add(createBillingEvent(subscriptionId1, plan1PhaseChangeDate, plan1, plan1Phase2, TWELVE, 5));
         expectedAmount = TWELVE;
         testInvoiceGeneration(events, invoiceItems, plan1PhaseChangeDate, 1, expectedAmount);
 
@@ -347,7 +352,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         testInvoiceGeneration(events, invoiceItems, buildDateTime(2011, 4, 10), 1, expectedAmount);
 
         // on 4/29/2011, cancel subscription 1
-        events.add(createBillingEvent(subscriptionId1, plan1CancelDate, planName1, plan1phase3, ZERO, 5));
+        events.add(createBillingEvent(subscriptionId1, plan1CancelDate, plan1, plan1Phase3, ZERO, 5));
         expectedAmount = TWELVE.multiply(SIX.divide(THIRTY, NUMBER_OF_DECIMALS, ROUNDING_METHOD)).negate().setScale(NUMBER_OF_DECIMALS);
         testInvoiceGeneration(events, invoiceItems, plan1CancelDate, 2, expectedAmount);
 
@@ -356,17 +361,17 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         testInvoiceGeneration(events, invoiceItems, buildDateTime(2011, 5, 10), 1, expectedAmount);
 
         // on 5/20/2011, create subscription 3 (monthly)
-        events.add(createBillingEvent(subscriptionId3, plan3StartDate, planName3, plan3PhaseName1, TEN, 20));
+        events.add(createBillingEvent(subscriptionId3, plan3StartDate, plan3, plan3Phase1, TEN, 20));
         expectedAmount = TEN;
         testInvoiceGeneration(events, invoiceItems, plan3StartDate, 1, expectedAmount);
 
         // on 6/7/2011, create subscription 4
-        events.add(createBillingEvent(subscriptionId4, plan4StartDate, planName4a, plan4PhaseName, FIFTEEN, 7));
+        events.add(createBillingEvent(subscriptionId4, plan4StartDate, plan4a, plan4aPhase1, FIFTEEN, 7));
         expectedAmount = FIFTEEN;
         testInvoiceGeneration(events, invoiceItems, plan4StartDate, 1, expectedAmount);
 
         // on 6/10/2011, invoice subscription 2 (discount)
-        events.add(createBillingEvent(subscriptionId2, plan2PhaseChangeToDiscountDate, planName2, plan2PhaseName2, THIRTY, 10));
+        events.add(createBillingEvent(subscriptionId2, plan2PhaseChangeToDiscountDate, plan2, plan2Phase2, THIRTY, 10));
         expectedAmount = THIRTY;
         testInvoiceGeneration(events, invoiceItems, plan2PhaseChangeToDiscountDate, 1, expectedAmount);
 
@@ -375,7 +380,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         testInvoiceGeneration(events, invoiceItems, buildDateTime(2011, 6, 20), 1, expectedAmount);
 
         // on 6/21/2011, create add-on (subscription 5)
-        events.add(createBillingEvent(subscriptionId5, plan5StartDate, planName5, plan5PhaseName1, TWENTY, 10));
+        events.add(createBillingEvent(subscriptionId5, plan5StartDate, plan5, plan5Phase1, TWENTY, 10));
         expectedAmount = TWENTY.multiply(NINETEEN.divide(THIRTY, NUMBER_OF_DECIMALS, ROUNDING_METHOD)).setScale(NUMBER_OF_DECIMALS);
         testInvoiceGeneration(events, invoiceItems, plan5StartDate, 1, expectedAmount);
 
@@ -392,14 +397,14 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         testInvoiceGeneration(events, invoiceItems, buildDateTime(2011, 7, 20), 1, expectedAmount);
 
         // on 7/31/2011, convert subscription 3 to annual
-        events.add(createAnnualBillingEvent(subscriptionId3, plan3UpgradeToAnnualDate, planName3, plan3PhaseName2, ONE_HUNDRED, 31));
+        events.add(createAnnualBillingEvent(subscriptionId3, plan3UpgradeToAnnualDate, plan3, plan3Phase2, ONE_HUNDRED, 31));
         expectedAmount = ONE_HUNDRED.subtract(TEN);
         expectedAmount = expectedAmount.add(TEN.multiply(ELEVEN.divide(THIRTY_ONE, NUMBER_OF_DECIMALS, ROUNDING_METHOD)));
         expectedAmount = expectedAmount.setScale(NUMBER_OF_DECIMALS);
         testInvoiceGeneration(events, invoiceItems, plan3UpgradeToAnnualDate, 3, expectedAmount);
 
         // on 8/7/2011, invoice subscription 4 (plan 2)
-        events.add(createBillingEvent(subscriptionId4, plan4ChangeOfPlanDate, planName4b, plan4PhaseName, TWENTY_FOUR, 7));
+        events.add(createBillingEvent(subscriptionId4, plan4ChangeOfPlanDate, plan4b, plan4bPhase1, TWENTY_FOUR, 7));
         expectedAmount = TWENTY_FOUR;
         testInvoiceGeneration(events, invoiceItems, plan4ChangeOfPlanDate, 1, expectedAmount);
 
@@ -412,12 +417,12 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         testInvoiceGeneration(events, invoiceItems, buildDateTime(2011, 9, 7), 1, expectedAmount);
 
         // on 9/10/2011, invoice plan 2 (evergreen), invoice subscription 5
-        events.add(createBillingEvent(subscriptionId2, plan2PhaseChangeToEvergreenDate, planName2, plan2PhaseName3, FORTY, 10));
+        events.add(createBillingEvent(subscriptionId2, plan2PhaseChangeToEvergreenDate, plan2, plan2Phase3, FORTY, 10));
         expectedAmount = FORTY.add(TWENTY);
         testInvoiceGeneration(events, invoiceItems, plan2PhaseChangeToEvergreenDate, 2, expectedAmount);
 
         // on 10/7/2011, invoice subscription 4 (plan 2), cancel subscription 5
-        events.add(createBillingEvent(subscriptionId5, plan5CancelDate, planName5, plan5PhaseName2, ZERO, 10));
+        events.add(createBillingEvent(subscriptionId5, plan5CancelDate, plan5, plan5Phase2, ZERO, 10));
         expectedAmount = TWENTY_FOUR.add(TWENTY.multiply(THREE.divide(THIRTY)).negate().setScale(NUMBER_OF_DECIMALS));
         testInvoiceGeneration(events, invoiceItems, plan5CancelDate, 3, expectedAmount);
 
@@ -426,33 +431,34 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         testInvoiceGeneration(events, invoiceItems, buildDateTime(2011, 10, 10), 1, expectedAmount);
     }
 
-    private DefaultBillingEvent createBillingEvent(UUID subscriptionId, DateTime startDate, String planName, String planPhaseName,
-                                            BigDecimal rate, int billCycleDay) {
-        MockCatalog catalog = new MockCatalog();
-        Plan plan = catalog.getCurrentPlans()[0];
-        PlanPhase phase = plan.getAllPhases()[0];
-        Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(UUID.randomUUID()));
-        return new DefaultBillingEvent(sub, startDate, plan, phase,
-                new InternationalPriceMock(ZERO),new InternationalPriceMock(rate), BillingPeriod.MONTHLY,
-                                billCycleDay, BillingModeType.IN_ADVANCE,"Test");
+    private DefaultBillingEvent createBillingEvent(final UUID subscriptionId, final DateTime startDate,
+                                                   final Plan plan, final PlanPhase planPhase,
+                                                   final BigDecimal rate, final int billCycleDay) {
+        Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(subscriptionId));
+
+        return new DefaultBillingEvent(sub, startDate, plan, planPhase,
+                                       new InternationalPriceMock(ZERO),
+                                       new InternationalPriceMock(rate), BillingPeriod.MONTHLY,
+                                       billCycleDay, BillingModeType.IN_ADVANCE,"Test");
     }
 
-    private DefaultBillingEvent createAnnualBillingEvent(UUID subscriptionId, DateTime startDate, String planName, String planPhaseName,
-                                                  BigDecimal rate, int billCycleDay) {
-        MockCatalog catalog = new MockCatalog();
-        Plan plan = catalog.getCurrentPlans()[0];
-        PlanPhase phase = plan.getAllPhases()[0];
-        Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(UUID.randomUUID()));
-        return new DefaultBillingEvent(sub, startDate, plan, phase,
-                new InternationalPriceMock(ZERO),new InternationalPriceMock(rate), BillingPeriod.ANNUAL,
-                                billCycleDay, BillingModeType.IN_ADVANCE,"Test");
+    private DefaultBillingEvent createAnnualBillingEvent(final UUID subscriptionId, final DateTime startDate,
+                                                         final Plan plan, final PlanPhase planPhase,
+                                                         final BigDecimal rate, final int billCycleDay) {
+        Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(subscriptionId));
+        return new DefaultBillingEvent(sub, startDate, plan, planPhase,
+                                       new InternationalPriceMock(ZERO),
+                                       new InternationalPriceMock(rate), BillingPeriod.ANNUAL,
+                                       billCycleDay, BillingModeType.IN_ADVANCE,"Test");
     }
 
-    private void testInvoiceGeneration(BillingEventSet events, InvoiceItemList existingInvoiceItems, DateTime targetDate, int expectedNumberOfItems, BigDecimal expectedAmount) {
+    private void testInvoiceGeneration(final BillingEventSet events, final InvoiceItemList existingInvoiceItems,
+                                       final DateTime targetDate, final int expectedNumberOfItems,
+                                       final BigDecimal expectedAmount) {
         Currency currency = Currency.USD;
         UUID accountId = UUID.randomUUID();
         Invoice invoice = generator.generateInvoice(accountId, events, existingInvoiceItems, targetDate, currency);
-        existingInvoiceItems.addAll(invoice.getItems());
+        existingInvoiceItems.addAll(invoice.getInvoiceItems());
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), expectedNumberOfItems);
         assertEquals(invoice.getTotalAmount(), expectedAmount);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/DoubleProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/DoubleProRationTests.java
index 84bf795..64fa95c 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/DoubleProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/DoubleProRationTests.java
@@ -24,7 +24,7 @@ import org.testng.annotations.Test;
 
 import java.math.BigDecimal;
 
-@Test(groups = {"invoicing", "proRation"})
+@Test(groups = {"fast", "invoicing", "proRation"})
 public class DoubleProRationTests extends ProRationInAdvanceTestBase {
     @Override
     protected BillingPeriod getBillingPeriod() {
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 f612f10..8d0eb06 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
@@ -22,7 +22,7 @@ import org.testng.annotations.Test;
 
 import java.math.BigDecimal;
 
-@Test(groups = {"invoicing", "proRation"})
+@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/annual/LeadingProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/LeadingProRationTests.java
index f44876e..a009fba 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/LeadingProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/LeadingProRationTests.java
@@ -24,7 +24,7 @@ import org.testng.annotations.Test;
 
 import java.math.BigDecimal;
 
-@Test(groups = {"invoicing", "proRation"})
+@Test(groups = {"fast", "invoicing", "proRation"})
 public class LeadingProRationTests extends ProRationInAdvanceTestBase {
     @Override
     protected BillingPeriod getBillingPeriod() {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/ProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/ProRationTests.java
index 274a8e7..f882005 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/ProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/ProRationTests.java
@@ -24,7 +24,7 @@ import org.testng.annotations.Test;
 
 import java.math.BigDecimal;
 
-@Test(groups = {"invoicing", "proRation"})
+@Test(groups = {"fast", "invoicing", "proRation"})
 public class ProRationTests extends ProRationInAdvanceTestBase {
     @Override
     protected BillingPeriod getBillingPeriod() {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/TrailingProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/TrailingProRationTests.java
index 0689c69..e0216b5 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/TrailingProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/annual/TrailingProRationTests.java
@@ -24,7 +24,7 @@ import org.testng.annotations.Test;
 
 import java.math.BigDecimal;
 
-@Test(groups = {"invoicing", "proRation"})
+@Test(groups = {"fast", "invoicing", "proRation"})
 public class TrailingProRationTests extends ProRationInAdvanceTestBase {
     @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 d4e4c24..60892e6 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
@@ -22,7 +22,7 @@ import org.testng.annotations.Test;
 
 import java.math.BigDecimal;
 
-@Test(groups = {"invoicing", "proRation"})
+@Test(groups = {"fast", "invoicing", "proRation"})
 public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBase {
     /**
      * used for testing cancellation in less than a single billing period
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/DoubleProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/DoubleProRationTests.java
index 4799e40..240b8aa 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/DoubleProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/DoubleProRationTests.java
@@ -24,7 +24,7 @@ import org.testng.annotations.Test;
 
 import java.math.BigDecimal;
 
-@Test(groups = {"invoicing", "proRation"})
+@Test(groups = {"fast", "invoicing", "proRation"})
 public class DoubleProRationTests extends ProRationInAdvanceTestBase {
     @Override
     protected BillingPeriod getBillingPeriod() {
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 eb3b1c9..8b40db8 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
@@ -22,7 +22,7 @@ import org.testng.annotations.Test;
 
 import java.math.BigDecimal;
 
-@Test(groups = {"invoicing", "proRation"})
+@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/monthly/LeadingProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/LeadingProRationTests.java
index 5db6911..998e566 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/LeadingProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/LeadingProRationTests.java
@@ -24,7 +24,7 @@ import org.testng.annotations.Test;
 
 import java.math.BigDecimal;
 
-@Test(groups = {"invoicing", "proRation"})
+@Test(groups = {"fast", "invoicing", "proRation"})
 public class LeadingProRationTests extends ProRationInAdvanceTestBase {
     @Override
     protected BillingPeriod getBillingPeriod() {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/ProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/ProRationTests.java
index 13c75df..c3748d1 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/ProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/ProRationTests.java
@@ -24,7 +24,7 @@ import org.testng.annotations.Test;
 
 import java.math.BigDecimal;
 
-@Test(groups = {"invoicing", "proRation"})
+@Test(groups = {"fast", "invoicing", "proRation"})
 public class ProRationTests extends ProRationInAdvanceTestBase {
     @Override
     protected BillingPeriod getBillingPeriod() {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TrailingProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TrailingProRationTests.java
index 9a8e635..6a5e5ef 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TrailingProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/monthly/TrailingProRationTests.java
@@ -24,7 +24,7 @@ import org.testng.annotations.Test;
 
 import java.math.BigDecimal;
 
-@Test(groups = {"invoicing", "proRation"})
+@Test(groups = {"fast", "invoicing", "proRation"})
 public class TrailingProRationTests extends ProRationInAdvanceTestBase {
     @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 14392a8..18bd096 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
@@ -21,7 +21,7 @@ import com.ning.billing.invoice.model.InAdvanceBillingMode;
 import com.ning.billing.invoice.tests.ProRationTestBase;
 import org.testng.annotations.Test;
 
-@Test(groups = {"invoicing", "proRation"})
+@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/DoubleProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/DoubleProRationTests.java
index 68e29fd..c1d6085 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/DoubleProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/DoubleProRationTests.java
@@ -24,7 +24,7 @@ import org.testng.annotations.Test;
 
 import java.math.BigDecimal;
 
-@Test(groups = {"invoicing", "proRation"})
+@Test(groups = {"fast", "invoicing", "proRation"})
 public class DoubleProRationTests extends ProRationInAdvanceTestBase {
     @Override
     protected BillingPeriod getBillingPeriod() {
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 8306716..c4237a6 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
@@ -22,7 +22,7 @@ import org.testng.annotations.Test;
 
 import java.math.BigDecimal;
 
-@Test(groups = {"invoicing", "proRation"})
+@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/quarterly/LeadingProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/LeadingProRationTests.java
index fe614a9..04ec683 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/LeadingProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/LeadingProRationTests.java
@@ -24,7 +24,7 @@ import org.testng.annotations.Test;
 
 import java.math.BigDecimal;
 
-@Test(groups = {"invoicing", "proRation"})
+@Test(groups = {"fast", "invoicing", "proRation"})
 public class LeadingProRationTests extends ProRationInAdvanceTestBase {
     @Override
     protected BillingPeriod getBillingPeriod() {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/ProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/ProRationTests.java
index c99aa5c..e13db0d 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/ProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/ProRationTests.java
@@ -24,7 +24,7 @@ import org.testng.annotations.Test;
 
 import java.math.BigDecimal;
 
-@Test(groups = {"invoicing", "proRation"})
+@Test(groups = {"fast", "invoicing", "proRation"})
 public class ProRationTests extends ProRationInAdvanceTestBase {
     @Override
     protected BillingPeriod getBillingPeriod() {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TrailingProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TrailingProRationTests.java
index 89e138e..8f63010 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TrailingProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/quarterly/TrailingProRationTests.java
@@ -24,7 +24,7 @@ import org.testng.annotations.Test;
 
 import java.math.BigDecimal;
 
-@Test(groups = {"invoicing", "proRation"})
+@Test(groups = {"fast", "invoicing", "proRation"})
 public class TrailingProRationTests extends ProRationInAdvanceTestBase {
     @Override
     protected BillingPeriod getBillingPeriod() {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/ValidationProRationTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/ValidationProRationTests.java
index 9de01bd..b38d076 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/ValidationProRationTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/inAdvance/ValidationProRationTests.java
@@ -28,7 +28,7 @@ import java.math.BigDecimal;
 
 import static org.testng.Assert.assertEquals;
 
-@Test(groups = {"invoicing", "proRation"})
+@Test(groups = {"fast", "invoicing", "proRation"})
 public class ValidationProRationTests extends ProRationTestBase {
     protected BillingPeriod getBillingPeriod() {
         return BillingPeriod.MONTHLY;