Search in sources :

Example 6 with UsageConsumableInArrearTierUnitAggregate

use of org.killbill.billing.invoice.usage.details.UsageConsumableInArrearTierUnitAggregate in project killbill by killbill.

the class ContiguousIntervalConsumableUsageInArrear method computeToBeBilledConsumableInArrearWith_TOP_TIER.

UsageConsumableInArrearTierUnitAggregate computeToBeBilledConsumableInArrearWith_TOP_TIER(final List<TieredBlock> tieredBlocks, final List<UsageConsumableInArrearTierUnitAggregate> previousUsage, final Long units) throws CatalogApiException {
    Long remainingUnits = units;
    // By default last last tierBlock
    TieredBlock targetBlock = tieredBlocks.get(tieredBlocks.size() - 1);
    int targetTierNum = tieredBlocks.size();
    int tierNum = 0;
    // Loop through all tier block
    for (final TieredBlock tieredBlock : tieredBlocks) {
        tierNum++;
        final long blockTierSize = tieredBlock.getSize().longValue();
        final long tmp = remainingUnits / blockTierSize + (remainingUnits % blockTierSize == 0 ? 0 : 1);
        if (tmp > tieredBlock.getMax()) {
            /* Includes the case where max is unlimited (-1) */
            remainingUnits -= tieredBlock.getMax().longValue() * blockTierSize;
        } else {
            targetBlock = tieredBlock;
            targetTierNum = tierNum;
            break;
        }
    }
    final long lastBlockTierSize = targetBlock.getSize().longValue();
    final long nbBlocks = units / lastBlockTierSize + (units % lastBlockTierSize == 0 ? 0 : 1);
    return new UsageConsumableInArrearTierUnitAggregate(targetTierNum, targetBlock.getUnit().getName(), targetBlock.getPrice().getPrice(getCurrency()), targetBlock.getSize().longValue(), nbBlocks);
}
Also used : TieredBlock(org.killbill.billing.catalog.api.TieredBlock) UsageConsumableInArrearTierUnitAggregate(org.killbill.billing.invoice.usage.details.UsageConsumableInArrearTierUnitAggregate)

Example 7 with UsageConsumableInArrearTierUnitAggregate

use of org.killbill.billing.invoice.usage.details.UsageConsumableInArrearTierUnitAggregate 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());
}
Also used : UsageConsumableInArrearAggregate(org.killbill.billing.invoice.usage.details.UsageConsumableInArrearAggregate) UsageInvoiceItem(org.killbill.billing.invoice.model.UsageInvoiceItem) InvoiceItem(org.killbill.billing.invoice.api.InvoiceItem) UsageConsumableInArrearTierUnitAggregate(org.killbill.billing.invoice.usage.details.UsageConsumableInArrearTierUnitAggregate) ArrayList(java.util.ArrayList) TreeMap(java.util.TreeMap) VisibleForTesting(com.google.common.annotations.VisibleForTesting)

Example 8 with UsageConsumableInArrearTierUnitAggregate

use of org.killbill.billing.invoice.usage.details.UsageConsumableInArrearTierUnitAggregate 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;
}
Also used : UsageConsumableInArrearAggregate(org.killbill.billing.invoice.usage.details.UsageConsumableInArrearAggregate) RolledUpUnit(org.killbill.billing.usage.api.RolledUpUnit) UsageInArrearAggregate(org.killbill.billing.invoice.usage.details.UsageInArrearAggregate) UsageConsumableInArrearTierUnitAggregate(org.killbill.billing.invoice.usage.details.UsageConsumableInArrearTierUnitAggregate) ArrayList(java.util.ArrayList) ArrayList(java.util.ArrayList) ImmutableList(com.google.common.collect.ImmutableList) List(java.util.List)

Example 9 with UsageConsumableInArrearTierUnitAggregate

use of org.killbill.billing.invoice.usage.details.UsageConsumableInArrearTierUnitAggregate 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);
            }
        }
    }
}
Also used : UsageConsumableInArrearAggregate(org.killbill.billing.invoice.usage.details.UsageConsumableInArrearAggregate) InvoiceApiException(org.killbill.billing.invoice.api.InvoiceApiException) UsageInvoiceItem(org.killbill.billing.invoice.model.UsageInvoiceItem) InvoiceItem(org.killbill.billing.invoice.api.InvoiceItem) UsageConsumableInArrearTierUnitAggregate(org.killbill.billing.invoice.usage.details.UsageConsumableInArrearTierUnitAggregate) UsageInvoiceItem(org.killbill.billing.invoice.model.UsageInvoiceItem) BigDecimal(java.math.BigDecimal)

Example 10 with UsageConsumableInArrearTierUnitAggregate

use of org.killbill.billing.invoice.usage.details.UsageConsumableInArrearTierUnitAggregate in project killbill by killbill.

the class ContiguousIntervalConsumableUsageInArrear method computeToBeBilledConsumableInArrearWith_ALL_TIERS.

List<UsageConsumableInArrearTierUnitAggregate> computeToBeBilledConsumableInArrearWith_ALL_TIERS(final LocalDate startDate, final LocalDate endDate, final List<TieredBlock> tieredBlocks, final List<UsageConsumableInArrearTierUnitAggregate> previousUsage, final Long units) throws CatalogApiException {
    List<UsageConsumableInArrearTierUnitAggregate> toBeBilledDetails = Lists.newLinkedList();
    long remainingUnits = units;
    int tierNum = 0;
    // we count tier from 1, 2, ...
    final int lastPreviousUsageTier = previousUsage.size();
    final boolean hasPreviousUsage = lastPreviousUsageTier > 0;
    for (final TieredBlock tieredBlock : tieredBlocks) {
        tierNum++;
        final long blockTierSize = tieredBlock.getSize().longValue();
        final long tmp = remainingUnits / blockTierSize + (remainingUnits % blockTierSize == 0 ? 0 : 1);
        long nbUsedTierBlocks;
        if (tieredBlock.getMax() != (double) -1 && tmp > tieredBlock.getMax()) {
            nbUsedTierBlocks = tieredBlock.getMax().longValue();
            remainingUnits -= tieredBlock.getMax() * blockTierSize;
        } else {
            nbUsedTierBlocks = tmp;
            remainingUnits = 0;
        }
        // We generate an entry if we consumed anything on this tier or if this is the first tier to also support $0 Usage item
        if (hasPreviousUsage) {
            final Long previousUsageQuantity = tierNum <= lastPreviousUsageTier ? previousUsage.get(tierNum - 1).getQuantity() : 0;
            // Be lenient for dryRun use cases (as we could have plugin optimizations not returning full usage data) or unless configured.
            if (!isDryRun && !invoiceConfig.isUsageMissingLenient(internalTenantContext)) {
                if (tierNum < lastPreviousUsageTier) {
                    Preconditions.checkState(nbUsedTierBlocks == previousUsageQuantity, String.format("Expected usage for subscription='%s', targetDate='%s', startDt='%s', endDt='%s', tier='%d', unit='%s' to be full, instead found units='[%d/%d]'", getSubscriptionId(), targetDate, startDate, endDate, tierNum, tieredBlock.getUnit().getName(), nbUsedTierBlocks, previousUsageQuantity));
                } else {
                    Preconditions.checkState(nbUsedTierBlocks - previousUsageQuantity >= 0, String.format("Expected usage for subscription='%s', targetDate='%s', startDt='%s', endDt='%s', tier='%d', unit='%s' to contain at least as much as current usage, instead found units='[%d/%d]'", getSubscriptionId(), targetDate, startDate, endDate, tierNum, tieredBlock.getUnit().getName(), nbUsedTierBlocks, previousUsageQuantity));
                }
            }
            nbUsedTierBlocks = nbUsedTierBlocks - previousUsageQuantity;
        }
        if (tierNum == 1 || nbUsedTierBlocks > 0) {
            toBeBilledDetails.add(new UsageConsumableInArrearTierUnitAggregate(tierNum, tieredBlock.getUnit().getName(), tieredBlock.getPrice().getPrice(getCurrency()), blockTierSize, nbUsedTierBlocks));
        }
    }
    return toBeBilledDetails;
}
Also used : TieredBlock(org.killbill.billing.catalog.api.TieredBlock) UsageConsumableInArrearTierUnitAggregate(org.killbill.billing.invoice.usage.details.UsageConsumableInArrearTierUnitAggregate)

Aggregations

UsageConsumableInArrearTierUnitAggregate (org.killbill.billing.invoice.usage.details.UsageConsumableInArrearTierUnitAggregate)18 BigDecimal (java.math.BigDecimal)14 LocalDate (org.joda.time.LocalDate)13 DefaultRawUsage (org.killbill.billing.usage.api.svcs.DefaultRawUsage)13 Test (org.testng.annotations.Test)13 DefaultTier (org.killbill.billing.catalog.DefaultTier)9 DefaultTieredBlock (org.killbill.billing.catalog.DefaultTieredBlock)9 DefaultUsage (org.killbill.billing.catalog.DefaultUsage)9 UsageConsumableInArrearAggregate (org.killbill.billing.invoice.usage.details.UsageConsumableInArrearAggregate)9 ArrayList (java.util.ArrayList)8 Usage (org.killbill.billing.catalog.api.Usage)8 InvoiceItem (org.killbill.billing.invoice.api.InvoiceItem)8 UsageInvoiceItem (org.killbill.billing.invoice.model.UsageInvoiceItem)8 FixedPriceInvoiceItem (org.killbill.billing.invoice.model.FixedPriceInvoiceItem)6 RawUsageRecord (org.killbill.billing.usage.api.RawUsageRecord)5 TypeReference (com.fasterxml.jackson.core.type.TypeReference)3 TieredBlock (org.killbill.billing.catalog.api.TieredBlock)2 VisibleForTesting (com.google.common.annotations.VisibleForTesting)1 ImmutableList (com.google.common.collect.ImmutableList)1 List (java.util.List)1