use of org.killbill.billing.invoice.usage.details.UsageConsumableInArrearAggregate in project killbill by killbill.
the class TestContiguousIntervalConsumableInArrear method testMultipleItemsAndTiersWithExistingItemsAllTiers_AGGREGATE.
@Test(groups = "fast")
public void testMultipleItemsAndTiersWithExistingItemsAllTiers_AGGREGATE() throws Exception {
//
// Let's assume we were already billed on the previous period
//
// FOO : 10 (tier 1) + 40 (tier 2) = 50
final UsageConsumableInArrearTierUnitAggregate existingFooUsageTier1 = new UsageConsumableInArrearTierUnitAggregate(1, "FOO", BigDecimal.ONE, 1L, 10L, new BigDecimal("10.00"));
final UsageConsumableInArrearTierUnitAggregate existingFooUsageTier2 = new UsageConsumableInArrearTierUnitAggregate(2, "FOO", BigDecimal.TEN, 1L, 40L, new BigDecimal("400.00"));
// BAR : 10 (tier 1) + 40 (tier 2)
final UsageConsumableInArrearTierUnitAggregate existingBarUsageTier1 = new UsageConsumableInArrearTierUnitAggregate(1, "BAR", new BigDecimal("2.00"), 1L, 80L, new BigDecimal("160.00"));
final List<UsageConsumableInArrearTierUnitAggregate> existingUsage = ImmutableList.of(existingFooUsageTier1, existingFooUsageTier2, existingBarUsageTier1);
final UsageConsumableInArrearAggregate usageConsumableInArrearDetail = new UsageConsumableInArrearAggregate(existingUsage);
final String existingUsageJson = objectMapper.writeValueAsString(usageConsumableInArrearDetail);
//
// Create usage data points (will include already billed + add new usage data)
//
List<RawUsageRecord> rawUsageRecords = new ArrayList<RawUsageRecord>();
// tier 3
rawUsageRecords.add(new DefaultRawUsage(subscriptionId, new LocalDate(2014, 03, 20), "FOO", 50L + /* already built */
20L, "tracking-1"));
// tier 2
rawUsageRecords.add(new DefaultRawUsage(subscriptionId, new LocalDate(2014, 03, 21), "BAR", 80L + /* already built */
120L, "tracking-1"));
final List<InvoiceItem> existingItems = new ArrayList<InvoiceItem>();
final InvoiceItem ii1 = new UsageInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, productName, planName, phaseName, usageName, null, new LocalDate(2014, 03, 20), new LocalDate(2014, 04, 15), new BigDecimal("570.00"), null, currency, null, existingUsageJson);
existingItems.add(ii1);
List<InvoiceItem> result = produceInvoiceItems(rawUsageRecords, TierBlockPolicy.ALL_TIERS, UsageDetailMode.AGGREGATE, existingItems);
assertEquals(result.size(), 1);
assertEquals(result.get(0).getAmount().compareTo(new BigDecimal("3140.00")), 0, String.format("%s != 3140.0", result.get(0).getAmount()));
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
assertEquals(itemDetails.get(0).getTierUnit(), "BAR");
assertEquals(itemDetails.get(0).getTier(), 1);
assertEquals(itemDetails.get(0).getTierBlockSize(), 1);
assertEquals(itemDetails.get(0).getQuantity().intValue(), 20);
assertEquals(itemDetails.get(0).getTierPrice().compareTo(new BigDecimal("2.00")), 0);
assertEquals(itemDetails.get(0).getAmount().compareTo(new BigDecimal("40.00")), 0);
assertEquals(itemDetails.get(1).getTierUnit(), "BAR");
assertEquals(itemDetails.get(1).getTier(), 2);
assertEquals(itemDetails.get(1).getTierBlockSize(), 1);
assertEquals(itemDetails.get(1).getQuantity().intValue(), 100);
assertEquals(itemDetails.get(1).getTierPrice().compareTo(new BigDecimal("20.00")), 0);
assertEquals(itemDetails.get(1).getAmount().compareTo(new BigDecimal("2000.00")), 0);
// FOO item detail
assertEquals(itemDetails.get(2).getTierUnit(), "FOO");
assertEquals(itemDetails.get(2).getTier(), 1);
assertEquals(itemDetails.get(2).getTierBlockSize(), 1);
assertEquals(itemDetails.get(2).getQuantity().intValue(), 0);
assertEquals(itemDetails.get(2).getTierPrice().compareTo(new BigDecimal("1.00")), 0);
assertEquals(itemDetails.get(2).getAmount().compareTo(new BigDecimal("0.00")), 0);
assertEquals(itemDetails.get(3).getTierUnit(), "FOO");
assertEquals(itemDetails.get(3).getTier(), 2);
assertEquals(itemDetails.get(3).getTierBlockSize(), 1);
assertEquals(itemDetails.get(3).getQuantity().intValue(), 10);
assertEquals(itemDetails.get(3).getTierPrice().compareTo(new BigDecimal("10.00")), 0);
assertEquals(itemDetails.get(3).getAmount().compareTo(new BigDecimal("100.00")), 0);
assertEquals(itemDetails.get(4).getTierUnit(), "FOO");
assertEquals(itemDetails.get(4).getTier(), 3);
assertEquals(itemDetails.get(4).getTierBlockSize(), 1);
assertEquals(itemDetails.get(4).getQuantity().intValue(), 10);
assertEquals(itemDetails.get(4).getTierPrice().compareTo(new BigDecimal("100.00")), 0);
assertEquals(itemDetails.get(4).getAmount().compareTo(new BigDecimal("1000.00")), 0);
}
use of org.killbill.billing.invoice.usage.details.UsageConsumableInArrearAggregate in project killbill by killbill.
the class ContiguousIntervalConsumableUsageInArrear method getBilledDetailsForUnitType.
@VisibleForTesting
List<UsageConsumableInArrearTierUnitAggregate> 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, UsageConsumableInArrearTierUnitAggregate> resultMap = new TreeMap<Integer, UsageConsumableInArrearTierUnitAggregate>(Ordering.<Integer>natural());
List<UsageConsumableInArrearTierUnitAggregate> tierDetails = new ArrayList<UsageConsumableInArrearTierUnitAggregate>();
for (final InvoiceItem bi : billedItems) {
if (usageDetailMode == UsageDetailMode.DETAIL) {
final UsageConsumableInArrearTierUnitAggregate targetTierUnitDetail = fromJson(bi.getItemDetails(), new TypeReference<UsageConsumableInArrearTierUnitAggregate>() {
});
if (targetTierUnitDetail.getTierUnit().equals(unitType)) {
// See https://github.com/killbill/killbill/issues/1325
final Long quantity = bi.getQuantity().longValue();
tierDetails.add(new UsageConsumableInArrearTierUnitAggregate(targetTierUnitDetail.getTier(), targetTierUnitDetail.getTierUnit(), bi.getRate(), targetTierUnitDetail.getTierBlockSize(), quantity, bi.getAmount()));
}
} else {
final UsageConsumableInArrearAggregate usageDetail = fromJson(bi.getItemDetails(), new TypeReference<UsageConsumableInArrearAggregate>() {
});
for (final UsageConsumableInArrearTierUnitAggregate unitAgg : usageDetail.getTierDetails()) {
if (unitAgg.getTierUnit().equals(unitType)) {
tierDetails.add(unitAgg);
}
}
}
}
for (final UsageConsumableInArrearTierUnitAggregate curDetail : tierDetails) {
if (!resultMap.containsKey(curDetail.getTier())) {
resultMap.put(curDetail.getTier(), curDetail);
} else {
final UsageConsumableInArrearTierUnitAggregate perTierDetail = resultMap.get(curDetail.getTier());
perTierDetail.updateQuantityAndAmount(curDetail.getQuantity());
}
}
return ImmutableList.copyOf(resultMap.values());
}
use of org.killbill.billing.invoice.usage.details.UsageConsumableInArrearAggregate in project killbill by killbill.
the class ContiguousIntervalConsumableUsageInArrear method getToBeBilledUsageDetails.
@Override
protected UsageInArrearAggregate getToBeBilledUsageDetails(final LocalDate startDate, final LocalDate endDate, final List<RolledUpUnit> rolledUpUnits, final Iterable<InvoiceItem> billedItems, final boolean areAllBilledItemsWithDetails) throws CatalogApiException {
final Map<String, List<UsageConsumableInArrearTierUnitAggregate>> previousUnitsUsage;
if (usageDetailMode == UsageDetailMode.DETAIL || areAllBilledItemsWithDetails) {
previousUnitsUsage = new HashMap<String, List<UsageConsumableInArrearTierUnitAggregate>>();
for (RolledUpUnit cur : rolledUpUnits) {
final List<UsageConsumableInArrearTierUnitAggregate> usageInArrearDetailForUnitType = getBilledDetailsForUnitType(billedItems, cur.getUnitType());
previousUnitsUsage.put(cur.getUnitType(), usageInArrearDetailForUnitType);
}
} else {
previousUnitsUsage = ImmutableMap.of();
}
final List<UsageConsumableInArrearTierUnitAggregate> usageConsumableInArrearTierUnitAggregates = new ArrayList<UsageConsumableInArrearTierUnitAggregate>();
for (final RolledUpUnit cur : rolledUpUnits) {
if (!unitTypes.contains(cur.getUnitType())) {
log.warn("ContiguousIntervalConsumableInArrear is skipping unitType " + cur.getUnitType());
continue;
}
final List<UsageConsumableInArrearTierUnitAggregate> previousUsage = previousUnitsUsage.containsKey(cur.getUnitType()) ? previousUnitsUsage.get(cur.getUnitType()) : ImmutableList.<UsageConsumableInArrearTierUnitAggregate>of();
final List<UsageConsumableInArrearTierUnitAggregate> toBeBilledConsumableInArrear = computeToBeBilledConsumableInArrear(startDate, endDate, cur, previousUsage);
usageConsumableInArrearTierUnitAggregates.addAll(toBeBilledConsumableInArrear);
}
final UsageInArrearAggregate toBeBilledUsageDetails = new UsageConsumableInArrearAggregate(usageConsumableInArrearTierUnitAggregates);
return toBeBilledUsageDetails;
}
use of org.killbill.billing.invoice.usage.details.UsageConsumableInArrearAggregate in project killbill by killbill.
the class ContiguousIntervalConsumableUsageInArrear method populateResults.
@Override
protected void populateResults(final LocalDate startDate, final LocalDate endDate, final DateTime catalogEffectiveDate, final BigDecimal billedUsage, final BigDecimal toBeBilledUsage, final UsageInArrearAggregate toBeBilledUsageDetails, final boolean areAllBilledItemsWithDetails, final boolean isPeriodPreviouslyBilled, final List<InvoiceItem> result) throws InvoiceApiException {
// In the case past invoice items showed the details (areAllBilledItemsWithDetails=true), billed usage has already been taken into account
// as it part of the reconciliation logic, so no need to subtract it here
final BigDecimal amountToBill = (usage.getTierBlockPolicy() == TierBlockPolicy.ALL_TIERS && areAllBilledItemsWithDetails) ? toBeBilledUsage : toBeBilledUsage.subtract(billedUsage);
if (amountToBill.compareTo(BigDecimal.ZERO) < 0) {
if (isDryRun || invoiceConfig.isUsageMissingLenient(internalTenantContext)) {
return;
} else {
throw new InvoiceApiException(ErrorCode.UNEXPECTED_ERROR, String.format("ILLEGAL INVOICING STATE: Usage period start='%s', end='%s', amountToBill='%s', (previously billed amount='%s', new proposed amount='%s')", startDate, endDate, amountToBill, billedUsage, toBeBilledUsage));
}
} else /* amountToBill.compareTo(BigDecimal.ZERO) >= 0 */
{
if (!isPeriodPreviouslyBilled || amountToBill.compareTo(BigDecimal.ZERO) > 0) {
if (UsageDetailMode.DETAIL == usageDetailMode) {
for (UsageConsumableInArrearTierUnitAggregate toBeBilledUsageDetail : ((UsageConsumableInArrearAggregate) toBeBilledUsageDetails).getTierDetails()) {
final String itemDetails = toJson(toBeBilledUsageDetail);
// See https://github.com/killbill/killbill/issues/1325
// Our current sql schema limits to an int value ...
final Integer quantity = toBeBilledUsageDetail.getQuantity() <= Integer.MAX_VALUE ? toBeBilledUsageDetail.getQuantity().intValue() : -1;
final InvoiceItem item = new UsageInvoiceItem(invoiceId, accountId, getBundleId(), getSubscriptionId(), getProductName(), getPlanName(), getPhaseName(), usage.getName(), catalogEffectiveDate, startDate, endDate, toBeBilledUsageDetail.getAmount(), toBeBilledUsageDetail.getTierPrice(), getCurrency(), quantity, itemDetails);
result.add(item);
}
} else {
final String itemDetails = toJson(toBeBilledUsageDetails);
final InvoiceItem item = new UsageInvoiceItem(invoiceId, accountId, getBundleId(), getSubscriptionId(), getProductName(), getPlanName(), getPhaseName(), usage.getName(), catalogEffectiveDate, startDate, endDate, amountToBill, null, getCurrency(), null, itemDetails);
result.add(item);
}
}
}
}
use of org.killbill.billing.invoice.usage.details.UsageConsumableInArrearAggregate in project killbill by killbill.
the class TestContiguousIntervalConsumableInArrear method testBilledDetailsForUnitType.
@Test(groups = "fast")
public void testBilledDetailsForUnitType() throws Exception {
final LocalDate startDate = new LocalDate(2014, 03, 20);
final LocalDate targetDate = startDate.plusDays(1);
final DefaultTieredBlock block = createDefaultTieredBlock("unit", 100, 1000, BigDecimal.ONE);
final DefaultTier tier = createDefaultTierWithBlocks(block);
final DefaultUsage usage = createConsumableInArrearUsage(usageName, BillingPeriod.MONTHLY, TierBlockPolicy.ALL_TIERS, tier);
final ContiguousIntervalConsumableUsageInArrear intervalConsumableInArrear = createContiguousIntervalConsumableInArrear(usage, ImmutableList.<RawUsageRecord>of(), targetDate, false, createMockBillingEvent(targetDate.toDateTimeAtStartOfDay(DateTimeZone.UTC), BillingPeriod.MONTHLY, Collections.<Usage>emptyList(), catalogEffectiveDate));
final UsageConsumableInArrearTierUnitAggregate detail1 = new UsageConsumableInArrearTierUnitAggregate(3, "FOO", new BigDecimal("0.50"), 1L, 700L);
final UsageConsumableInArrearTierUnitAggregate detail2 = new UsageConsumableInArrearTierUnitAggregate(2, "FOO", BigDecimal.ONE, 1L, 500L);
final UsageConsumableInArrearTierUnitAggregate detail3 = new UsageConsumableInArrearTierUnitAggregate(1, "FOO", BigDecimal.TEN, 1L, 10L);
final UsageConsumableInArrearTierUnitAggregate detail4 = new UsageConsumableInArrearTierUnitAggregate(2, "FOO", BigDecimal.ONE, 1L, 50L);
final UsageConsumableInArrearTierUnitAggregate detail5 = new UsageConsumableInArrearTierUnitAggregate(1, "FOO", BigDecimal.TEN, 1L, 100L);
final List<UsageConsumableInArrearTierUnitAggregate> existingUsage = ImmutableList.of(detail1, detail2, detail3, detail4, detail5);
final UsageConsumableInArrearAggregate usageConsumableInArrearDetail = new UsageConsumableInArrearAggregate(existingUsage);
final String existingUsageJson = objectMapper.writeValueAsString(usageConsumableInArrearDetail);
final List<InvoiceItem> existingItems = new ArrayList<InvoiceItem>();
final InvoiceItem ii1 = new UsageInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, productName, planName, phaseName, usageName, null, 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);
assertEquals(aggregateDetails.get(0).getQuantity().intValue(), 110);
assertEquals(aggregateDetails.get(1).getTier(), 2);
assertEquals(aggregateDetails.get(1).getQuantity().intValue(), 550);
assertEquals(aggregateDetails.get(2).getTier(), 3);
assertEquals(aggregateDetails.get(2).getQuantity().intValue(), 700);
}
Aggregations