killbill-aplcache

Changes

Details

diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java
index b0c9961..1abcf83 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java
@@ -34,6 +34,10 @@ public interface InvoiceItem extends Entity, Comparable<InvoiceItem> {
 
     String getDescription();
 
+    DateTime getStartDate();
+
+    DateTime getEndDate();
+
     BigDecimal getAmount();
 
     Currency getCurrency();
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 1f2adce..f2c30a1 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
@@ -154,7 +154,8 @@ public class TestBasic {
                 h.execute("truncate table notifications");
                 h.execute("truncate table claimed_notifications");
                 h.execute("truncate table invoices");
-                h.execute("truncate table invoice_items");
+                h.execute("truncate table fixed_invoice_items");
+                h.execute("truncate table recurring_invoice_items");
                 h.execute("truncate table tag_definitions");
                 h.execute("truncate table tags");
                 return null;
diff --git a/beatrix/src/test/resources/catalogSample.xml b/beatrix/src/test/resources/catalogSample.xml
index 325f731..c18816f 100644
--- a/beatrix/src/test/resources/catalogSample.xml
+++ b/beatrix/src/test/resources/catalogSample.xml
@@ -200,16 +200,16 @@ Use Cases to do:
 		<plan name="pistol-monthly">
 			<product>Pistol</product>
 			<initialPhases>
-                                <phase type="TRIAL">
-                                        <duration>
-                                                <unit>DAYS</unit>
-                                                <number>30</number>
-                                        </duration>
-                                        <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
-                                        <fixedPrice>
-                                        </fixedPrice>
-                                    <!-- no price implies $0 -->
-                                </phase>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+                    <fixedPrice>
+                    </fixedPrice>
+                    <!-- no price implies $0 -->
+                </phase>
 			</initialPhases>
 			<finalPhase type="EVERGREEN">
 				<duration>
diff --git a/catalog/src/main/java/com/ning/billing/catalog/DefaultDuration.java b/catalog/src/main/java/com/ning/billing/catalog/DefaultDuration.java
index 878497b..0e821ef 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/DefaultDuration.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/DefaultDuration.java
@@ -52,7 +52,7 @@ public class DefaultDuration extends ValidatingConfig<StandaloneCatalog> impleme
 
     @Override
     public DateTime addToDateTime(DateTime dateTime) {
-        if (number < 0) {return null;}
+        if (number == null) {return dateTime;}
 
         switch (unit) {
             case DAYS:
@@ -63,9 +63,9 @@ public class DefaultDuration extends ValidatingConfig<StandaloneCatalog> impleme
                 return dateTime.plusYears(number);
             case UNLIMITED:
                 return dateTime.plusYears(100);
+            default:
+                return dateTime;
         }
-
-        return null;
     }
 
     @Override
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 a5199a9..ee9ec81 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
@@ -158,8 +158,11 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
         } else {
             Date paidThroughDate = (subscription.getPaidThroughDate() == null) ? null : subscription.getPaidThroughDate().toDate();
 
-            subscriptionSqlDao.updateSubscription(subscriptionId.toString(), subscription.getActiveVersion(),
-                                                  ctd.toDate(), paidThroughDate);
+            DateTime chargedThroughDate = subscription.getChargedThroughDate();
+            if (chargedThroughDate == null || chargedThroughDate.isBefore(ctd)) {
+                subscriptionSqlDao.updateSubscription(subscriptionId.toString(), subscription.getActiveVersion(),
+                                                      ctd.toDate(), paidThroughDate);
+            }
         }
     }
 }
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 824b239..e7a26a3 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.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
@@ -44,7 +47,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
 
     private final InvoiceSqlDao invoiceSqlDao;
     private final RecurringInvoiceItemSqlDao recurringInvoiceItemSqlDao;
-    //private final FixedPriceInvoiceItemSqlDao fixedPriceInvoiceItemSqlDao;
+    private final FixedPriceInvoiceItemSqlDao fixedPriceInvoiceItemSqlDao;
     private final InvoicePaymentSqlDao invoicePaymentSqlDao;
     private final NextBillingDateNotifier notifier;
     private final EntitlementBillingApi entitlementBillingApi;
@@ -56,7 +59,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
                              final NextBillingDateNotifier notifier, final EntitlementBillingApi entitlementBillingApi) {
         this.invoiceSqlDao = dbi.onDemand(InvoiceSqlDao.class);
         this.recurringInvoiceItemSqlDao = dbi.onDemand(RecurringInvoiceItemSqlDao.class);
-        //this.fixedPriceInvoiceItemSqlDao = dbi.onDemand(FixedPriceInvoiceItemSqlDao.class);
+        this.fixedPriceInvoiceItemSqlDao = dbi.onDemand(FixedPriceInvoiceItemSqlDao.class);
         this.invoicePaymentSqlDao = dbi.onDemand(InvoicePaymentSqlDao.class);
         this.eventBus = eventBus;
         this.notifier = notifier;
@@ -95,7 +98,10 @@ public class DefaultInvoiceDao implements InvoiceDao {
 
     @Override
     public List<InvoiceItem> getInvoiceItemsByAccount(final UUID accountId) {
-        return recurringInvoiceItemSqlDao.getInvoiceItemsByAccount(accountId.toString());
+        List<InvoiceItem> results = new ArrayList<InvoiceItem>();
+        results.addAll(recurringInvoiceItemSqlDao.getInvoiceItemsByAccount(accountId.toString()));
+        results.addAll(fixedPriceInvoiceItemSqlDao.getInvoiceItemsByAccount(accountId.toString()));
+        return results;
     }
 
     @Override
@@ -147,12 +153,13 @@ public class DefaultInvoiceDao implements InvoiceDao {
                     recurringInvoiceItemDao.batchCreateFromTransaction(recurringInvoiceItems);
 
                     notifyOfFutureBillingEvents(invoiceSqlDao, recurringInvoiceItems);
-                    setChargedThroughDates(invoiceSqlDao, recurringInvoiceItems);
 
                     List<InvoiceItem> fixedPriceInvoiceItems = invoice.getInvoiceItems(FixedPriceInvoiceItem.class);
                     FixedPriceInvoiceItemSqlDao fixedPriceInvoiceItemDao = invoiceDao.become(FixedPriceInvoiceItemSqlDao.class);
                     fixedPriceInvoiceItemDao.batchCreateFromTransaction(fixedPriceInvoiceItems);
 
+                    setChargedThroughDates(invoiceSqlDao, fixedPriceInvoiceItems, recurringInvoiceItems);
+
                     // STEPH Why do we need that? Are the payments not always null at this point?
                     List<InvoicePayment> invoicePayments = invoice.getPayments();
                     InvoicePaymentSqlDao invoicePaymentSqlDao = invoiceDao.become(InvoicePaymentSqlDao.class);
@@ -272,16 +279,30 @@ public class DefaultInvoiceDao implements InvoiceDao {
         }
     }
 
-    private void setChargedThroughDates(final InvoiceSqlDao dao, final Collection<InvoiceItem> invoiceItems) {
-        for (InvoiceItem item : invoiceItems) {
-            if (item instanceof RecurringInvoiceItem) {
-                RecurringInvoiceItem recurringInvoiceItem = (RecurringInvoiceItem) item;
-                if ((recurringInvoiceItem.getEndDate() != null) &&
-                        (recurringInvoiceItem.getAmount() == null ||
-                                recurringInvoiceItem.getAmount().compareTo(BigDecimal.ZERO) >= 0)) {
-                    log.info("Setting CTD for invoice item {} to {}", recurringInvoiceItem.getId().toString(), recurringInvoiceItem.getEndDate().toString());
-                    entitlementBillingApi.setChargedThroughDateFromTransaction(dao, recurringInvoiceItem.getSubscriptionId(), recurringInvoiceItem.getEndDate());
+    private void setChargedThroughDates(final InvoiceSqlDao dao, final Collection<InvoiceItem> fixedPriceItems,
+                                        final Collection<InvoiceItem> recurringItems) {
+        Map<UUID, DateTime> chargeThroughDates = new HashMap<UUID, DateTime>();
+        addInvoiceItemsToChargeThroughDates(chargeThroughDates, fixedPriceItems);
+        addInvoiceItemsToChargeThroughDates(chargeThroughDates, recurringItems);
+
+        for (UUID subscriptionId : chargeThroughDates.keySet()) {
+            DateTime chargeThroughDate = chargeThroughDates.get(subscriptionId);
+            log.info("Setting CTD for subscription {} to {}", subscriptionId.toString(), chargeThroughDate.toString());
+            entitlementBillingApi.setChargedThroughDateFromTransaction(dao, subscriptionId, chargeThroughDate);
+        }
+    }
+
+    private void addInvoiceItemsToChargeThroughDates(Map<UUID, DateTime> chargeThroughDates, Collection<InvoiceItem> items) {
+        for (InvoiceItem item : items) {
+            UUID subscriptionId = item.getSubscriptionId();
+            DateTime endDate = item.getEndDate();
+
+            if (chargeThroughDates.containsKey(subscriptionId)) {
+                if (chargeThroughDates.get(subscriptionId).isBefore(endDate)) {
+                    chargeThroughDates.put(subscriptionId, endDate);
                 }
+            } else {
+                chargeThroughDates.put(subscriptionId, endDate);
             }
         }
     }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.java
index 6070505..c86cc2f 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.java
@@ -68,7 +68,8 @@ public interface FixedPriceInvoiceItemSqlDao extends EntityDao<InvoiceItem> {
                         q.bind("subscriptionId", item.getSubscriptionId().toString());
                         q.bind("planName", item.getPlanName());
                         q.bind("phaseName", item.getPhaseName());
-                        q.bind("date", item.getDate().toDate());
+                        q.bind("startDate", item.getStartDate().toDate());
+                        q.bind("endDate", item.getEndDate().toDate());
                         q.bind("amount", item.getAmount());
                         q.bind("currency", item.getCurrency().toString());
                     }
@@ -85,12 +86,13 @@ public interface FixedPriceInvoiceItemSqlDao extends EntityDao<InvoiceItem> {
             UUID subscriptionId = UUID.fromString(result.getString("subscription_id"));
             String planName = result.getString("plan_name");
             String phaseName = result.getString("phase_name");
-            DateTime date = new DateTime(result.getTimestamp("date"));
+            DateTime startDate = new DateTime(result.getTimestamp("start_date"));
+            DateTime endDate = new DateTime(result.getTimestamp("end_date"));
             BigDecimal amount = result.getBigDecimal("amount");
             Currency currency = Currency.valueOf(result.getString("currency"));
 
-            return new FixedPriceInvoiceItem(id, invoiceId, subscriptionId, planName, phaseName, date,
-                                            amount, currency);
+            return new FixedPriceInvoiceItem(id, invoiceId, subscriptionId, planName, phaseName,
+                                            startDate, endDate, amount, currency);
         }
     }
 }
\ No newline at end of file
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/BillingMode.java b/invoice/src/main/java/com/ning/billing/invoice/model/BillingMode.java
index e197feb..4920b88 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/BillingMode.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/BillingMode.java
@@ -24,12 +24,4 @@ import java.util.List;
 public interface BillingMode {
     List<RecurringInvoiceItemData> calculateInvoiceItemData(DateTime startDate, DateTime endDate, DateTime targetDate, int billingCycleDay, BillingPeriod billingPeriod) throws InvalidDateSequenceException;
     List<RecurringInvoiceItemData> calculateInvoiceItemData(DateTime startDate, DateTime targetDate, int billingCycleDay, BillingPeriod billingPeriod) throws InvalidDateSequenceException;
-
-//    BigDecimal calculateNumberOfBillingCycles(DateTime startDate, DateTime endDate, DateTime targetDate, int billingCycleDay, BillingPeriod billingPeriod) throws InvalidDateSequenceException;
-//
-//    BigDecimal calculateNumberOfBillingCycles(DateTime startDate, DateTime targetDate, int billingCycleDay, BillingPeriod billingPeriod) throws InvalidDateSequenceException;
-//
-//    DateTime calculateEffectiveEndDate(DateTime startDate, DateTime targetDate, int billingCycleDay, BillingPeriod billingPeriod);
-//
-//    DateTime calculateEffectiveEndDate(DateTime startDate, DateTime endDate, DateTime targetDate, int billingCycleDay, BillingPeriod billingPeriod);
 }
\ No newline at end of file
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 b2e3e6e..41a86e1 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
@@ -18,7 +18,6 @@ package com.ning.billing.invoice.model;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
@@ -27,6 +26,7 @@ import java.util.UUID;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.Duration;
 import com.ning.billing.catalog.api.InternationalPrice;
 import com.ning.billing.entitlement.api.billing.BillingModeType;
 import com.ning.billing.invoice.api.InvoiceApiException;
@@ -35,7 +35,6 @@ import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.entitlement.api.billing.BillingEvent;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
-import org.joda.time.MutableDateTime;
 
 import javax.annotation.Nullable;
 
@@ -54,7 +53,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
 
         Collections.sort(events);
 
-        List<InvoiceItem> existingItems = null;
+        List<InvoiceItem> existingItems = new ArrayList<InvoiceItem>();
         if (items != null) {
             existingItems = new ArrayList<InvoiceItem>(items);
             Collections.sort(existingItems);
@@ -205,10 +204,12 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
 
             if (thisEvent.getFixedPrice() != null) {
                 try {
+                    Duration duration = thisEvent.getPlanPhase().getDuration();
+                    DateTime endDate = duration.addToDateTime(thisEvent.getEffectiveDate());
                     BigDecimal fixedPrice = thisEvent.getFixedPrice().getPrice(currency);
                     fixedPriceInvoiceItem = new FixedPriceInvoiceItem(invoiceId, thisEvent.getSubscription().getId(),
                                                                       thisEvent.getPlan().getName(), thisEvent.getPlanPhase().getName(),
-                                                                      thisEvent.getEffectiveDate(), fixedPrice, currency);
+                                                                      thisEvent.getEffectiveDate(), endDate, fixedPrice, currency);
                 } catch (CatalogApiException e) {
                     throw new InvoiceApiException(e, ErrorCode.CAT_NO_PRICE_FOR_CURRENCY, currency.toString());
                 }
@@ -218,33 +219,33 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
         }
     }
 
-    // assumption: startDate is in the user's time zone
-    private DateTime calculateSegmentEndDate(final DateTime startDate, final DateTime nextEndDate,
-                                             final int billCycleDay, final BillingPeriod billingPeriod) {
-        int dayOfMonth = startDate.getDayOfMonth();
-        int maxDayOfMonth = startDate.dayOfMonth().getMaximumValue();
-
-        DateTime nextBillingDate;
-
-        // if the start date is not on the bill cycle day, move it to the nearest following date that works
-        if ((billCycleDay > maxDayOfMonth) || (dayOfMonth == billCycleDay)) {
-            nextBillingDate = startDate.plusMonths(billingPeriod.getNumberOfMonths());
-        } else {
-            MutableDateTime proposedDate = startDate.toMutableDateTime();
-
-            if (dayOfMonth < billCycleDay) {
-                // move the end date forward to the bill cycle date (same month)
-                int effectiveBillCycleDay = (billCycleDay > maxDayOfMonth) ? maxDayOfMonth : billCycleDay;
-                nextBillingDate = proposedDate.dayOfMonth().set(effectiveBillCycleDay).toDateTime();
-            } else {
-                // go to the next month
-                proposedDate = proposedDate.monthOfYear().add(1);
-                maxDayOfMonth = proposedDate.dayOfMonth().getMaximumValue();
-                int effectiveBillCycleDay = (billCycleDay > maxDayOfMonth) ? maxDayOfMonth : billCycleDay;
-                nextBillingDate = proposedDate.dayOfMonth().set(effectiveBillCycleDay).toDateTime();
-            }
-        }
-
-        return nextBillingDate.isAfter(nextEndDate) ? nextEndDate : nextBillingDate;
-    }
+//    // assumption: startDate is in the user's time zone
+//    private DateTime calculateSegmentEndDate(final DateTime startDate, final DateTime nextEndDate,
+//                                             final int billCycleDay, final BillingPeriod billingPeriod) {
+//        int dayOfMonth = startDate.getDayOfMonth();
+//        int maxDayOfMonth = startDate.dayOfMonth().getMaximumValue();
+//
+//        DateTime nextBillingDate;
+//
+//        // if the start date is not on the bill cycle day, move it to the nearest following date that works
+//        if ((billCycleDay > maxDayOfMonth) || (dayOfMonth == billCycleDay)) {
+//            nextBillingDate = startDate.plusMonths(billingPeriod.getNumberOfMonths());
+//        } else {
+//            MutableDateTime proposedDate = startDate.toMutableDateTime();
+//
+//            if (dayOfMonth < billCycleDay) {
+//                // move the end date forward to the bill cycle date (same month)
+//                int effectiveBillCycleDay = (billCycleDay > maxDayOfMonth) ? maxDayOfMonth : billCycleDay;
+//                nextBillingDate = proposedDate.dayOfMonth().set(effectiveBillCycleDay).toDateTime();
+//            } else {
+//                // go to the next month
+//                proposedDate = proposedDate.monthOfYear().add(1);
+//                maxDayOfMonth = proposedDate.dayOfMonth().getMaximumValue();
+//                int effectiveBillCycleDay = (billCycleDay > maxDayOfMonth) ? maxDayOfMonth : billCycleDay;
+//                nextBillingDate = proposedDate.dayOfMonth().set(effectiveBillCycleDay).toDateTime();
+//            }
+//        }
+//
+//        return nextBillingDate.isAfter(nextEndDate) ? nextEndDate : nextBillingDate;
+//    }
 }
\ No newline at end of file
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java
index 2e20fce..2b7c12a 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java
@@ -23,83 +23,23 @@ import org.joda.time.DateTime;
 import java.math.BigDecimal;
 import java.util.UUID;
 
-public class FixedPriceInvoiceItem implements InvoiceItem {
-    private final UUID id;
-    private final UUID invoiceId;
-    private final UUID subscriptionId;
-    private final String planName;
-    private final String phaseName;
-    private DateTime date;
-    private BigDecimal amount;
-    private final Currency currency;
-
-    public FixedPriceInvoiceItem(UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
-                                DateTime date, BigDecimal amount, Currency currency) {
-        this(UUID.randomUUID(), invoiceId, subscriptionId, planName, phaseName,
-             date, amount, currency);
+public class FixedPriceInvoiceItem extends InvoiceItemBase {
+    public FixedPriceInvoiceItem(UUID invoiceId, UUID subscriptionId, String planName, String phaseName, DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
+        super(invoiceId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
     }
 
-    public FixedPriceInvoiceItem(UUID id, UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
-                                DateTime date, BigDecimal amount, Currency currency) {
-        this.id = id;
-        this.invoiceId = invoiceId;
-        this.subscriptionId = subscriptionId;
-        this.planName = planName;
-        this.phaseName = phaseName;
-        this.date = date;
-        this.amount = amount;
-        this.currency = currency;
+    public FixedPriceInvoiceItem(UUID id, UUID invoiceId, UUID subscriptionId, String planName, String phaseName, DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
+        super(id, invoiceId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
     }
 
-//    public FixedPriceInvoiceItem(FixedPriceInvoiceItem that, UUID invoiceId) {
-//        this.id = UUID.randomUUID();
-//        this.invoiceId = invoiceId;
-//        this.subscriptionId = that.getSubscriptionId();
-//        this.planName = that.getPlanName();
-//        this.phaseName = that.getPhaseName();
-//        this.date = that.getDate();
-//        this.amount = that.getAmount();
-//        this.currency = that.getCurrency();
-//    }
-
     @Override
     public InvoiceItem asCredit() {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public UUID getId() {
-        return id;
-    }
-
-    @Override
-    public UUID getInvoiceId() {
-        return invoiceId;
-    }
-
-    @Override
-    public UUID getSubscriptionId() {
-        return subscriptionId;
-    }
-
-    @Override
-    public String getPlanName() {
-        return planName;
-    }
-
-    @Override
-    public String getPhaseName() {
-        return phaseName;
-    }
-
-    @Override
     public String getDescription() {
-        return String.format("%s (fixed price) on %s", getPhaseName(), getDate().toString());
-    }
-
-    @Override
-    public BigDecimal getAmount() {
-        return amount;
+        return String.format("%s (fixed price) on %s", getPhaseName(), getStartDate().toString());
     }
 
     @Override
@@ -107,21 +47,13 @@ public class FixedPriceInvoiceItem implements InvoiceItem {
         int result = subscriptionId != null ? subscriptionId.hashCode() : 0;
         result = 31 * result + (planName != null ? planName.hashCode() : 0);
         result = 31 * result + (phaseName != null ? phaseName.hashCode() : 0);
-        result = 31 * result + (date != null ? date.hashCode() : 0);
+        result = 31 * result + (startDate != null ? startDate.hashCode() : 0);
+        result = 31 * result + (endDate != null ? endDate.hashCode() : 0);
         result = 31 * result + (amount != null ? amount.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
         return result;
     }
 
-    public DateTime getDate() {
-        return date;
-    }
-
-    @Override
-    public Currency getCurrency() {
-        return currency;
-    }
-
     @Override
     public int compareTo(InvoiceItem item) {
         if (!(item instanceof FixedPriceInvoiceItem)) {
@@ -132,92 +64,40 @@ public class FixedPriceInvoiceItem implements InvoiceItem {
         int compareSubscriptions = getSubscriptionId().compareTo(that.getSubscriptionId());
 
         if (compareSubscriptions == 0) {
-            return getDate().compareTo(that.getDate());
+            return getStartDate().compareTo(that.getStartDate());
         } else {
             return compareSubscriptions;
         }
     }
 
-//    @Override
-//    public void subtract(InvoiceItem that) {
-//        // for now, do nothing -- fixed price items aren't subtracted
-//        return;
-//    }
-//
-//    @Override
-//    public boolean duplicates(InvoiceItem item) {
-//        if (!(item instanceof FixedPriceInvoiceItem)) {return false;}
-//
-//        FixedPriceInvoiceItem that = (FixedPriceInvoiceItem) item;
-//        if (!this.getSubscriptionId().equals(that.getSubscriptionId())) {return false;}
-//
-//        if (!this.planName.equals(that.getPlanName())) {return false;}
-//        if (!this.phaseName.equals(that.getPhaseName())) {return false;}
-//
-//        if (!compareNullableBigDecimal(this.getAmount(), that.getAmount())) {return false;}
-//
-//        if (!this.getCurrency().equals(that.getCurrency())) {return false;}
-//
-//        return (this.date.compareTo(that.getDate()) == 0);
-//    }
-//
-//    private boolean compareNullableBigDecimal(@Nullable BigDecimal value1, @Nullable BigDecimal value2) {
-//        if ((value1 == null) && (value2 != null)) {return false;}
-//        if ((value1 != null) && (value2 == null)) {return false;}
-//
-//        if ((value1 != null) && (value2 != null)) {
-//            if (value1.compareTo(value2) != 0) {return false;}
-//        }
-//
-//        return true;
-//    }
-//
-//    /**
-//     * indicates whether the supplied item is a cancelling item for this item
-//     * @param item  the InvoiceItem to be examined
-//     * @return true if the two invoice items cancel each other out (same subscription, same date range, sum of amounts = 0)
-//     */
-//    @Override
-//    public boolean cancels(InvoiceItem item) {
-//        if (!(item instanceof FixedPriceInvoiceItem)) {return false;}
-//
-//        FixedPriceInvoiceItem that = (FixedPriceInvoiceItem) item;
-//
-//        if(!this.getSubscriptionId().equals(that.getSubscriptionId())) {return false;}
-//        if(!this.getDate().equals(that.getDate())) {return false;}
-//
-//        if (!safeCheckForZeroSum(this.getAmount(), that.getAmount())) {return false;}
-//
-//        if(!this.getCurrency().equals(that.getCurrency())) {return false;}
-//
-//        return true;
-//    }
-//
-//    private boolean safeCheckForZeroSum(final BigDecimal value1, final BigDecimal value2) {
-//        if ((value1 == null) && (value2 == null)) {return true;}
-//        if ((value1 == null) ^ (value2 == null)) {return false;}
-//        return (value1.add(value2).compareTo(BigDecimal.ZERO) == 0);
-//    }
-
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        sb.append("InvoiceItem = {").append("id = ").append(id.toString()).append(", ");
-        sb.append("invoiceId = ").append(invoiceId.toString()).append(", ");
-        sb.append("subscriptionId = ").append(subscriptionId.toString()).append(", ");
-        sb.append("planName = ").append(planName).append(", ");
-        sb.append("phaseName = ").append(phaseName).append(", ");
-        sb.append("date = ").append(date.toString()).append(", ");
-
-        sb.append("amount = ");
-        if (amount == null) {
-            sb.append("null");
-        } else {
-            sb.append(amount.toString());
-        }
 
-        sb.append("}");
+        sb.append(phaseName).append(", ");
+        sb.append(startDate.toString()).append(", ");
+        sb.append(endDate.toString()).append(", ");
+        sb.append(amount.toString()).append(", ");
+
         return sb.toString();
+//        StringBuilder sb = new StringBuilder();
+//        sb.append("InvoiceItem = {").append("id = ").append(id.toString()).append(", ");
+//        sb.append("invoiceId = ").append(invoiceId.toString()).append(", ");
+//        sb.append("subscriptionId = ").append(subscriptionId.toString()).append(", ");
+//        sb.append("planName = ").append(planName).append(", ");
+//        sb.append("phaseName = ").append(phaseName).append(", ");
+//        sb.append("startDate = ").append(startDate.toString()).append(", ");
+//        sb.append("endDate = ").append(endDate.toString()).append(", ");
+//
+//        sb.append("amount = ");
+//        if (amount == null) {
+//            sb.append("null");
+//        } else {
+//            sb.append(amount.toString());
+//        }
+//
+//        sb.append("}");
+//        return sb.toString();
     }
 
     @Override
@@ -229,7 +109,8 @@ public class FixedPriceInvoiceItem implements InvoiceItem {
 
         if (amount != null ? !amount.equals(that.amount) : that.amount != null) return false;
         if (currency != that.currency) return false;
-        if (date != null ? !date.equals(that.date) : that.date != null) return false;
+        if (startDate != null ? !startDate.equals(that.startDate) : that.startDate != null) return false;
+        if (endDate != null ? !endDate.equals(that.endDate) : that.endDate != null) return false;
         if (phaseName != null ? !phaseName.equals(that.phaseName) : that.phaseName != null) return false;
         if (planName != null ? !planName.equals(that.planName) : that.planName != null) return false;
         if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null)
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 864aecf..4d3dff9 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
@@ -80,10 +80,13 @@ public class InAdvanceBillingMode implements BillingMode {
     public List<RecurringInvoiceItemData> calculateInvoiceItemData(final DateTime startDate,
                                                                    final DateTime targetDate, final int billingCycleDay,
                                                                    final BillingPeriod billingPeriod) throws InvalidDateSequenceException {
-        if (targetDate.isBefore(startDate)) {throw new InvalidDateSequenceException();}
-
         List<RecurringInvoiceItemData> results = new ArrayList<RecurringInvoiceItemData>();
 
+        if (targetDate.isBefore(startDate)) {
+            // since the target date is before the start date of the event, this should result in no items being generated
+            throw new InvalidDateSequenceException();
+        }
+
         // beginning from the start date, find the first billing date
         DateTime firstBillingCycleDate = calculateBillingCycleDateOnOrAfter(startDate, billingCycleDay);
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java
new file mode 100644
index 0000000..12abdd6
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java
@@ -0,0 +1,93 @@
+package com.ning.billing.invoice.model;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoiceItem;
+import org.joda.time.DateTime;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+public abstract class InvoiceItemBase implements InvoiceItem {
+    protected final UUID id;
+    protected final UUID invoiceId;
+    protected final UUID subscriptionId;
+    protected final String planName;
+    protected final String phaseName;
+    protected final DateTime startDate;
+    protected final DateTime endDate;
+    protected final BigDecimal amount;
+    protected final Currency currency;
+
+    public InvoiceItemBase(UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
+                           DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
+        this(UUID.randomUUID(), invoiceId, subscriptionId, planName, phaseName,
+             startDate, endDate, amount, currency);
+    }
+
+    public InvoiceItemBase(UUID id, UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
+                           DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
+        this.id = id;
+        this.invoiceId = invoiceId;
+        this.subscriptionId = subscriptionId;
+        this.planName = planName;
+        this.phaseName = phaseName;
+        this.startDate = startDate;
+        this.endDate = endDate;
+        this.amount = amount;
+        this.currency = currency;
+    }
+
+    @Override
+    public UUID getId() {
+        return id;
+    }
+
+    @Override
+    public UUID getInvoiceId() {
+        return invoiceId;
+    }
+
+    @Override
+    public UUID getSubscriptionId() {
+        return subscriptionId;
+    }
+
+    @Override
+    public String getPlanName() {
+        return planName;
+    }
+
+    @Override
+    public String getPhaseName() {
+        return phaseName;
+    }
+
+    @Override
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    @Override
+    public DateTime getStartDate() {
+        return startDate;
+    }
+
+    @Override
+    public DateTime getEndDate() {
+        return endDate;
+    }
+
+    @Override
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    @Override
+    public abstract InvoiceItem asCredit();
+
+    @Override
+    public abstract String getDescription();
+
+    @Override
+    public abstract int compareTo(InvoiceItem invoiceItem);
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java
index 8dfbf41..55e5487 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java
@@ -23,17 +23,8 @@ import org.joda.time.DateTime;
 import java.math.BigDecimal;
 import java.util.UUID;
 
-public class RecurringInvoiceItem implements InvoiceItem {
-    private final UUID id;
-    private final UUID invoiceId;
-    private final UUID subscriptionId;
-    private final String planName;
-    private final String phaseName;
-    private DateTime startDate;
-    private DateTime endDate;
-    private BigDecimal amount;
+public class RecurringInvoiceItem extends InvoiceItemBase {
     private final BigDecimal rate;
-    private final Currency currency;
     private final UUID reversedItemId;
 
     public RecurringInvoiceItem(UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
@@ -56,16 +47,9 @@ public class RecurringInvoiceItem implements InvoiceItem {
                                 DateTime startDate, DateTime endDate,
                                 BigDecimal amount, BigDecimal rate,
                                 Currency currency) {
-        this.id = id;
-        this.invoiceId = invoiceId;
-        this.subscriptionId = subscriptionId;
-        this.planName = planName;
-        this.phaseName = phaseName;
-        this.startDate = startDate;
-        this.endDate = endDate;
-        this.amount = amount;
+        super(id, invoiceId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+
         this.rate = rate;
-        this.currency = currency;
         this.reversedItemId = null;
     }
 
@@ -73,16 +57,9 @@ public class RecurringInvoiceItem implements InvoiceItem {
                                 DateTime startDate, DateTime endDate,
                                 BigDecimal amount, BigDecimal rate,
                                 Currency currency, UUID reversedItemId) {
-        this.id = id;
-        this.invoiceId = invoiceId;
-        this.subscriptionId = subscriptionId;
-        this.planName = planName;
-        this.phaseName = phaseName;
-        this.startDate = startDate;
-        this.endDate = endDate;
-        this.amount = amount;
+        super(id, invoiceId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+
         this.rate = rate;
-        this.currency = currency;
         this.reversedItemId = reversedItemId;
     }
 
@@ -94,48 +71,10 @@ public class RecurringInvoiceItem implements InvoiceItem {
     }
 
     @Override
-    public UUID getId() {
-        return id;
-    }
-
-    @Override
-    public UUID getInvoiceId() {
-        return invoiceId;
-    }
-
-    @Override
-    public UUID getSubscriptionId() {
-        return subscriptionId;
-    }
-
-    @Override
-    public String getPlanName() {
-        return planName;
-    }
-
-    @Override
-    public String getPhaseName() {
-        return phaseName;
-    }
-
-    @Override
     public String getDescription() {
         return String.format("%s from %s to %s", phaseName, startDate.toString(), endDate.toString());
     }
 
-    public DateTime getStartDate() {
-        return startDate;
-    }
-
-    public DateTime getEndDate() {
-        return endDate;
-    }
-
-    @Override
-    public BigDecimal getAmount() {
-        return amount;
-    }
-
     public UUID getReversedItemId() {
         return reversedItemId;
     }
@@ -149,11 +88,6 @@ public class RecurringInvoiceItem implements InvoiceItem {
     }
 
     @Override
-    public Currency getCurrency() {
-        return currency;
-    }
-
-    @Override
     public int compareTo(InvoiceItem item) {
         if (item == null) {return -1;}
         if (!(item instanceof RecurringInvoiceItem)) {return -1;}
@@ -212,34 +146,43 @@ public class RecurringInvoiceItem implements InvoiceItem {
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        sb.append("InvoiceItem = {").append("id = ").append(id.toString()).append(", ");
-        sb.append("invoiceId = ").append(invoiceId.toString()).append(", ");
-        sb.append("subscriptionId = ").append(subscriptionId.toString()).append(", ");
-        sb.append("planName = ").append(planName).append(", ");
-        sb.append("phaseName = ").append(phaseName).append(", ");
-        sb.append("startDate = ").append(startDate.toString()).append(", ");
-        if (endDate != null) {
-            sb.append("endDate = ").append(endDate.toString()).append(", ");
-        } else {
-            sb.append("endDate = null");
-        }
-        sb.append("recurringAmount = ");
-        if (amount == null) {
-            sb.append("null");
-        } else {
-            sb.append(amount.toString());
-        }
-        sb.append(", ");
 
-        sb.append("recurringRate = ");
-        if (rate == null) {
-            sb.append("null");
-        } else {
-            sb.append(rate.toString());
-        }
-        sb.append(", ");
+        sb.append(phaseName).append(", ");
+        sb.append(startDate.toString()).append(", ");
+        sb.append(endDate.toString()).append(", ");
+        sb.append(amount.toString()).append(", ");
 
-        sb.append("}");
         return sb.toString();
+
+//        StringBuilder sb = new StringBuilder();
+//        sb.append("InvoiceItem = {").append("id = ").append(id.toString()).append(", ");
+//        sb.append("invoiceId = ").append(invoiceId.toString()).append(", ");
+//        sb.append("subscriptionId = ").append(subscriptionId.toString()).append(", ");
+//        sb.append("planName = ").append(planName).append(", ");
+//        sb.append("phaseName = ").append(phaseName).append(", ");
+//        sb.append("startDate = ").append(startDate.toString()).append(", ");
+//        if (endDate != null) {
+//            sb.append("endDate = ").append(endDate.toString()).append(", ");
+//        } else {
+//            sb.append("endDate = null");
+//        }
+//        sb.append("recurringAmount = ");
+//        if (amount == null) {
+//            sb.append("null");
+//        } else {
+//            sb.append(amount.toString());
+//        }
+//        sb.append(", ");
+//
+//        sb.append("recurringRate = ");
+//        if (rate == null) {
+//            sb.append("null");
+//        } else {
+//            sb.append(rate.toString());
+//        }
+//        sb.append(", ");
+//
+//        sb.append("}");
+//        return sb.toString();
     }
 }
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.sql.stg
index 8e7e7be..90310d5 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.sql.stg
@@ -6,7 +6,8 @@ fields(prefix) ::= <<
   <prefix>subscription_id,
   <prefix>plan_name,
   <prefix>phase_name,
-  <prefix>date,
+  <prefix>start_date,
+  <prefix>end_date,
   <prefix>amount,
   <prefix>currency
 >>
@@ -39,19 +40,19 @@ getInvoiceItemsBySubscription() ::= <<
 create() ::= <<
   INSERT INTO fixed_invoice_items(<fields()>)
   VALUES(:id, :invoiceId, :subscriptionId, :planName, :phaseName,
-         :date, :amount, :currency);
+         :startDate, :endDate, :amount, :currency);
 >>
 
 batchCreateFromTransaction() ::= <<
   INSERT INTO fixed_invoice_items(<fields()>)
   VALUES(:id, :invoiceId, :subscriptionId, :planName, :phaseName,
-         :date, :amount, :currency);
+         :startDate, :endDate, :amount, :currency);
 >>
 
 update() ::= <<
   UPDATE fixed_invoice_items
   SET invoice_id = :invoiceId, subscription_id = :subscriptionId, plan_name = :planName, phase_name = :phaseName,
-      date = :date, amount = :amount, currency = :currency
+      start_date = :startDate, end_date = :endDate, amount = :amount, currency = :currency
   WHERE id = :id;
 >>
 
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 f00e06c..c7712a8 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
@@ -24,7 +24,8 @@ CREATE TABLE fixed_invoice_items (
   subscription_id char(36) NOT NULL,
   plan_name varchar(50) NOT NULL,
   phase_name varchar(50) NOT NULL,
-  date datetime NOT NULL,
+  start_date datetime NOT NULL,
+  end_date datetime NOT NULL,
   amount numeric(10,4) NULL,
   currency char(3) NOT NULL,
   PRIMARY KEY(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 b1c919e..b666ece 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
@@ -648,4 +648,80 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(savedInvoice.getNumberOfItems(), 2);
         assertEquals(savedInvoice.getTotalAmount().compareTo(cheapAmount), 0);
     }
+
+//    @Test
+//    public void testCancellationWithMultipleBillingPeriodsFollowing() throws InvoiceApiException {
+//        UUID accountId = UUID.randomUUID();
+//
+//        BigDecimal fixedValue = FIVE;
+//        DefaultPrice fixedAmount = new DefaultPrice(fixedValue, Currency.USD);
+//        MockInternationalPrice fixedPrice = new MockInternationalPrice(fixedAmount);
+//        MockPlanPhase plan1phase1 = new MockPlanPhase(null, fixedPrice);
+//
+//        BigDecimal trialValue = new BigDecimal("9.95");
+//        DefaultPrice trialAmount = new DefaultPrice(trialValue, Currency.USD);
+//        MockInternationalPrice trialPrice = new MockInternationalPrice(trialAmount);
+//        MockPlanPhase plan2phase1 = new MockPlanPhase(trialPrice, null);
+//
+//        BigDecimal discountValue = new BigDecimal("24.95");
+//        DefaultPrice discountAmount = new DefaultPrice(discountValue, Currency.USD);
+//        MockInternationalPrice discountPrice = new MockInternationalPrice(discountAmount);
+//        MockPlanPhase plan2phase2 = new MockPlanPhase(discountPrice, null);
+//
+//        MockPlan plan1 = new MockPlan();
+//        MockPlan plan2 = new MockPlan();
+//        Subscription subscription = new MockSubscription();
+//        DateTime effectiveDate1 = buildDateTime(2011, 1, 1);
+//
+//        BillingEvent creationEvent = new DefaultBillingEvent(subscription, effectiveDate1, plan1, plan1phase1, fixedPrice,
+//                                                     null, BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
+//                                                     "trial", SubscriptionTransitionType.CREATE);
+//        BillingEventSet events = new BillingEventSet();
+//        events.add(creationEvent);
+//
+//        InvoiceGenerator generator = new DefaultInvoiceGenerator();
+//        InvoiceItemList existingItems;
+//
+//        existingItems = new InvoiceItemList(invoiceDao.getInvoiceItemsByAccount(accountId));
+//        Invoice invoice1 = generator.generateInvoice(accountId, events, existingItems, effectiveDate1, Currency.USD);
+//
+//        assertNotNull(invoice1);
+//        assertEquals(invoice1.getNumberOfItems(), 1);
+//        assertEquals(invoice1.getTotalAmount().compareTo(fixedValue), 0);
+//        invoiceDao.create(invoice1);
+//
+//        DateTime effectiveDate2 = effectiveDate1.plusSeconds(1);
+//        BillingEvent changeEvent = new DefaultBillingEvent(subscription, effectiveDate2, plan2, plan2phase1, null,
+//                                                     trialPrice, BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
+//                                                     "discount", SubscriptionTransitionType.CHANGE);
+//        events.add(changeEvent);
+//
+//        existingItems = new InvoiceItemList(invoiceDao.getInvoiceItemsByAccount(accountId));
+//        Invoice invoice2 = generator.generateInvoice(accountId, events, existingItems, effectiveDate2, Currency.USD);
+//        assertNotNull(invoice2);
+//        assertEquals(invoice2.getNumberOfItems(), 2);
+//        assertEquals(invoice2.getTotalAmount().compareTo(trialValue), 0);
+//        invoiceDao.create(invoice2);
+//
+//        DateTime effectiveDate3 = effectiveDate2.plusMonths(1);
+//        BillingEvent phaseEvent = new DefaultBillingEvent(subscription, effectiveDate3, plan2, plan2phase2, null,
+//                                                     discountPrice, BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
+//                                                     "discount", SubscriptionTransitionType.PHASE);
+//        events.add(phaseEvent);
+//
+//        existingItems = new InvoiceItemList(invoiceDao.getInvoiceItemsByAccount(accountId));
+//        Invoice invoice3 = generator.generateInvoice(accountId, events, existingItems, effectiveDate3, Currency.USD);
+//        assertNotNull(invoice3);
+//        assertEquals(invoice3.getNumberOfItems(), 1);
+//        assertEquals(invoice3.getTotalAmount().compareTo(discountValue), 0);
+//        invoiceDao.create(invoice3);
+//
+//        DateTime effectiveDate4 = effectiveDate3.plusMonths(1);
+//        existingItems = new InvoiceItemList(invoiceDao.getInvoiceItemsByAccount(accountId));
+//        Invoice invoice4 = generator.generateInvoice(accountId, events, existingItems, effectiveDate4, Currency.USD);
+//        assertNotNull(invoice4);
+//        assertEquals(invoice4.getNumberOfItems(), 1);
+//        assertEquals(invoice4.getTotalAmount().compareTo(discountValue), 0);
+//        invoiceDao.create(invoice4);
+//    }
 }
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 442f1a0..79affcc 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
@@ -22,7 +22,6 @@ import com.ning.billing.catalog.MockPlan;
 import com.ning.billing.catalog.MockPlanPhase;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.catalog.api.InternationalPrice;
 import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
@@ -54,7 +53,6 @@ import java.util.UUID;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
-import static org.testng.Assert.fail;
 
 @Test(groups = {"fast", "invoicing", "invoiceGenerator"})
 public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
@@ -487,7 +485,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         assertNotNull(invoice2);
         assertEquals(invoice2.getNumberOfItems(), 1);
         FixedPriceInvoiceItem item = (FixedPriceInvoiceItem) invoice2.getInvoiceItems().get(0);
-        assertEquals(item.getDate().compareTo(changeDate), 0);
+        assertEquals(item.getStartDate().compareTo(changeDate), 0);
    }
 
     @Test
@@ -575,7 +573,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
                                  null, BillingPeriod.MONTHLY, phaseType);
     }
 
-    private MockPlanPhase createMockMonthlyPlanPhase(final BigDecimal recurringRate, final BigDecimal fixedCost,
+    private MockPlanPhase createMockMonthlyPlanPhase(@Nullable BigDecimal recurringRate, final BigDecimal fixedCost,
                                                      final PhaseType phaseType) {
         MockInternationalPrice recurringPrice = (recurringRate == null) ? null : new MockInternationalPrice(new DefaultPrice(recurringRate, Currency.USD));
         MockInternationalPrice fixedPrice = (fixedCost == null) ? null : new MockInternationalPrice(new DefaultPrice(fixedCost, Currency.USD));
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java
index afe95fd..c90bf69 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java
@@ -17,7 +17,6 @@
 package com.ning.billing.invoice.tests;
 
 
-import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.invoice.model.InvoicingConfiguration;
 import org.joda.time.DateTime;
 
diff --git a/util/src/main/java/com/ning/billing/util/globalLocker/GlobalLock.java b/util/src/main/java/com/ning/billing/util/globalLocker/GlobalLock.java
index 62a61a5..12ebc0d 100644
--- a/util/src/main/java/com/ning/billing/util/globalLocker/GlobalLock.java
+++ b/util/src/main/java/com/ning/billing/util/globalLocker/GlobalLock.java
@@ -14,7 +14,7 @@
  * under the License.
  */
 
-package com.ning.billing.util.globalLocker;
+package com.ning.billing.util.globallocker;
 
 public interface GlobalLock
 {
diff --git a/util/src/main/java/com/ning/billing/util/globalLocker/GlobalLocker.java b/util/src/main/java/com/ning/billing/util/globalLocker/GlobalLocker.java
index d6ce0af..5312e09 100644
--- a/util/src/main/java/com/ning/billing/util/globalLocker/GlobalLocker.java
+++ b/util/src/main/java/com/ning/billing/util/globalLocker/GlobalLocker.java
@@ -14,7 +14,7 @@
  * under the License.
  */
 
-package com.ning.billing.util.globalLocker;
+package com.ning.billing.util.globallocker;
 
 public interface GlobalLocker {
 
diff --git a/util/src/main/java/com/ning/billing/util/globalLocker/LockFailedException.java b/util/src/main/java/com/ning/billing/util/globalLocker/LockFailedException.java
index 341edc3..67f83a4 100644
--- a/util/src/main/java/com/ning/billing/util/globalLocker/LockFailedException.java
+++ b/util/src/main/java/com/ning/billing/util/globalLocker/LockFailedException.java
@@ -14,7 +14,7 @@
  * under the License.
  */
 
-package com.ning.billing.util.globalLocker;
+package com.ning.billing.util.globallocker;
 
 public class LockFailedException extends RuntimeException
 {
diff --git a/util/src/main/java/com/ning/billing/util/globalLocker/MySqlGlobalLocker.java b/util/src/main/java/com/ning/billing/util/globalLocker/MySqlGlobalLocker.java
index 762ddc7..f9de358 100644
--- a/util/src/main/java/com/ning/billing/util/globalLocker/MySqlGlobalLocker.java
+++ b/util/src/main/java/com/ning/billing/util/globalLocker/MySqlGlobalLocker.java
@@ -14,7 +14,7 @@
  * under the License.
  */
 
-package com.ning.billing.util.globalLocker;
+package com.ning.billing.util.globallocker;
 
 import com.google.inject.Inject;
 import org.skife.jdbi.v2.Handle;
diff --git a/util/src/main/java/com/ning/billing/util/globalLocker/MySqlGlobalLockerDao.java b/util/src/main/java/com/ning/billing/util/globalLocker/MySqlGlobalLockerDao.java
index 14a02d4..876f6e1 100644
--- a/util/src/main/java/com/ning/billing/util/globalLocker/MySqlGlobalLockerDao.java
+++ b/util/src/main/java/com/ning/billing/util/globalLocker/MySqlGlobalLockerDao.java
@@ -14,7 +14,7 @@
  * under the License.
  */
 
-package com.ning.billing.util.globalLocker;
+package com.ning.billing.util.globallocker;
 
 import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
diff --git a/util/src/test/java/com/ning/billing/util/globalLocker/MockGlobalLocker.java b/util/src/test/java/com/ning/billing/util/globalLocker/MockGlobalLocker.java
index 18336df..90122ec 100644
--- a/util/src/test/java/com/ning/billing/util/globalLocker/MockGlobalLocker.java
+++ b/util/src/test/java/com/ning/billing/util/globalLocker/MockGlobalLocker.java
@@ -14,7 +14,7 @@
  * under the License.
  */
 
-package com.ning.billing.util.globalLocker;
+package com.ning.billing.util.globallocker;
 
 public class MockGlobalLocker implements GlobalLocker {
 
diff --git a/util/src/test/java/com/ning/billing/util/globalLocker/TestMysqlGlobalLocker.java b/util/src/test/java/com/ning/billing/util/globalLocker/TestMysqlGlobalLocker.java
index b797522..f4c7960 100644
--- a/util/src/test/java/com/ning/billing/util/globalLocker/TestMysqlGlobalLocker.java
+++ b/util/src/test/java/com/ning/billing/util/globalLocker/TestMysqlGlobalLocker.java
@@ -14,7 +14,7 @@
  * under the License.
  */
 
-package com.ning.billing.util.globalLocker;
+package com.ning.billing.util.globallocker;
 
 import java.io.IOException;
 import java.util.UUID;
@@ -32,7 +32,7 @@ import org.testng.annotations.Test;
 import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
 import com.ning.billing.dbi.MysqlTestingHelper;
-import com.ning.billing.util.globalLocker.GlobalLocker.LockerService;
+import com.ning.billing.util.globallocker.GlobalLocker.LockerService;
 
 @Guice(modules=TestMysqlGlobalLocker.TestMysqlGlobalLockerModule.class)
 public class TestMysqlGlobalLocker {
@@ -60,7 +60,7 @@ public class TestMysqlGlobalLocker {
 
         final String lockName = UUID.randomUUID().toString();
 
-        GlobalLocker locker = new  MySqlGlobalLocker(dbi);
+        GlobalLocker locker = new MySqlGlobalLocker(dbi);
         GlobalLock lock = locker.lockWithNumberOfTries(LockerService.INVOICE, lockName, 3);
 
         dbi.inTransaction(new TransactionCallback<Void>() {