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 7e0d45a..ee0e65b 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
@@ -30,6 +30,8 @@ import java.util.UUID;
import org.joda.time.LocalDate;
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;
@@ -79,7 +81,7 @@ public class ContiguousIntervalConsumableUsageInArrear extends ContiguousInterva
if (UsageDetailMode.DETAIL == usageDetailMode) {
for (UsageConsumableInArrearTierUnitDetail toBeBilledUsageDetail : ((UsageConsumableInArrearDetail) toBeBilledUsageDetails).getTierDetails()) {
final InvoiceItem item = new UsageInvoiceItem(invoiceId, accountId, getBundleId(), getSubscriptionId(), getPlanName(),
- getPhaseName(), usage.getName(), startDate, endDate, toBeBilledUsageDetail.getAmount(), toBeBilledUsageDetail.getTierPrice(), getCurrency(), toBeBilledUsageDetail.getQuantity(), null);
+ getPhaseName(), usage.getName(), startDate, endDate, toBeBilledUsageDetail.getAmount(), toBeBilledUsageDetail.getTierPrice(), getCurrency(), toBeBilledUsageDetail.getQuantity(), toBeBilledUsageDetail.getTierUnit());
result.add(item);
}
} else {
@@ -95,7 +97,7 @@ public class ContiguousIntervalConsumableUsageInArrear extends ContiguousInterva
protected UsageInArrearDetail getToBeBilledUsageDetails(final List<RolledUpUnit> rolledUpUnits, final Iterable<InvoiceItem> billedItems, final boolean areAllBilledItemsWithDetails) throws CatalogApiException {
final Map<String, List<UsageConsumableInArrearTierUnitDetail>> previousUnitsUsage;
- if (areAllBilledItemsWithDetails) {
+ if (usageDetailMode == UsageDetailMode.DETAIL || areAllBilledItemsWithDetails) {
previousUnitsUsage = new HashMap<String, List<UsageConsumableInArrearTierUnitDetail>>();
for (RolledUpUnit cur : rolledUpUnits) {
final List<UsageConsumableInArrearTierUnitDetail> usageInArrearDetailForUnitType = getBilledDetailsForUnitType(billedItems, cur.getUnitType());
@@ -120,24 +122,66 @@ public class ContiguousIntervalConsumableUsageInArrear extends ContiguousInterva
return toBeBilledUsageDetails;
}
+
@VisibleForTesting
List<UsageConsumableInArrearTierUnitDetail> getBilledDetailsForUnitType(final Iterable<InvoiceItem> billedItems, final String unitType) {
// Aggregate on a per-tier level, will return a list with item per level -- for this 'unitType'
final Map<Integer, UsageConsumableInArrearTierUnitDetail> resultMap = new TreeMap<Integer, UsageConsumableInArrearTierUnitDetail>(Ordering.<Integer>natural());
+
+
+
+ List<UsageConsumableInArrearTierUnitDetail> tierDetails = new ArrayList<UsageConsumableInArrearTierUnitDetail>();
for (final InvoiceItem bi : billedItems) {
- final UsageConsumableInArrearDetail usageDetail = fromJson(bi.getItemDetails());
- for (final UsageConsumableInArrearTierUnitDetail curDetail : usageDetail.getTierDetails()) {
+ if (usageDetailMode == UsageDetailMode.DETAIL) {
- if (curDetail.getTierUnit().equals(unitType)) {
+ final Currency currency = getCurrency();
- if (!resultMap.containsKey(curDetail.getTier())) {
- resultMap.put(curDetail.getTier(), curDetail);
- } else {
- final UsageConsumableInArrearTierUnitDetail perTierDetail = resultMap.get(curDetail.getTier());
- perTierDetail.updateQuantityAndAmount(curDetail.getQuantity());
+ final String biUnitType = bi.getItemDetails();
+ if (!biUnitType.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, "OHHHHHH some is wrong!!!");
+
+ tierDetails.add(new UsageConsumableInArrearTierUnitDetail(tierLevel, biUnitType, bi.getRate(), targetTier.getSize().intValue(), bi.getQuantity(), bi.getAmount()));
+ } else {
+ final UsageConsumableInArrearDetail usageDetail = fromJson(bi.getItemDetails());
+ tierDetails.addAll(usageDetail.getTierDetails());
+ }
+ }
+
+ for (final UsageConsumableInArrearTierUnitDetail curDetail : tierDetails) {
+
+ if (curDetail.getTierUnit().equals(unitType)) {
+
+ if (!resultMap.containsKey(curDetail.getTier())) {
+ resultMap.put(curDetail.getTier(), curDetail);
+ } else {
+ final UsageConsumableInArrearTierUnitDetail perTierDetail = resultMap.get(curDetail.getTier());
+ perTierDetail.updateQuantityAndAmount(curDetail.getQuantity());
}
}
}
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 7fd4e71..6a07028 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
@@ -518,7 +518,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
}
@Test(groups = "fast")
- public void testComputeMissingItemsAggregateModeAllTier() throws CatalogApiException, IOException, InvoiceApiException {
+ public void testComputeMissingItemsAggregateModeAllTier_AGGREGATE() throws CatalogApiException, IOException, InvoiceApiException {
// Case 1
List<RawUsage> rawUsages = new ArrayList<RawUsage>();
@@ -620,7 +620,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
}
@Test(groups = "fast")
- public void testComputeMissingItemsDetailModeAllTier() throws CatalogApiException, IOException, InvoiceApiException {
+ public void testComputeMissingItemsDetailModeAllTier_DETAIL() throws CatalogApiException, IOException, InvoiceApiException {
// Case 1
List<RawUsage> rawUsages = new ArrayList<RawUsage>();
@@ -688,7 +688,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
}
@Test(groups = "fast")
- public void testComputeMissingItemsAggregateModeTopTier() throws CatalogApiException, IOException, InvoiceApiException {
+ public void testComputeMissingItemsAggregateModeTopTier_AGGREGATE() throws CatalogApiException, IOException, InvoiceApiException {
// Case 1
List<RawUsage> rawUsages = new ArrayList<RawUsage>();
@@ -765,7 +765,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
}
@Test(groups = "fast")
- public void testComputeMissingItemsDetailModeTopTier() throws CatalogApiException, IOException, InvoiceApiException {
+ public void testComputeMissingItemsDetailModeTopTier_DETAIL() throws CatalogApiException, IOException, InvoiceApiException {
// Case 1
List<RawUsage> rawUsages = new ArrayList<RawUsage>();
@@ -819,7 +819,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
@Test(groups = "fast")
- public void testMultipleItemsAndTiersWithExistingItemsAllTiers() throws CatalogApiException, IOException, InvoiceApiException {
+ public void testMultipleItemsAndTiersWithExistingItemsAllTiers_AGGREGATE() throws CatalogApiException, IOException, InvoiceApiException {
//
// Let's assume we were already billed on the previous period
@@ -854,6 +854,9 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
UsageConsumableInArrearDetail usageDetail = objectMapper.readValue(result.get(0).getItemDetails(), new TypeReference<UsageConsumableInArrearDetail>() {});
List<UsageConsumableInArrearTierUnitDetail> itemDetails = usageDetail.getTierDetails();
+
+ // We get same total than AGGREGATE : 3140
+
// BAR item detail
assertEquals(itemDetails.get(0).getTierUnit(), "BAR");
assertEquals(itemDetails.get(0).getTier(), 1);
@@ -887,6 +890,55 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
@Test(groups = "fast")
+ public void testMultipleItemsAndTiersWithExistingItemsAllTiers_DETAIL() throws CatalogApiException, IOException, InvoiceApiException {
+
+
+ //
+ // Create usage data points (will include already billed + add new usage data)
+ //
+ List<RawUsage> rawUsages = new ArrayList<RawUsage>();
+ 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
+
+ // 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");
+ 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");
+ 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");
+ 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");
+ 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");
+ 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
}