killbill-memoizeit

invoice: usage rework 1. Rework json for consumable in arrear

2/17/2018 12:56:42 AM

Details

diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalCapacityUsageInArrear.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalCapacityUsageInArrear.java
index a00f2c2..ed2236c 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalCapacityUsageInArrear.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalCapacityUsageInArrear.java
@@ -33,8 +33,8 @@ import org.killbill.billing.catalog.api.Usage;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.model.UsageInvoiceItem;
-import org.killbill.billing.invoice.usage.details.aggregate.UsageCapacityInArrearAggregate;
-import org.killbill.billing.invoice.usage.details.aggregate.UsageInArrearAggregate;
+import org.killbill.billing.invoice.usage.details.UsageCapacityInArrearAggregate;
+import org.killbill.billing.invoice.usage.details.UsageInArrearAggregate;
 import org.killbill.billing.invoice.usage.details.UsageInArrearTierUnitDetail;
 import org.killbill.billing.usage.RawUsage;
 import org.killbill.billing.usage.api.RolledUpUnit;
@@ -63,7 +63,7 @@ public class ContiguousIntervalCapacityUsageInArrear extends ContiguousIntervalU
     }
 
     @Override
-    protected void populateResults(final LocalDate startDate, final LocalDate endDate, final BigDecimal billedUsage, final BigDecimal toBeBilledUsage, final UsageInArrearDetail toBeBilledUsageDetails, final boolean areAllBilledItemsWithDetails, final List<InvoiceItem> result) {
+    protected void populateResults(final LocalDate startDate, final LocalDate endDate, final BigDecimal billedUsage, final BigDecimal toBeBilledUsage, final UsageInArrearAggregate toBeBilledUsageDetails, final boolean areAllBilledItemsWithDetails, final List<InvoiceItem> result) throws InvoiceApiException {
         // Compute final amount by subtracting  amount that was already billed.
         final BigDecimal amountToBill = toBeBilledUsage.subtract(billedUsage);
 
@@ -72,6 +72,10 @@ public class ContiguousIntervalCapacityUsageInArrear extends ContiguousIntervalU
             final InvoiceItem item = new UsageInvoiceItem(invoiceId, accountId, getBundleId(), getSubscriptionId(), getPlanName(),
                                                           getPhaseName(), usage.getName(), startDate, endDate, amountToBill, null, getCurrency(), null, itemDetails);
             result.add(item);
+        } else if (amountToBill.compareTo(BigDecimal.ZERO) < 0) {
+            throw new InvoiceApiException(ErrorCode.UNEXPECTED_ERROR,
+                                          String.format("ILLEGAL INVOICING STATE: Usage period start='%s', end='%s', previously billed amount='%.2f', new proposed amount='%.2f'",
+                                                        startDate, endDate, billedUsage, toBeBilledUsage));
         }
     }
 
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableUsageInArrear.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableUsageInArrear.java
index 782559c..eabec9e 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableUsageInArrear.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableUsageInArrear.java
@@ -31,17 +31,15 @@ import org.joda.time.LocalDate;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.CatalogApiException;
-import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.catalog.api.Tier;
 import org.killbill.billing.catalog.api.TierBlockPolicy;
 import org.killbill.billing.catalog.api.TieredBlock;
 import org.killbill.billing.catalog.api.Usage;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.model.UsageInvoiceItem;
-import org.killbill.billing.invoice.usage.details.aggregate.UsageConsumableInArrearAggregate;
-import org.killbill.billing.invoice.usage.details.aggregate.UsageConsumableInArrearTierUnitAggregate;
-import org.killbill.billing.invoice.usage.details.aggregate.UsageInArrearAggregate;
+import org.killbill.billing.invoice.usage.details.UsageConsumableInArrearAggregate;
+import org.killbill.billing.invoice.usage.details.UsageConsumableInArrearTierUnitAggregate;
+import org.killbill.billing.invoice.usage.details.UsageInArrearAggregate;
 import org.killbill.billing.usage.RawUsage;
 import org.killbill.billing.usage.api.RolledUpUnit;
 import org.killbill.billing.util.config.definition.InvoiceConfig.UsageDetailMode;
@@ -81,9 +79,11 @@ public class ContiguousIntervalConsumableUsageInArrear extends ContiguousInterva
 
         if (amountToBill.compareTo(BigDecimal.ZERO) > 0) {
             if (UsageDetailMode.DETAIL == usageDetailMode) {
+
                 for (UsageConsumableInArrearTierUnitAggregate toBeBilledUsageDetail : ((UsageConsumableInArrearAggregate) toBeBilledUsageDetails).getTierDetails()) {
+                    final String itemDetails = toJson(toBeBilledUsageDetail);
                     final InvoiceItem item = new UsageInvoiceItem(invoiceId, accountId, getBundleId(), getSubscriptionId(), getPlanName(),
-                                                                  getPhaseName(), usage.getName(), startDate, endDate, toBeBilledUsageDetail.getAmount(), toBeBilledUsageDetail.getTierPrice(), getCurrency(), toBeBilledUsageDetail.getQuantity(), toBeBilledUsageDetail.getTierUnit());
+                                                                  getPhaseName(), usage.getName(), startDate, endDate, toBeBilledUsageDetail.getAmount(), toBeBilledUsageDetail.getTierPrice(), getCurrency(), toBeBilledUsageDetail.getQuantity(), itemDetails);
                     result.add(item);
                 }
             } else {
@@ -92,6 +92,10 @@ public class ContiguousIntervalConsumableUsageInArrear extends ContiguousInterva
                                                               getPhaseName(), usage.getName(), startDate, endDate, amountToBill, null, getCurrency(), null, itemDetails);
                 result.add(item);
             }
+        } else if (amountToBill.compareTo(BigDecimal.ZERO) < 0) {
+            throw new InvoiceApiException(ErrorCode.UNEXPECTED_ERROR,
+                                          String.format("ILLEGAL INVOICING STATE: Usage period start='%s', end='%s', previously billed amount='%.2f', new proposed amount='%.2f'",
+                                                        startDate, endDate, billedUsage, toBeBilledUsage));
         }
     }
 
@@ -124,7 +128,6 @@ public class ContiguousIntervalConsumableUsageInArrear extends ContiguousInterva
         return toBeBilledUsageDetails;
     }
 
-
     @VisibleForTesting
     List<UsageConsumableInArrearTierUnitAggregate> getBilledDetailsForUnitType(final Iterable<InvoiceItem> billedItems, final String unitType) {
 
@@ -136,39 +139,14 @@ public class ContiguousIntervalConsumableUsageInArrear extends ContiguousInterva
 
             if (usageDetailMode == UsageDetailMode.DETAIL) {
 
-                final Currency currency = getCurrency();
-
-                final String biUnitType = bi.getItemDetails();
-                if (!biUnitType.equals(unitType)) {
+                final UsageConsumableInArrearTierUnitAggregate targetTierUnitDetail = fromJson(bi.getItemDetails(), new TypeReference<UsageConsumableInArrearTierUnitAggregate>() {});
+                if (!targetTierUnitDetail.getTierUnit().equals(unitType)) {
                     continue;
                 }
 
-                int tierLevel = 0;
-                TieredBlock targetTier = null;
-                for (Tier tier : usage.getTiers()) {
-                    tierLevel++;
-                    for (TieredBlock tierBlock : tier.getTieredBlocks()) {
-                        if (tierBlock.getUnit().getName().equals(unitType)) {
-                            try {
-                                if (tierBlock.getPrice().getPrice(currency).compareTo(bi.getRate()) == 0) {
-                                    targetTier = tierBlock;
-                                    break;
-                                }
-                            } catch (CatalogApiException e) {
-                                throw new IllegalStateException(String.format("Failed to extract catalog price for currency '%s'", currency), e);
-                            }
-                        }
-                    }
-                    if (targetTier != null) {
-                        break;
-                    }
-                }
-
-                Preconditions.checkState(targetTier != null, "Could not extract tier level matching usage='%s', unit='%s', rate='%s' ", usage.getName(), unitType, bi.getRate());
-
-                tierDetails.add(new UsageConsumableInArrearTierUnitAggregate(tierLevel, biUnitType, bi.getRate(), targetTier.getSize().intValue(), bi.getQuantity(), bi.getAmount()));
+                tierDetails.add(new UsageConsumableInArrearTierUnitAggregate(targetTierUnitDetail.getTier(), targetTierUnitDetail.getTierUnit(), bi.getRate(), targetTierUnitDetail.getQuantity(), bi.getQuantity(), bi.getAmount()));
             } else {
-                final UsageConsumableInArrearAggregate usageDetail = fromJson(bi.getItemDetails());
+                final UsageConsumableInArrearAggregate usageDetail = fromJson(bi.getItemDetails(), new TypeReference<UsageConsumableInArrearAggregate>() {});
                 tierDetails.addAll(usageDetail.getTierDetails());
             }
         }
@@ -232,10 +210,10 @@ public class ContiguousIntervalConsumableUsageInArrear extends ContiguousInterva
                     final Integer previousUsageQuantity = tierNum <= lastPreviousUsageTier ? previousUsage.get(tierNum - 1).getQuantity() : 0;
                     if (tierNum < lastPreviousUsageTier) {
                         Preconditions.checkState(nbUsedTierBlocks == previousUsageQuantity, "Expected usage for tier='%d', unit='%s' to be full, instead found units='[%d/%d]'",
-                                                                                                          tierNum, tieredBlock.getUnit().getName(), nbUsedTierBlocks, previousUsageQuantity);
+                                                 tierNum, tieredBlock.getUnit().getName(), nbUsedTierBlocks, previousUsageQuantity);
                     } else {
                         Preconditions.checkState(nbUsedTierBlocks - previousUsageQuantity >= 0, "Expected usage for tier='%d', unit='%s' to contain at least as mush as current usage, instead found units='[%d/%d]",
-                                                                                                              tierNum, tieredBlock.getUnit().getName(), nbUsedTierBlocks, previousUsageQuantity);
+                                                 tierNum, tieredBlock.getUnit().getName(), nbUsedTierBlocks, previousUsageQuantity);
                     }
                     nbUsedTierBlocks = nbUsedTierBlocks - previousUsageQuantity;
                 }
@@ -286,11 +264,11 @@ public class ContiguousIntervalConsumableUsageInArrear extends ContiguousInterva
         return sb.toString();
     }
 
-    public static UsageConsumableInArrearAggregate fromJson(String itemDetails) {
-        UsageConsumableInArrearAggregate result = null;
+    public static <T> T fromJson(String itemDetails, TypeReference<T> ref) {
+        T result = null;
         if (itemDetails != null) {
             try {
-                result = objectMapper.readValue(itemDetails, new TypeReference<UsageConsumableInArrearAggregate>() {});
+                result = objectMapper.readValue(itemDetails, ref);
             } catch (IOException e) {
                 Preconditions.checkState(false, e.getMessage());
             }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalUsageInArrear.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalUsageInArrear.java
index 1d0acf2..e828849 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalUsageInArrear.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalUsageInArrear.java
@@ -41,7 +41,7 @@ import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.generator.BillingIntervalDetail;
 import org.killbill.billing.invoice.model.UsageInvoiceItem;
-import org.killbill.billing.invoice.usage.details.aggregate.UsageInArrearAggregate;
+import org.killbill.billing.invoice.usage.details.UsageInArrearAggregate;
 import org.killbill.billing.junction.BillingEvent;
 import org.killbill.billing.usage.RawUsage;
 import org.killbill.billing.usage.api.RolledUpUnit;
@@ -449,7 +449,7 @@ public abstract class ContiguousIntervalUsageInArrear {
         }
     }
 
-    protected String toJson(final UsageInArrearAggregate usageInArrearAggregate) {
+    protected String toJson(final Object usageInArrearAggregate) {
         try {
             return objectMapper.writeValueAsString(usageInArrearAggregate);
         } catch (JsonProcessingException e) {
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalCapacityInArrear.java b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalCapacityInArrear.java
index 3570600..043d29b 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalCapacityInArrear.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalCapacityInArrear.java
@@ -38,7 +38,7 @@ import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
 import org.killbill.billing.invoice.model.UsageInvoiceItem;
 import org.killbill.billing.invoice.usage.ContiguousIntervalUsageInArrear.UsageInArrearItemsAndNextNotificationDate;
-import org.killbill.billing.invoice.usage.details.aggregate.UsageCapacityInArrearAggregate;
+import org.killbill.billing.invoice.usage.details.UsageCapacityInArrearAggregate;
 import org.killbill.billing.invoice.usage.details.UsageInArrearTierUnitDetail;
 import org.killbill.billing.junction.BillingEvent;
 import org.killbill.billing.usage.RawUsage;
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalConsumableInArrear.java b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalConsumableInArrear.java
index 8bbf5f5..c2a883b 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalConsumableInArrear.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalConsumableInArrear.java
@@ -40,8 +40,8 @@ import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
 import org.killbill.billing.invoice.model.UsageInvoiceItem;
 import org.killbill.billing.invoice.usage.ContiguousIntervalUsageInArrear.UsageInArrearItemsAndNextNotificationDate;
-import org.killbill.billing.invoice.usage.details.aggregate.UsageConsumableInArrearAggregate;
-import org.killbill.billing.invoice.usage.details.aggregate.UsageConsumableInArrearTierUnitAggregate;
+import org.killbill.billing.invoice.usage.details.UsageConsumableInArrearAggregate;
+import org.killbill.billing.invoice.usage.details.UsageConsumableInArrearTierUnitAggregate;
 import org.killbill.billing.junction.BillingEvent;
 import org.killbill.billing.usage.RawUsage;
 import org.killbill.billing.usage.api.RolledUpUsage;
@@ -86,7 +86,6 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         super.beforeMethod();
     }
 
-
     @Test(groups = "fast")
     public void testBilledDetailsForUnitType() throws JsonProcessingException {
 
@@ -97,9 +96,9 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         final DefaultTier tier = createDefaultTierWithBlocks(block);
         final DefaultUsage usage = createConsumableInArrearUsage(usageName, BillingPeriod.MONTHLY, TierBlockPolicy.ALL_TIERS, tier);
         final ContiguousIntervalConsumableUsageInArrear intervalConsumableInArrear = createContiguousIntervalConsumableInArrear(usage, ImmutableList.<RawUsage>of(), targetDate, false,
-                                                                                                                      createMockBillingEvent(targetDate.toDateTimeAtStartOfDay(DateTimeZone.UTC),
-                                                                                                                                             BillingPeriod.MONTHLY,
-                                                                                                                                             Collections.<Usage>emptyList()));
+                                                                                                                                createMockBillingEvent(targetDate.toDateTimeAtStartOfDay(DateTimeZone.UTC),
+                                                                                                                                                       BillingPeriod.MONTHLY,
+                                                                                                                                                       Collections.<Usage>emptyList()));
 
         final UsageConsumableInArrearTierUnitAggregate detail1 = new UsageConsumableInArrearTierUnitAggregate(3, "FOO", new BigDecimal("0.50"), 1, 700);
         final UsageConsumableInArrearTierUnitAggregate detail2 = new UsageConsumableInArrearTierUnitAggregate(2, "FOO", BigDecimal.ONE, 1, 500);
@@ -111,13 +110,12 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
 
         final UsageConsumableInArrearAggregate usageConsumableInArrearDetail = new UsageConsumableInArrearAggregate(existingUsage);
 
-        final String existingUsageJson =  objectMapper.writeValueAsString(usageConsumableInArrearDetail);
+        final String existingUsageJson = objectMapper.writeValueAsString(usageConsumableInArrearDetail);
 
         final List<InvoiceItem> existingItems = new ArrayList<InvoiceItem>();
         final InvoiceItem ii1 = new UsageInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, usageName, new LocalDate(2014, 03, 20), new LocalDate(2014, 04, 15), new BigDecimal("570.00"), null, currency, null, existingUsageJson);
         existingItems.add(ii1);
 
-
         final List<UsageConsumableInArrearTierUnitAggregate> aggregateDetails = intervalConsumableInArrear.getBilledDetailsForUnitType(existingItems, "FOO");
         assertEquals(aggregateDetails.size(), 3);
         assertEquals(aggregateDetails.get(0).getTier(), 1);
@@ -142,8 +140,8 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         final LocalDate targetDate = startDate.plusDays(1);
         final ContiguousIntervalUsageInArrear intervalConsumableInArrear = createContiguousIntervalConsumableInArrear(usage, ImmutableList.<RawUsage>of(), targetDate, false,
                                                                                                                       createMockBillingEvent(targetDate.toDateTimeAtStartOfDay(DateTimeZone.UTC),
-                                                                                                                                                  BillingPeriod.MONTHLY,
-                                                                                                                                                  Collections.<Usage>emptyList())
+                                                                                                                                             BillingPeriod.MONTHLY,
+                                                                                                                                             Collections.<Usage>emptyList())
                                                                                                                      );
 
         final List<InvoiceItem> existingUsage = Lists.newArrayList();
@@ -168,7 +166,6 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         assertEquals(result.compareTo(BigDecimal.TEN.add(BigDecimal.TEN)), 0);
     }
 
-
     @Test(groups = "fast")
     public void testComputeBilledUsageSizeOneWith_ALL_TIERS() throws CatalogApiException {
 
@@ -178,7 +175,6 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         final DefaultTieredBlock block2 = createDefaultTieredBlock("unit", 1, 100, new BigDecimal("1.0"));
         final DefaultTier tier2 = createDefaultTierWithBlocks(block2);
 
-
         final DefaultTieredBlock block3 = createDefaultTieredBlock("unit", 1, 1000, new BigDecimal("0.5"));
         final DefaultTier tier3 = createDefaultTierWithBlocks(block3);
         final DefaultUsage usage = createConsumableInArrearUsage(usageName, BillingPeriod.MONTHLY, TierBlockPolicy.ALL_TIERS, tier1, tier2, tier3);
@@ -186,10 +182,10 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         final LocalDate targetDate = new LocalDate(2014, 03, 20);
 
         final ContiguousIntervalConsumableUsageInArrear intervalConsumableInArrear = createContiguousIntervalConsumableInArrear(usage, ImmutableList.<RawUsage>of(), targetDate, false,
-                                                                                                                      createMockBillingEvent(targetDate.toDateTimeAtStartOfDay(DateTimeZone.UTC),
-                                                                                                                                             BillingPeriod.MONTHLY,
-                                                                                                                                             Collections.<Usage>emptyList())
-                                                                                                                     );
+                                                                                                                                createMockBillingEvent(targetDate.toDateTimeAtStartOfDay(DateTimeZone.UTC),
+                                                                                                                                                       BillingPeriod.MONTHLY,
+                                                                                                                                                       Collections.<Usage>emptyList())
+                                                                                                                               );
 
         List<UsageConsumableInArrearTierUnitAggregate> result = intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("unit", 111L), ImmutableList.<UsageConsumableInArrearTierUnitAggregate>of());
         assertEquals(result.size(), 3);
@@ -199,8 +195,6 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         assertEquals(result.get(2).getAmount(), new BigDecimal("0.5"));
     }
 
-
-
     @Test(groups = "fast")
     public void testComputeBilledUsageWith_ALL_TIERS() throws CatalogApiException {
 
@@ -214,10 +208,10 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         final LocalDate targetDate = new LocalDate(2014, 03, 20);
 
         final ContiguousIntervalConsumableUsageInArrear intervalConsumableInArrear = createContiguousIntervalConsumableInArrear(usage, ImmutableList.<RawUsage>of(), targetDate, false,
-                                                                                                                      createMockBillingEvent(targetDate.toDateTimeAtStartOfDay(DateTimeZone.UTC),
-                                                                                                                                             BillingPeriod.MONTHLY,
-                                                                                                                                             Collections.<Usage>emptyList())
-                                                                                                                     );
+                                                                                                                                createMockBillingEvent(targetDate.toDateTimeAtStartOfDay(DateTimeZone.UTC),
+                                                                                                                                                       BillingPeriod.MONTHLY,
+                                                                                                                                                       Collections.<Usage>emptyList())
+                                                                                                                               );
 
         List<UsageConsumableInArrearTierUnitAggregate> result = intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("unit", 5325L), ImmutableList.<UsageConsumableInArrearTierUnitAggregate>of());
         assertEquals(result.size(), 2);
@@ -227,8 +221,6 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         assertEquals(result.get(1).getAmount(), new BigDecimal("5"));
     }
 
-
-
     @Test(groups = "fast")
     public void testComputeBilledUsageWith_TOP_TIER() throws CatalogApiException {
 
@@ -246,10 +238,10 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         final LocalDate targetDate = new LocalDate(2014, 03, 20);
 
         final ContiguousIntervalConsumableUsageInArrear intervalConsumableInArrear = createContiguousIntervalConsumableInArrear(usage, ImmutableList.<RawUsage>of(), targetDate, false,
-                                                                                                                      createMockBillingEvent(targetDate.toDateTimeAtStartOfDay(DateTimeZone.UTC),
-                                                                                                                                             BillingPeriod.MONTHLY,
-                                                                                                                                             Collections.<Usage>emptyList())
-                                                                                                                     );
+                                                                                                                                createMockBillingEvent(targetDate.toDateTimeAtStartOfDay(DateTimeZone.UTC),
+                                                                                                                                                       BillingPeriod.MONTHLY,
+                                                                                                                                                       Collections.<Usage>emptyList())
+                                                                                                                               );
         //
         // In this model unit amount is first used to figure out which tier we are in, and then we price all unit at that 'target' tier
         //
@@ -275,7 +267,6 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         assertEquals(inputLastTier.get(0).getAmount(), new BigDecimal("150.0"));
     }
 
-
     @Test(groups = "fast")
     public void testComputeBilledUsageSizeOneWith_TOP_TIER() throws CatalogApiException {
 
@@ -285,7 +276,6 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         final DefaultTieredBlock block2 = createDefaultTieredBlock("unit", 1, 100, new BigDecimal("1.0"));
         final DefaultTier tier2 = createDefaultTierWithBlocks(block2);
 
-
         final DefaultTieredBlock block3 = createDefaultTieredBlock("unit", 1, 1000, new BigDecimal("0.5"));
         final DefaultTier tier3 = createDefaultTierWithBlocks(block3);
         final DefaultUsage usage = createConsumableInArrearUsage(usageName, BillingPeriod.MONTHLY, TierBlockPolicy.TOP_TIER, tier1, tier2, tier3);
@@ -293,10 +283,10 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         final LocalDate targetDate = new LocalDate(2014, 03, 20);
 
         final ContiguousIntervalConsumableUsageInArrear intervalConsumableInArrear = createContiguousIntervalConsumableInArrear(usage, ImmutableList.<RawUsage>of(), targetDate, false,
-                                                                                                                      createMockBillingEvent(targetDate.toDateTimeAtStartOfDay(DateTimeZone.UTC),
-                                                                                                                                             BillingPeriod.MONTHLY,
-                                                                                                                                             Collections.<Usage>emptyList())
-                                                                                                                     );
+                                                                                                                                createMockBillingEvent(targetDate.toDateTimeAtStartOfDay(DateTimeZone.UTC),
+                                                                                                                                                       BillingPeriod.MONTHLY,
+                                                                                                                                                       Collections.<Usage>emptyList())
+                                                                                                                               );
 
         List<UsageConsumableInArrearTierUnitAggregate> result = intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("unit", 111L), ImmutableList.<UsageConsumableInArrearTierUnitAggregate>of());
         assertEquals(result.size(), 1);
@@ -305,9 +295,6 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         assertEquals(result.get(0).getAmount(), new BigDecimal("55.5"));
     }
 
-
-
-
     @Test(groups = "fast")
     public void testComputeMissingItems() throws CatalogApiException, IOException, InvoiceApiException {
 
@@ -328,7 +315,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
 
         final LocalDate targetDate = endDate;
 
-        final BillingEvent event1 = createMockBillingEvent(startDate.toDateTimeAtStartOfDay(DateTimeZone.UTC),BillingPeriod.MONTHLY, Collections.<Usage>emptyList());
+        final BillingEvent event1 = createMockBillingEvent(startDate.toDateTimeAtStartOfDay(DateTimeZone.UTC), BillingPeriod.MONTHLY, Collections.<Usage>emptyList());
         final BillingEvent event2 = createMockBillingEvent(endDate.toDateTimeAtStartOfDay(DateTimeZone.UTC), BillingPeriod.MONTHLY, Collections.<Usage>emptyList());
 
         final ContiguousIntervalUsageInArrear intervalConsumableInArrear = createContiguousIntervalConsumableInArrear(usage, rawUsages, targetDate, true, event1, event2);
@@ -397,10 +384,8 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         final DefaultTieredBlock tieredBlock2 = createDefaultTieredBlock("unit2", 10, 1000, BigDecimal.ONE);
         final DefaultTier tier = createDefaultTierWithBlocks(tieredBlock1, tieredBlock2);
 
-
         final DefaultUsage usage = createConsumableInArrearUsage(usageName, BillingPeriod.MONTHLY, TierBlockPolicy.ALL_TIERS, tier);
 
-
         final LocalDate t0 = new LocalDate(2015, 03, BCD);
         final BillingEvent eventT0 = createMockBillingEvent(t0.toDateTimeAtStartOfDay(DateTimeZone.UTC), BillingPeriod.MONTHLY, Collections.<Usage>emptyList());
 
@@ -415,7 +400,6 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
 
         final LocalDate targetDate = t3;
 
-
         // Prev t0
         final RawUsage raw1 = new DefaultRawUsage(subscriptionId, new LocalDate(2015, 03, 01), "unit", 12L);
 
@@ -437,30 +421,27 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
 
         final ContiguousIntervalUsageInArrear intervalConsumableInArrear = createContiguousIntervalConsumableInArrear(usage, rawUsage, targetDate, true, eventT0, eventT1, eventT2, eventT3);
 
-
-        final List<RolledUpUsage> unsortedRolledUpUsage =  intervalConsumableInArrear.getRolledUpUsage();
+        final List<RolledUpUsage> unsortedRolledUpUsage = intervalConsumableInArrear.getRolledUpUsage();
         Assert.assertEquals(unsortedRolledUpUsage.size(), 2);
 
         final List<RolledUpUsage> rolledUpUsage = TEST_ROLLED_UP_FIRST_USAGE_ORDERING.sortedCopy(unsortedRolledUpUsage);
 
         Assert.assertEquals(rolledUpUsage.get(0).getStart().compareTo(t0), 0);
         Assert.assertEquals(rolledUpUsage.get(0).getEnd().compareTo(t1), 0);
-        Assert.assertEquals(rolledUpUsage.get(0).getRolledUpUnits().size(),1);
+        Assert.assertEquals(rolledUpUsage.get(0).getRolledUpUnits().size(), 1);
         Assert.assertEquals(rolledUpUsage.get(0).getRolledUpUnits().get(0).getUnitType(), "unit");
         Assert.assertEquals(rolledUpUsage.get(0).getRolledUpUnits().get(0).getAmount(), new Long(10L));
 
         Assert.assertEquals(rolledUpUsage.get(1).getStart().compareTo(t2), 0);
         Assert.assertEquals(rolledUpUsage.get(1).getEnd().compareTo(t3), 0);
-        Assert.assertEquals(rolledUpUsage.get(1).getRolledUpUnits().size(),2);
+        Assert.assertEquals(rolledUpUsage.get(1).getRolledUpUnits().size(), 2);
         Assert.assertEquals(rolledUpUsage.get(1).getRolledUpUnits().get(0).getUnitType(), "unit");
         Assert.assertEquals(rolledUpUsage.get(1).getRolledUpUnits().get(0).getAmount(), new Long(20L));
         Assert.assertEquals(rolledUpUsage.get(1).getRolledUpUnits().get(1).getUnitType(), "unit2");
         Assert.assertEquals(rolledUpUsage.get(1).getRolledUpUnits().get(1).getAmount(), new Long(21L));
     }
 
-
-
-    @Test(groups = "fast", description="See https://github.com/killbill/killbill/issues/706")
+    @Test(groups = "fast", description = "See https://github.com/killbill/killbill/issues/706")
     public void testWithRawUsageStartDateAfterEndDate() throws CatalogApiException {
 
         final LocalDate startDate = new LocalDate(2014, 10, 16);
@@ -476,12 +457,9 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         final DefaultTier tier = createDefaultTierWithBlocks(block);
         final DefaultUsage usage = createConsumableInArrearUsage(usageName, BillingPeriod.MONTHLY, TierBlockPolicy.ALL_TIERS, tier);
 
-
-        final BillingEvent event1 = createMockBillingEvent(startDate.toDateTimeAtStartOfDay(DateTimeZone.UTC),BillingPeriod.MONTHLY, Collections.<Usage>emptyList());
+        final BillingEvent event1 = createMockBillingEvent(startDate.toDateTimeAtStartOfDay(DateTimeZone.UTC), BillingPeriod.MONTHLY, Collections.<Usage>emptyList());
         final BillingEvent event2 = createMockBillingEvent(new LocalDate(2014, 10, 16).toDateTimeAtStartOfDay(DateTimeZone.UTC), BillingPeriod.MONTHLY, Collections.<Usage>emptyList());
 
-
-
         final ContiguousIntervalUsageInArrear intervalConsumableInArrear = usage.getUsageType() == UsageType.CAPACITY ?
                                                                            new ContiguousIntervalCapacityUsageInArrear(usage, accountId, invoiceId, rawUsages, targetDate, rawUsageStartDate, usageDetailMode, internalCallContext) :
                                                                            new ContiguousIntervalConsumableUsageInArrear(usage, accountId, invoiceId, rawUsages, targetDate, rawUsageStartDate, usageDetailMode, internalCallContext);
@@ -503,10 +481,10 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         final DefaultUsage usage = createConsumableInArrearUsage(usageName, BillingPeriod.MONTHLY, TierBlockPolicy.ALL_TIERS, tier);
         final LocalDate targetDate = new LocalDate(2014, 03, 20);
         final ContiguousIntervalConsumableUsageInArrear intervalConsumableInArrear = createContiguousIntervalConsumableInArrear(usage, ImmutableList.<RawUsage>of(), targetDate, false,
-                                                                                                                      createMockBillingEvent(targetDate.toDateTimeAtStartOfDay(DateTimeZone.UTC),
-                                                                                                                                             BillingPeriod.MONTHLY,
-                                                                                                                                             Collections.<Usage>emptyList())
-                                                                                                                     );
+                                                                                                                                createMockBillingEvent(targetDate.toDateTimeAtStartOfDay(DateTimeZone.UTC),
+                                                                                                                                                       BillingPeriod.MONTHLY,
+                                                                                                                                                       Collections.<Usage>emptyList())
+                                                                                                                               );
         final List<UsageConsumableInArrearTierUnitAggregate> tierUnitDetails = Lists.newArrayList();
         tierUnitDetails.addAll(intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("cell-phone-minutes", 1000L), ImmutableList.<UsageConsumableInArrearTierUnitAggregate>of()));
         tierUnitDetails.addAll(intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("Mbytes", 30720L), ImmutableList.<UsageConsumableInArrearTierUnitAggregate>of()));
@@ -645,7 +623,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
 
         result = produceInvoiceItems(rawUsages, TierBlockPolicy.ALL_TIERS, UsageDetailMode.DETAIL, ImmutableList.<InvoiceItem>of());
         assertEquals(result.size(), 3);
-       // BAR: 100 * 2 = 200
+        // BAR: 100 * 2 = 200
         assertEquals(result.get(0).getAmount().compareTo(new BigDecimal("200.0")), 0);
         assertEquals(result.get(0).getQuantity().intValue(), 100);
         assertEquals(result.get(0).getRate().compareTo(new BigDecimal("2.0")), 0);
@@ -665,7 +643,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
 
         result = produceInvoiceItems(rawUsages, TierBlockPolicy.ALL_TIERS, UsageDetailMode.DETAIL, ImmutableList.<InvoiceItem>of());
         assertEquals(result.size(), 5);
-       // BAR: 100 * 2 = 200
+        // BAR: 100 * 2 = 200
         assertEquals(result.get(0).getAmount().compareTo(new BigDecimal("200.0")), 0);
         assertEquals(result.get(0).getQuantity().intValue(), 100);
         assertEquals(result.get(0).getRate().compareTo(new BigDecimal("2.0")), 0);
@@ -816,8 +794,6 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         assertEquals(result.get(1).getRate().compareTo(new BigDecimal("100.0")), 0);
     }
 
-
-
     @Test(groups = "fast")
     public void testMultipleItemsAndTiersWithExistingItemsAllTiers_AGGREGATE() throws CatalogApiException, IOException, InvoiceApiException {
 
@@ -834,7 +810,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
 
         final UsageConsumableInArrearAggregate usageConsumableInArrearDetail = new UsageConsumableInArrearAggregate(existingUsage);
 
-        final String existingUsageJson =  objectMapper.writeValueAsString(usageConsumableInArrearDetail);
+        final String existingUsageJson = objectMapper.writeValueAsString(usageConsumableInArrearDetail);
 
         //
         // Create usage data points (will include already billed + add new usage data)
@@ -854,7 +830,6 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         UsageConsumableInArrearAggregate usageDetail = objectMapper.readValue(result.get(0).getItemDetails(), new TypeReference<UsageConsumableInArrearAggregate>() {});
         List<UsageConsumableInArrearTierUnitAggregate> itemDetails = usageDetail.getTierDetails();
 
-
         // We get same total than AGGREGATE : 3140
 
         // BAR item detail
@@ -888,11 +863,9 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         assertEquals(itemDetails.get(3).getAmount().compareTo(new BigDecimal("1000.00")), 0);
     }
 
-
     @Test(groups = "fast")
     public void testMultipleItemsAndTiersWithExistingItemsAllTiers_DETAIL() throws CatalogApiException, IOException, InvoiceApiException {
 
-
         //
         // Create usage data points (will include already billed + add new usage data)
         //
@@ -900,44 +873,68 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         rawUsages.add(new DefaultRawUsage(subscriptionId, new LocalDate(2014, 03, 20), "FOO", 50L /* already built */ + 20L)); // tier 3
         rawUsages.add(new DefaultRawUsage(subscriptionId, new LocalDate(2014, 03, 21), "BAR", 80L /* already built */ + 120L)); // tier 2
 
+        // FOO : 10 (tier 1) + 40 (tier 2) = 50
+        final UsageConsumableInArrearTierUnitAggregate existingFooUsageTier1 = new UsageConsumableInArrearTierUnitAggregate(1, "FOO", BigDecimal.ONE, 1, 10, new BigDecimal("10.00"));
+        final String usageInArrearDetail1 = objectMapper.writeValueAsString(existingFooUsageTier1);
+
+        final UsageConsumableInArrearTierUnitAggregate existingFooUsageTier2 = new UsageConsumableInArrearTierUnitAggregate(2, "FOO", BigDecimal.TEN, 1, 40, new BigDecimal("400.00"));
+        final String usageInArrearDetail2 = objectMapper.writeValueAsString(existingFooUsageTier2);
+        // BAR : 10 (tier 1) + 40 (tier 2)
+        final UsageConsumableInArrearTierUnitAggregate existingBarUsageTier1 = new UsageConsumableInArrearTierUnitAggregate(1, "BAR", new BigDecimal("2.00"), 1, 80, new BigDecimal("160.00"));
+        final String usageInArrearDetail3 = objectMapper.writeValueAsString(existingBarUsageTier1);
+
         // Same as previous example bu instead of creating JSON we create one item per type/tier
         final List<InvoiceItem> existingItems = new ArrayList<InvoiceItem>();
-        final InvoiceItem i1 = new UsageInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, usageName, new LocalDate(2014, 03, 20), new LocalDate(2014, 04, 15), new BigDecimal("10.00") /* amount */,  new BigDecimal("1.00") /* rate = tierPrice*/, currency, 10 /* # units*/, "FOO");
-        final InvoiceItem i2 = new UsageInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, usageName, new LocalDate(2014, 03, 20), new LocalDate(2014, 04, 15), new BigDecimal("400.00"),  new BigDecimal("10.00"), currency, 40, "FOO");
-        final InvoiceItem i3 = new UsageInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, usageName, new LocalDate(2014, 03, 20), new LocalDate(2014, 04, 15), new BigDecimal("160.00"),  new BigDecimal("2.00"), currency, 80, "BAR");
+        final InvoiceItem i1 = new UsageInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, usageName, new LocalDate(2014, 03, 20), new LocalDate(2014, 04, 15), new BigDecimal("10.00") /* amount */, new BigDecimal("1.00") /* rate = tierPrice*/, currency, 10 /* # units*/, usageInArrearDetail1);
+        final InvoiceItem i2 = new UsageInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, usageName, new LocalDate(2014, 03, 20), new LocalDate(2014, 04, 15), new BigDecimal("400.00"), new BigDecimal("10.00"), currency, 40, usageInArrearDetail2);
+        final InvoiceItem i3 = new UsageInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, usageName, new LocalDate(2014, 03, 20), new LocalDate(2014, 04, 15), new BigDecimal("160.00"), new BigDecimal("2.00"), currency, 80, usageInArrearDetail3);
         existingItems.addAll(ImmutableList.<InvoiceItem>of(i1, i2, i3));
 
         List<InvoiceItem> result = produceInvoiceItems(rawUsages, TierBlockPolicy.ALL_TIERS, UsageDetailMode.DETAIL, existingItems);
         assertEquals(result.size(), 4);
-        assertEquals(result.get(0).getItemDetails(), "BAR");
+        final UsageConsumableInArrearTierUnitAggregate resultUsageInArrearDetail0 = objectMapper.readValue(result.get(0).getItemDetails(), new TypeReference<UsageConsumableInArrearTierUnitAggregate>() {});
+        assertEquals(resultUsageInArrearDetail0.getTierUnit(), "BAR");
+        assertEquals(resultUsageInArrearDetail0.getTier(), 1);
+        assertEquals(resultUsageInArrearDetail0.getQuantity().intValue(), 20);
+        assertEquals(resultUsageInArrearDetail0.getTierPrice().compareTo(new BigDecimal("2.00")), 0);
+
         assertEquals(result.get(0).getRate().compareTo(new BigDecimal("2.00")), 0);
         assertEquals(result.get(0).getAmount().compareTo(new BigDecimal("40.00")), 0);
         assertEquals(result.get(0).getQuantity().intValue(), 20);
 
-        assertEquals(result.get(1).getItemDetails(), "BAR");
+        final UsageConsumableInArrearTierUnitAggregate resultUsageInArrearDetail1 = objectMapper.readValue(result.get(1).getItemDetails(), new TypeReference<UsageConsumableInArrearTierUnitAggregate>() {});
+        assertEquals(resultUsageInArrearDetail1.getTierUnit(), "BAR");
+        assertEquals(resultUsageInArrearDetail1.getTier(), 2);
+        assertEquals(resultUsageInArrearDetail1.getQuantity().intValue(), 100);
+        assertEquals(resultUsageInArrearDetail1.getTierPrice().compareTo(new BigDecimal("20.00")), 0);
+
         assertEquals(result.get(1).getRate().compareTo(new BigDecimal("20.00")), 0);
         assertEquals(result.get(1).getAmount().compareTo(new BigDecimal("2000.00")), 0);
         assertEquals(result.get(1).getQuantity().intValue(), 100);
 
-        assertEquals(result.get(2).getItemDetails(), "FOO");
+        final UsageConsumableInArrearTierUnitAggregate resultUsageInArrearDetail2 = objectMapper.readValue(result.get(2).getItemDetails(), new TypeReference<UsageConsumableInArrearTierUnitAggregate>() {});
+        assertEquals(resultUsageInArrearDetail2.getTierUnit(), "FOO");
+        assertEquals(resultUsageInArrearDetail2.getTier(), 2);
+        assertEquals(resultUsageInArrearDetail2.getQuantity().intValue(), 10);
+        assertEquals(resultUsageInArrearDetail2.getTierPrice().compareTo(new BigDecimal("10.00")), 0);
+
         assertEquals(result.get(2).getRate().compareTo(new BigDecimal("10.00")), 0);
         assertEquals(result.get(2).getAmount().compareTo(new BigDecimal("100.00")), 0);
         assertEquals(result.get(2).getQuantity().intValue(), 10);
 
-        assertEquals(result.get(3).getItemDetails(), "FOO");
+
+        final UsageConsumableInArrearTierUnitAggregate resultUsageInArrearDetail3 = objectMapper.readValue(result.get(3).getItemDetails(), new TypeReference<UsageConsumableInArrearTierUnitAggregate>() {});
+        assertEquals(resultUsageInArrearDetail3.getTierUnit(), "FOO");
+        assertEquals(resultUsageInArrearDetail3.getTier(), 3);
+        assertEquals(resultUsageInArrearDetail3.getQuantity().intValue(), 10);
+        assertEquals(resultUsageInArrearDetail3.getTierPrice().compareTo(new BigDecimal("100.00")), 0);
+
         assertEquals(result.get(3).getRate().compareTo(new BigDecimal("100.00")), 0);
         assertEquals(result.get(3).getAmount().compareTo(new BigDecimal("1000.00")), 0);
         assertEquals(result.get(3).getQuantity().intValue(), 10);
 
     }
 
-
-
-
-
-
-
-
     @Test(groups = "fast")
     public void testMultipleItemsAndTiersWithExistingItemsTopTier() throws CatalogApiException, IOException {
         // TODO + code
@@ -964,7 +961,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
 
         final LocalDate targetDate = endDate;
 
-        final BillingEvent event1 = createMockBillingEvent(startDate.toDateTimeAtStartOfDay(DateTimeZone.UTC),BillingPeriod.MONTHLY, Collections.<Usage>emptyList());
+        final BillingEvent event1 = createMockBillingEvent(startDate.toDateTimeAtStartOfDay(DateTimeZone.UTC), BillingPeriod.MONTHLY, Collections.<Usage>emptyList());
         final BillingEvent event2 = createMockBillingEvent(endDate.toDateTimeAtStartOfDay(DateTimeZone.UTC), BillingPeriod.MONTHLY, Collections.<Usage>emptyList());
 
         final ContiguousIntervalUsageInArrear intervalConsumableInArrear = createContiguousIntervalConsumableInArrear(usage, rawUsages, targetDate, true, usageDetailMode, event1, event2);
@@ -978,7 +975,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
             }
         }));
 
-        for (InvoiceItem item: result) {
+        for (InvoiceItem item : result) {
             assertEquals(item.getCurrency(), Currency.BTC);
             assertEquals(item.getAccountId(), accountId);
             assertEquals(item.getBundleId(), bundleId);