killbill-aplcache

invoice/entitlement integration

1/23/2012 7:23:48 PM

Changes

Details

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 ba3912a..6dc3e94 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
@@ -20,6 +20,8 @@ import java.util.SortedSet;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
 public interface EntitlementBillingApi {
 
@@ -34,6 +36,8 @@ public interface EntitlementBillingApi {
 
     public UUID getAccountIdFromSubscriptionId(UUID subscriptionId);
 
-    public void setChargedThroughDate(UUID subscriptionId, DateTime ctd) throws EntitlementBillingApiException;
+    public void setChargedThroughDate(UUID subscriptionId, DateTime ctd);
+
+    public void setChargedThroughDateFromTransaction(Transmogrifier transactionalDao, UUID subscriptionId, DateTime ctd);
 
 }
diff --git a/api/src/main/java/com/ning/billing/invoice/api/BillingEventSet.java b/api/src/main/java/com/ning/billing/invoice/api/BillingEventSet.java
index 8e42a9e..5eaa9f1 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/BillingEventSet.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/BillingEventSet.java
@@ -19,8 +19,18 @@ package com.ning.billing.invoice.api;
 import com.ning.billing.entitlement.api.billing.BillingEvent;
 
 import java.util.ArrayList;
+import java.util.Collection;
 
 public class BillingEventSet extends ArrayList<BillingEvent> {
+    public BillingEventSet() {
+        super();
+    }
+
+    public BillingEventSet(Collection<BillingEvent> events) {
+        super();
+        addAll(events);
+    }
+
     public BillingEvent getLast() {
         if (this.size() == 0) {return null;}
 
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/TestBasic.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/TestBasic.java
index 6c470c6..8750e8c 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/TestBasic.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/TestBasic.java
@@ -208,7 +208,6 @@ public class TestBasic {
         subscription.changePlan(newProductName, newTerm, newPlanSetName, clock.getUTCNow());
         assertTrue(busHandler.isCompleted(5000));
 
-
         //
         // VERIFY AGAIN CTD HAS BEEN SET
         //
diff --git a/beatrix/src/test/resources/resource.properties b/beatrix/src/test/resources/resource.properties
index cc82754..1cf5ad0 100644
--- a/beatrix/src/test/resources/resource.properties
+++ b/beatrix/src/test/resources/resource.properties
@@ -1,4 +1,4 @@
-killbill.catalog.uri=file:src/test/resources/catalogSample.xml
+killbill.catalog.uri=file:beatrix/src/test/resources/catalogSample.xml
 killbill.entitlement.dao.claim.time=60000
 killbill.entitlement.dao.ready.max=1
 killbill.entitlement.engine.notifications.sleep=500
diff --git a/catalog/src/main/java/com/ning/billing/catalog/DefaultCatalogService.java b/catalog/src/main/java/com/ning/billing/catalog/DefaultCatalogService.java
index 7f88f07..b824617 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/DefaultCatalogService.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/DefaultCatalogService.java
@@ -28,6 +28,7 @@ import com.ning.billing.lifecycle.LifecycleHandlerType;
 import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel;
 
 public class DefaultCatalogService implements KillbillService, Provider<Catalog>, CatalogService {
+    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultCatalogService.class);
 
     private static final String CATALOG_SERVICE_NAME = "catalog-service";
 
@@ -54,7 +55,7 @@ public class DefaultCatalogService implements KillbillService, Provider<Catalog>
             	System.out.println("Really really::" + config.getCatalogURI());
             	String url = config.getCatalogURI();
             	catalog = loader.load(url);
-            	
+
                 //catalog = XMLLoader.getObjectFromProperty(config.getCatalogURI(), Catalog.class);
                 isInitialized = true;
             } catch (Exception e) {
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 0077e0b..9b22a84 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
@@ -16,35 +16,24 @@
 
 package com.ning.billing.entitlement.api.billing;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.UUID;
-
-import org.joda.time.DateTime;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 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.BillingAlignment;
-import com.ning.billing.catalog.api.Catalog;
-import com.ning.billing.catalog.api.CatalogApiException;
-import com.ning.billing.catalog.api.CatalogService;
-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.catalog.api.*;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
 import com.ning.billing.entitlement.api.user.SubscriptionTransition;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
+import com.ning.billing.entitlement.engine.dao.SubscriptionSqlDao;
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
 
 public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
 	private static final Logger log = LoggerFactory.getLogger(DefaultEntitlementBillingApi.class);
@@ -127,17 +116,25 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
     		
     }
     
-
     @Override
-    public void setChargedThroughDate(final UUID subscriptionId, final DateTime ctd) throws EntitlementBillingApiException {
+    public void setChargedThroughDate(final UUID subscriptionId, final DateTime ctd) {
         SubscriptionData subscription = (SubscriptionData) dao.getSubscriptionFromId(subscriptionId);
-        if (subscription == null) {
-            throw new EntitlementBillingApiException(ErrorCode.ENT_INVALID_SUBSCRIPTION_ID, subscriptionId.toString());
-        }
 
         SubscriptionBuilder builder = new SubscriptionBuilder(subscription)
             .setChargedThroughDate(ctd)
             .setPaidThroughDate(subscription.getPaidThroughDate());
+
         dao.updateSubscription(new SubscriptionData(builder));
     }
+
+    @Override
+    public void setChargedThroughDateFromTransaction(final Transmogrifier transactionalDao, final UUID subscriptionId, final DateTime ctd) {
+        SubscriptionSqlDao subscriptionSqlDao = transactionalDao.become(SubscriptionSqlDao.class);
+        SubscriptionData subscription = (SubscriptionData) subscriptionSqlDao.getSubscriptionFromId(subscriptionId.toString());
+
+        Date paidThroughDate = (subscription.getPaidThroughDate() == null) ? null : subscription.getPaidThroughDate().toDate();
+
+        subscriptionSqlDao.updateSubscription(subscriptionId.toString(), subscription.getActiveVersion(),
+                                              ctd.toDate(), paidThroughDate);
+    }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
index f54f902..47149a3 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
@@ -89,7 +89,6 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
 
     @Override
     public Subscription createSubscription(UUID bundleId, PlanPhaseSpecifier spec, DateTime requestedDate) throws EntitlementUserApiException {
-
         try {
             String realPriceList = (spec.getPriceListName() == null) ? PriceListSet.DEFAULT_PRICELIST_NAME : spec.getPriceListName();
             DateTime now = clock.getUTCNow();
@@ -102,7 +101,6 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
 
             Plan plan = catalogService.getFullCatalog().findPlan(spec.getProductName(), spec.getBillingPeriod(), realPriceList, requestedDate);
 
-
             PlanPhase phase = (plan.getInitialPhases() != null) ? plan.getInitialPhases()[0] : plan.getFinalPhase();
             if (phase == null) {
                 throw new EntitlementError(String.format("No initial PlanPhase for Product %s, term %s and set %s does not exist in the catalog",
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 75c92eb..154acb5 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
@@ -25,6 +25,7 @@ import org.joda.time.DateTime;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
+import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -52,6 +53,7 @@ import com.ning.billing.util.notificationq.NotificationKey;
 import com.ning.billing.util.notificationq.NotificationQueue;
 import com.ning.billing.util.notificationq.NotificationQueueService;
 import com.ning.billing.util.notificationq.NotificationQueueService.NoSuchNotificationQueue;
+import sun.jkernel.Bundle;
 
 
 public class EntitlementSqlDao implements EntitlementDao {
@@ -110,7 +112,13 @@ public class EntitlementSqlDao implements EntitlementDao {
 
     @Override
     public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId) {
-        UUID bundleId = subscriptionsDao.getSubscriptionFromId(subscriptionId.toString()).getBundleId();
+        Subscription subscription = subscriptionsDao.getSubscriptionFromId(subscriptionId.toString());
+        if (subscription == null) {
+            log.error(String.format(ErrorCode.ENT_INVALID_SUBSCRIPTION_ID.getFormat(), subscriptionId.toString()));
+            return null;
+        }
+
+        UUID bundleId = subscription.getBundleId();
         if (bundleId == null) {
             log.error(String.format(ErrorCode.ENT_GET_NO_BUNDLE_FOR_SUBSCRIPTION.getFormat(), subscriptionId.toString()));
             return null;
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 813f5d2..a762441 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
@@ -17,8 +17,11 @@
 package com.ning.billing.invoice.dao;
 
 import java.math.BigDecimal;
+import java.util.Collection;
 import java.util.List;
 import java.util.UUID;
+
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
@@ -37,15 +40,18 @@ public class DefaultInvoiceDao implements InvoiceDao {
     private final InvoiceSqlDao invoiceSqlDao;
     private final InvoiceItemSqlDao invoiceItemSqlDao;
     private final NextBillingDateNotifier notifier;
+    private final EntitlementBillingApi entitlementBillingApi;
 
     private final Bus eventBus;
 
     @Inject
-    public DefaultInvoiceDao(final IDBI dbi, final Bus eventBus, final NextBillingDateNotifier notifier) {
+    public DefaultInvoiceDao(final IDBI dbi, final Bus eventBus,
+                             final NextBillingDateNotifier notifier, final EntitlementBillingApi entitlementBillingApi) {
         this.invoiceSqlDao = dbi.onDemand(InvoiceSqlDao.class);
         this.invoiceItemSqlDao = dbi.onDemand(InvoiceItemSqlDao.class);
         this.eventBus = eventBus;
         this.notifier = notifier;
+        this.entitlementBillingApi = entitlementBillingApi;
     }
 
     @Override
@@ -134,7 +140,8 @@ public class DefaultInvoiceDao implements InvoiceDao {
                     InvoiceItemSqlDao invoiceItemDao = invoiceDao.become(InvoiceItemSqlDao.class);
                     invoiceItemDao.create(invoiceItems);
 
-                    notifyOfChargeThroughDate(invoiceSqlDao, invoiceItems);
+                    notifyOfFutureBillingEvents(invoiceSqlDao, invoiceItems);
+                    setChargedThroughDates(invoiceSqlDao, invoiceItems);
 
                     List<InvoicePayment> invoicePayments = invoice.getPayments();
                     InvoicePaymentSqlDao invoicePaymentSqlDao = invoiceDao.become(InvoicePaymentSqlDao.class);
@@ -211,9 +218,15 @@ public class DefaultInvoiceDao implements InvoiceDao {
         }
     }
 
-    private void notifyOfChargeThroughDate(final InvoiceSqlDao dao, final List<InvoiceItem> invoiceItems) {
+    private void notifyOfFutureBillingEvents(final InvoiceSqlDao dao, final List<InvoiceItem> invoiceItems) {
         for (final InvoiceItem item : invoiceItems) {
             notifier.insertNextBillingNotification(dao, item.getSubscriptionId(), item.getEndDate());
         }
     }
+
+    private void setChargedThroughDates(final InvoiceSqlDao dao, final Collection<InvoiceItem> invoiceItems) {
+        for (InvoiceItem invoiceItem : invoiceItems) {
+            entitlementBillingApi.setChargedThroughDateFromTransaction(dao, invoiceItem.getSubscriptionId(), invoiceItem.getEndDate());
+        }
+    }
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
index 2efc98d..389b433 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
@@ -105,7 +105,7 @@ public interface InvoicePaymentSqlDao extends EntityDao<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("paymentDate", payment.getPaymentDate().toDate());
                         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/InvoiceListener.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
index d2dcd01..7cff1ee 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
@@ -19,6 +19,8 @@ package com.ning.billing.invoice;
 import java.util.List;
 import java.util.SortedSet;
 import java.util.UUID;
+
+import com.ning.billing.invoice.notification.NextBillingDateEvent;
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -64,7 +66,17 @@ public class InvoiceListener {
 
     @Subscribe
     public void handleSubscriptionTransition(final SubscriptionTransition transition) {
-        UUID subscriptionId = transition.getSubscriptionId();
+        processSubscription(transition.getSubscriptionId(), transition.getEffectiveTransitionTime());
+    }
+
+    @Subscribe
+    public void handleNextBillingDateEvent(final NextBillingDateEvent event) {
+        processSubscription(event.getSubscriptionId(), new DateTime());
+    }
+
+    private void processSubscription(final UUID subscriptionId, final DateTime targetDate) {
+        log.info("Got subscription transition from InvoiceListener.");
+
         if (subscriptionId == null) {
             log.error("Failed handling entitlement change.", new InvoiceApiException(ErrorCode.INVOICE_INVALID_TRANSITION));
             return;
@@ -85,10 +97,8 @@ public class InvoiceListener {
         }
 
         SortedSet<BillingEvent> events = entitlementBillingApi.getBillingEventsForAccount(accountId);
-        BillingEventSet billingEvents = new BillingEventSet();
-        billingEvents.addAll(events);
+        BillingEventSet billingEvents = new BillingEventSet(events);
 
-        DateTime targetDate = transition.getEffectiveTransitionTime();
         Currency targetCurrency = account.getCurrency();
 
         List<InvoiceItem> items = invoiceDao.getInvoiceItemsByAccount(accountId);
@@ -96,9 +106,9 @@ public class InvoiceListener {
         Invoice invoice = generator.generateInvoice(accountId, billingEvents, invoiceItemList, targetDate, targetCurrency);
 
         if (invoice != null) {
-            if (invoice.getNumberOfItems() > 0) {
+            //if (invoice.getNumberOfItems() > 0) {
                 invoiceDao.create(invoice);
-            }
+            //}
         }
     }
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/BillingModeBase.java b/invoice/src/main/java/com/ning/billing/invoice/model/BillingModeBase.java
index 949f711..d7235c7 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/BillingModeBase.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/BillingModeBase.java
@@ -27,6 +27,10 @@ public abstract class BillingModeBase implements BillingMode {
         if (endDate.isBefore(startDate)) {throw new InvalidDateSequenceException();}
         if (targetDate.isBefore(startDate)) {throw new InvalidDateSequenceException();}
 
+        if (billingPeriod == BillingPeriod.NO_BILLING_PERIOD) {
+            return BigDecimal.ZERO;
+        }
+
         BigDecimal precedingProRation = calculateProRationBeforeFirstBillingPeriod(startDate, billingCycleDay, billingPeriod);
 
         DateTime firstBillCycleDate = calculateBillingCycleDateOnOrAfter(startDate, billingCycleDay);
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 29d6360..e21da4f 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
@@ -85,7 +85,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
         existingItems.removeCancellingPairs();
 
         // remove zero-dollar invoice items
-        currentItems.removeZeroDollarItems();
+        //currentItems.removeZeroDollarItems();
 
         // add existing items that aren't covered by current items as credit items
         for (final InvoiceItem existingItem : existingItems) {
@@ -128,7 +128,8 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
     	try {
     		//TODO: Jeff getPrice() -> getRecurringPrice()
             InternationalPrice recurringPrice = event.getRecurringPrice();
-    		BigDecimal rate = recurringPrice.getPrice(targetCurrency);
+    		BigDecimal rate = (recurringPrice == null) ? BigDecimal.ZERO : recurringPrice.getPrice(targetCurrency);
+
     		BigDecimal invoiceItemAmount = calculateInvoiceItemAmount(event, targetDate, rate);
     		BillingMode billingMode = getBillingMode(event.getBillingMode());
     		DateTime billThroughDate = billingMode.calculateEffectiveEndDate(event.getEffectiveDate(), targetDate, event.getBillCycleDay(), event.getBillingPeriod());
@@ -146,15 +147,13 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
     	//TODO: Jeff getPrice() -> getRecurringPrice()
     	try {
             InternationalPrice recurringPrice = firstEvent.getRecurringPrice();
+            BigDecimal rate = (recurringPrice == null) ? BigDecimal.ZERO : recurringPrice.getPrice(targetCurrency);
 
-            if (recurringPrice != null) {
-                BigDecimal rate = recurringPrice.getPrice(targetCurrency);
-                BigDecimal invoiceItemAmount = calculateInvoiceItemAmount(firstEvent, secondEvent, targetDate, rate);
-                BillingMode billingMode = getBillingMode(firstEvent.getBillingMode());
-                DateTime billThroughDate = billingMode.calculateEffectiveEndDate(firstEvent.getEffectiveDate(), secondEvent.getEffectiveDate(), targetDate, firstEvent.getBillCycleDay(), firstEvent.getBillingPeriod());
+            BigDecimal invoiceItemAmount = calculateInvoiceItemAmount(firstEvent, secondEvent, targetDate, rate);
+            BillingMode billingMode = getBillingMode(firstEvent.getBillingMode());
+            DateTime billThroughDate = billingMode.calculateEffectiveEndDate(firstEvent.getEffectiveDate(), secondEvent.getEffectiveDate(), targetDate, firstEvent.getBillCycleDay(), firstEvent.getBillingPeriod());
 
-                addInvoiceItem(invoiceId, items, firstEvent, billThroughDate, invoiceItemAmount, rate, targetCurrency);
-            }
+            addInvoiceItem(invoiceId, items, firstEvent, billThroughDate, invoiceItemAmount, rate, targetCurrency);
     	} catch (CatalogApiException e) {
     		log.error(String.format("Encountered a catalog error processing invoice %s for billing event on date %s",
                     invoiceId.toString(),
@@ -165,10 +164,10 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
     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)) {
+        //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(final BillingEvent event, final DateTime targetDate,
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InAdvanceBillingMode.java b/invoice/src/main/java/com/ning/billing/invoice/model/InAdvanceBillingMode.java
index 37b5820..3ad4f50 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InAdvanceBillingMode.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InAdvanceBillingMode.java
@@ -114,7 +114,12 @@ public class InAdvanceBillingMode extends BillingModeBase {
         DateTime nextBillingCycleDate = calculateBillingCycleDateOnOrAfter(startDate, billingCycleDay);
         DateTime previousBillingCycleDate = nextBillingCycleDate.plusMonths(-billingPeriod.getNumberOfMonths());
 
-        BigDecimal daysInPeriod = new BigDecimal(Days.daysBetween(previousBillingCycleDate, nextBillingCycleDate).getDays());
+        int daysBetween = Days.daysBetween(previousBillingCycleDate, nextBillingCycleDate).getDays();
+        if (daysBetween == 0) {
+            return BigDecimal.ZERO;
+        }
+
+        BigDecimal daysInPeriod = new BigDecimal(daysBetween);
         BigDecimal days = new BigDecimal(Days.daysBetween(startDate, nextBillingCycleDate).getDays());
 
         return days.divide(daysInPeriod, NUMBER_OF_DECIMALS, ROUNDING_METHOD);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDateNotifier.java b/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDateNotifier.java
index fce27b5..dfed0db 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDateNotifier.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDateNotifier.java
@@ -123,12 +123,15 @@ public class DefaultNextBillingDateNotifier implements  NextBillingDateNotifier 
     @Override
     public void insertNextBillingNotification(Transmogrifier transactionalDao, final UUID subscriptionId, DateTime futureNotificationTime) {
     	if (nextBillingQueue != null) {
+            log.info("Queuing next billing date notification. id: {}, timestamp: {}", subscriptionId.toString(), futureNotificationTime.toString());
             nextBillingQueue.recordFutureNotificationFromTransaction(transactionalDao, futureNotificationTime, new NotificationKey(){
                 @Override
                 public String toString() {
                     return subscriptionId.toString();
                 }
     	    });
+        } else {
+            log.error("Attempting to put items on a non-existent queue (NextBillingDateNotifier).");
         }
     }
 }
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 eb312dd..4bd3b5c 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
@@ -62,10 +62,10 @@ getById() ::= <<
 getAccountBalance() ::= <<
   SELECT SUM(iis.total_amount) AS amount_invoiced, SUM(ips.total_paid) AS amount_paid
   FROM invoices i
-  LEFT JOIN invoice_payment_summary ips ON ips.invoice_id = i.id
-  LEFT JOIN invoice_item_summary iis ON iis.invoice_id = i.id
-  GROUP BY i.account_id
-  WHERE i.account_id = :accountId;
+  LEFT JOIN invoice_payment_summary ips ON i.id = ips.invoice_id
+  LEFT JOIN invoice_item_summary iis ON i.id = iis.invoice_id
+  WHERE i.account_id = :accountId
+  GROUP BY i.account_id;
 >>
 
 create() ::= <<
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql b/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
index a3ff37d..f8e6cfb 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
@@ -45,6 +45,6 @@ GROUP BY invoice_id;
 
 DROP VIEW IF EXISTS invoice_item_summary;
 CREATE VIEW invoice_item_summary AS
-SELECT invoice_id, SUM(amount) AS total_amount
-FROM invoice_items
-GROUP BY invoice_id;
\ No newline at end of file
+select invoice_id, sum(amount) AS total_amount
+from invoice_items
+group by invoice_id;
\ No newline at end of file
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
index ee6060a..9f83fdf 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
@@ -18,6 +18,7 @@ package com.ning.billing.invoice.dao;
 
 import java.io.IOException;
 import org.apache.commons.io.IOUtils;
+import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
@@ -25,19 +26,22 @@ import com.google.inject.Stage;
 import com.ning.billing.invoice.glue.InvoiceModuleMock;
 import com.ning.billing.util.eventbus.DefaultEventBusService;
 import com.ning.billing.util.eventbus.BusService;
+import sun.jvm.hotspot.utilities.Assert;
 
+import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
 public abstract class InvoiceDaoTestBase {
     protected InvoiceDao invoiceDao;
     protected InvoiceItemSqlDao invoiceItemDao;
     protected InvoicePaymentSqlDao invoicePaymentDao;
+    protected InvoiceModuleMock module;
 
-    @BeforeClass()
+    @BeforeClass(alwaysRun = true)
     protected void setup() throws IOException {
         // Health check test to make sure MySQL is setup properly
         try {
-            InvoiceModuleMock module = new InvoiceModuleMock();
+            module = new InvoiceModuleMock();
             final String ddl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
             module.createDb(ddl);
 
@@ -52,9 +56,17 @@ public abstract class InvoiceDaoTestBase {
 
             BusService busService = injector.getInstance(BusService.class);
             ((DefaultEventBusService) busService).startBus();
+
+            assertTrue(true);
         }
         catch (Throwable t) {
             fail(t.toString());
         }
     }
+
+    @AfterClass(alwaysRun = true)
+    protected void tearDown() {
+        module.stopDb();
+        assertTrue(true);
+    }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleMock.java b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleMock.java
index a8df7a2..ffa48c5 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleMock.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleMock.java
@@ -50,6 +50,10 @@ public class InvoiceModuleMock extends InvoiceModule {
         helper.initDb(ddl);
     }
 
+    public void stopDb() {
+        helper.stopMysql();
+    }
+
     public IDBI getIDBI() {
         return dbi;
     }
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 67de300..5e8168f 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
@@ -16,18 +16,8 @@
 
 package com.ning.billing.invoice.tests;
 
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-
-import java.math.BigDecimal;
-import java.util.UUID;
-
-import org.joda.time.DateTime;
-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,6 +34,14 @@ import com.ning.billing.invoice.model.DefaultInvoiceGenerator;
 import com.ning.billing.invoice.model.DefaultInvoiceItem;
 import com.ning.billing.invoice.model.InvoiceGenerator;
 import com.ning.billing.invoice.model.InvoiceItemList;
+import org.joda.time.DateTime;
+import org.testng.annotations.Test;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
 
 @Test(groups = {"fast", "invoicing", "invoiceGenerator"})
 public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
@@ -53,7 +51,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
     @Test
     public void testWithNullEventSetAndNullInvoiceSet() {
         UUID accountId = UUID.randomUUID();
-        Invoice invoice = generator.generateInvoice(accountId, null, null, new DateTime(), Currency.USD);
+        Invoice invoice = generator.generateInvoice(accountId, new BillingEventSet(), new InvoiceItemList(), new DateTime(), Currency.USD);
 
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), 0);
@@ -98,6 +96,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), 1);
         assertEquals(invoice.getTotalAmount(), TWENTY);
+        assertEquals(invoice.getInvoiceItems().get(0).getSubscriptionId(), sub.getId());
     }
 
     @Test