Skip to content

Commit

Permalink
Issue#839 - fix tier number for consumable usage
Browse files Browse the repository at this point in the history
  • Loading branch information
wwjbatista committed Feb 6, 2018
1 parent e3f7165 commit b712baf
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 25 deletions.
Expand Up @@ -195,7 +195,7 @@ public UsageInArrearItemsAndNextNotificationDate computeMissingItemsAndNextNotif

final List<RolledUpUsage> allUsage = getRolledUpUsage();
for (final RolledUpUsage ru : allUsage) {
int tierNum = 1;

List<UsageInArrearDetail> toBeBilledUsageDetails = Lists.newLinkedList();
BigDecimal toBeBilledUsage = BigDecimal.ZERO;
if (usage.getUsageType() == UsageType.CAPACITY) {
Expand All @@ -209,7 +209,7 @@ public UsageInArrearItemsAndNextNotificationDate computeMissingItemsAndNextNotif
continue;
}

toBeBilledUsageDetails.addAll(computeToBeBilledConsumableInArrear(cur, tierNum++));
toBeBilledUsageDetails.addAll(computeToBeBilledConsumableInArrear(cur));
}

}
Expand Down Expand Up @@ -424,28 +424,31 @@ List<UsageInArrearDetail> computeToBeBilledCapacityInArrear(final List<RolledUpU
* @throws CatalogApiException
*/
@VisibleForTesting
List<UsageInArrearDetail> computeToBeBilledConsumableInArrear(final RolledUpUnit roUnit, int tierNum) throws CatalogApiException {
List<UsageInArrearDetail> computeToBeBilledConsumableInArrear(final RolledUpUnit roUnit) throws CatalogApiException {

Preconditions.checkState(isBuilt.get());
final List<TieredBlock> tieredBlocks = getConsumableInArrearTieredBlocks(usage, roUnit.getUnitType());
final List<Map<Integer,TieredBlock>> tieredBlocksWithTierNum = getConsumableInArrearTieredBlocks(usage, roUnit.getUnitType());

switch (usage.getTierBlockPolicy()) {
case ALL_TIERS:
return computeToBeBilledConsumableInArrearWith_ALL_TIERS(tieredBlocks, roUnit.getAmount(), tierNum);
return computeToBeBilledConsumableInArrearWith_ALL_TIERS(tieredBlocksWithTierNum, roUnit.getAmount());
case TOP_TIER:
return Arrays.asList(computeToBeBilledConsumableInArrearWith_TOP_TIER(tieredBlocks, roUnit.getAmount(), tierNum));
return Arrays.asList(computeToBeBilledConsumableInArrearWith_TOP_TIER(tieredBlocksWithTierNum, roUnit.getAmount()));
default:
throw new IllegalStateException("Unknown TierBlockPolicy " + usage.getTierBlockPolicy());
}
}


List<UsageInArrearDetail> computeToBeBilledConsumableInArrearWith_ALL_TIERS(final List<TieredBlock> tieredBlocks, final Long units, int tierNum) throws CatalogApiException {
List<UsageInArrearDetail> computeToBeBilledConsumableInArrearWith_ALL_TIERS(final List<Map<Integer,TieredBlock>> tieredBlocksWithTierNum, final Long units) throws CatalogApiException {

List<UsageInArrearDetail> toBeBilledDetails = Lists.newLinkedList();
BigDecimal result = BigDecimal.ZERO;
int remainingUnits = units.intValue();
for (final TieredBlock tieredBlock : tieredBlocks) {
for (final Map<Integer,TieredBlock> tieredBlockWithTierNum : tieredBlocksWithTierNum) {

final TieredBlock tieredBlock = tieredBlockWithTierNum.entrySet().iterator().next().getValue();
final int tierNum = tieredBlockWithTierNum.entrySet().iterator().next().getKey();

final int blockTierSize = tieredBlock.getSize().intValue();
final int tmp = remainingUnits / blockTierSize + (remainingUnits % blockTierSize == 0 ? 0 : 1);
Expand All @@ -457,33 +460,42 @@ List<UsageInArrearDetail> computeToBeBilledConsumableInArrearWith_ALL_TIERS(fina
nbUsedTierBlocks = tmp;
remainingUnits = 0;
}
toBeBilledDetails.add(new UsageInArrearDetail(tierNum, tieredBlock.getUnit().getName(), tieredBlock.getPrice().getPrice(getCurrency()), nbUsedTierBlocks));

if (nbUsedTierBlocks > 0) {
toBeBilledDetails.add(new UsageInArrearDetail(tierNum, tieredBlock.getUnit().getName(), tieredBlock.getPrice().getPrice(getCurrency()), nbUsedTierBlocks));
}
}
return toBeBilledDetails;
}

UsageInArrearDetail computeToBeBilledConsumableInArrearWith_TOP_TIER(final List<TieredBlock> tieredBlocks, final Long units, int tierNum) throws CatalogApiException {
UsageInArrearDetail computeToBeBilledConsumableInArrearWith_TOP_TIER(final List<Map<Integer,TieredBlock>> tieredBlocksWithTierNum, final Long units) throws CatalogApiException {

int remainingUnits = units.intValue();

// By default last last tierBlock
TieredBlock targetBlock = tieredBlocks.get(tieredBlocks.size() - 1);
Map<Integer,TieredBlock> targetBlockWithTierNum = tieredBlocksWithTierNum.get(tieredBlocksWithTierNum.size() - 1);
TieredBlock targetBlock = targetBlockWithTierNum.entrySet().iterator().next().getValue();
int targetTierNum = targetBlockWithTierNum.entrySet().iterator().next().getKey();
// Loop through all tier block
for (final TieredBlock tieredBlock : tieredBlocks) {
for (final Map<Integer,TieredBlock> tieredBlockWithTierNum : tieredBlocksWithTierNum) {

final TieredBlock tieredBlock = tieredBlockWithTierNum.entrySet().iterator().next().getValue();
final int tierNum = tieredBlockWithTierNum.entrySet().iterator().next().getKey();

final int blockTierSize = tieredBlock.getSize().intValue();
final int tmp = remainingUnits / blockTierSize + (remainingUnits % blockTierSize == 0 ? 0 : 1);
if (tmp > tieredBlock.getMax()) {
remainingUnits -= tieredBlock.getMax() * blockTierSize;
} else {
targetBlock = tieredBlock;
targetTierNum = tierNum;
break;
}
}
final int lastBlockTierSize = targetBlock.getSize().intValue();
final int nbBlocks = units.intValue() / lastBlockTierSize + (units.intValue() % lastBlockTierSize == 0 ? 0 : 1);

return new UsageInArrearDetail(tierNum, targetBlock.getUnit().getName(), targetBlock.getPrice().getPrice(getCurrency()), nbBlocks);
return new UsageInArrearDetail(targetTierNum, targetBlock.getUnit().getName(), targetBlock.getPrice().getPrice(getCurrency()), nbBlocks);
}


Expand Down
Expand Up @@ -16,8 +16,10 @@

package org.killbill.billing.invoice.usage;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.killbill.billing.catalog.api.BillingMode;
Expand All @@ -33,16 +35,20 @@

public class UsageUtils {

public static List<TieredBlock> getConsumableInArrearTieredBlocks(final Usage usage, final String unitType) {
public static List<Map<Integer,TieredBlock>> getConsumableInArrearTieredBlocks(final Usage usage, final String unitType) {

Preconditions.checkArgument(usage.getBillingMode() == BillingMode.IN_ARREAR && usage.getUsageType() == UsageType.CONSUMABLE);
Preconditions.checkArgument(usage.getTiers().length > 0);

final List<TieredBlock> result = Lists.newLinkedList();
final List<Map<Integer,TieredBlock>> result = Lists.newLinkedList();
int tierNum = 0;
for (Tier tier : usage.getTiers()) {
tierNum++;
for (TieredBlock tierBlock : tier.getTieredBlocks()) {
if (tierBlock.getUnit().getName().equals(unitType)) {
result.add(tierBlock);
Map<Integer, TieredBlock> tieredBlockWithTierNum = new HashMap<>();
tieredBlockWithTierNum.put(tierNum, tierBlock);
result.add(tieredBlockWithTierNum);
}
}
}
Expand Down
Expand Up @@ -145,7 +145,7 @@ public void testComputeBilledUsageSizeOneWith_ALL_TIERS() throws CatalogApiExcep
Collections.<Usage>emptyList())
);

List<UsageInArrearDetail> result = intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("unit", 111L), 1);
List<UsageInArrearDetail> result = intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("unit", 111L));
assertEquals(result.size(), 3);
// 111 = 10 (tier1) + 100 (tier2) + 1 (tier3) => 10 * 1.5 + 100 * 1 + 1 * 0.5 = 115.5
assertEquals(result.get(0).getAmount(), new BigDecimal("15.0"));
Expand Down Expand Up @@ -173,7 +173,7 @@ public void testComputeBilledUsageWith_ALL_TIERS() throws CatalogApiException {
Collections.<Usage>emptyList())
);

List<UsageInArrearDetail> result = intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("unit", 5325L), 1);
List<UsageInArrearDetail> result = intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("unit", 5325L));
assertEquals(result.size(), 2);

// 5000 = 1000 (tier1) + 4325 (tier2) => 10 + 5 = 15
Expand Down Expand Up @@ -207,23 +207,23 @@ public void testComputeBilledUsageWith_TOP_TIER() throws CatalogApiException {
//
// 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
//
List<UsageInArrearDetail> inputTier1 = intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("unit", 1000L), 1);
List<UsageInArrearDetail> inputTier1 = intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("unit", 1000L));
assertEquals(inputTier1.size(), 1);
// 1000 units => (tier1) : 1000 / 100 + 1000 % 100 = 10
assertEquals(inputTier1.get(0).getAmount(), new BigDecimal("10"));

List<UsageInArrearDetail> inputTier2 = intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("unit", 101000L), 1);
List<UsageInArrearDetail> inputTier2 = intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("unit", 101000L));
assertEquals(inputTier2.size(), 1);
// 101000 units => (tier2) : 101000 / 1000 + 101000 % 1000 = 101 + 0 = 101
assertEquals(inputTier2.get(0).getAmount(), new BigDecimal("101"));

List<UsageInArrearDetail> inputTier3 = intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("unit", 101001L), 1);
List<UsageInArrearDetail> inputTier3 = intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("unit", 101001L));
assertEquals(inputTier3.size(), 1);
// 101001 units => (tier3) : 101001 / 1000 + 101001 % 1000 = 101 + 1 = 102 units => $51
assertEquals(inputTier3.get(0).getAmount(), new BigDecimal("51.0"));

// If we pass the maximum of the last tier, we price all units at the last tier
List<UsageInArrearDetail> inputLastTier = intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("unit", 300000L), 1);
List<UsageInArrearDetail> inputLastTier = intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("unit", 300000L));
assertEquals(inputLastTier.size(), 1);
// 300000 units => (tier3) : 300000 / 1000 + 300000 % 1000 = 300 units => $150
assertEquals(inputLastTier.get(0).getAmount(), new BigDecimal("150.0"));
Expand Down Expand Up @@ -252,7 +252,7 @@ public void testComputeBilledUsageSizeOneWith_TOP_TIER() throws CatalogApiExcept
Collections.<Usage>emptyList())
);

List<UsageInArrearDetail> result = intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("unit", 111L), 1);
List<UsageInArrearDetail> result = intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("unit", 111L));
assertEquals(result.size(), 1);

// 111 = 111 * 0.5 =
Expand Down Expand Up @@ -462,8 +462,8 @@ public void testTobeBilledForUnit() throws CatalogApiException {
Collections.<Usage>emptyList())
);
List<UsageInArrearDetail> results = Lists.newArrayList();
results.addAll(intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("cell-phone-minutes", 1000L), 1));
results.addAll(intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("Mbytes", 30720L), 2));
results.addAll(intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("cell-phone-minutes", 1000L)));
results.addAll(intervalConsumableInArrear.computeToBeBilledConsumableInArrear(new DefaultRolledUpUnit("Mbytes", 30720L)));
assertEquals(results.size(), 2);

assertEquals(intervalConsumableInArrear.toBeBilledForUnit(results), new BigDecimal("18.5"));
Expand Down

0 comments on commit b712baf

Please sign in to comment.