public DefaultUsage(final Usage in, UsagePriceOverride override, Currency currency) { this.name = in.getName(); this.usageType = in.getUsageType(); this.tierBlockPolicy = in.getTierBlockPolicy(); this.billingPeriod = in.getBillingPeriod(); this.billingMode = in.getBillingMode(); this.limits = (DefaultLimit[]) in.getLimits(); this.blocks = (DefaultBlock[]) in.getBlocks(); this.tiers = new DefaultTier[in.getTiers().length]; for (int i = 0; i < in.getTiers().length; i++) { if (override != null && override.getTierPriceOverrides() != null) { final TieredBlock[] curTieredBlocks = in.getTiers()[i].getTieredBlocks(); tiers[i] = (overriddenTier != null) ? new DefaultTier(in.getTiers()[i], overriddenTier, currency) : (DefaultTier) in.getTiers()[i]; } else { tiers[i] = (DefaultTier) in.getTiers()[i];
public List<UsagePriceOverride> getResolvedUsageOverrides(Usage[] usages, List<UsagePriceOverride> usagePriceOverrides) throws CatalogApiException { List<UsagePriceOverride> resolvedUsageOverrides = new ArrayList<UsagePriceOverride>(); for (final Usage curUsage : usages) { final UsagePriceOverride curOverride = Iterables.tryFind(usagePriceOverrides, new Predicate<UsagePriceOverride>() { @Override public boolean apply(final UsagePriceOverride input) { return input.getName() != null && input.getName().equals(curUsage.getName()); } }).orNull(); if (curOverride != null) { List<TierPriceOverride> tierPriceOverrides = getResolvedTierOverrides(curUsage.getTiers(), curOverride.getTierPriceOverrides()); resolvedUsageOverrides.add(new DefaultUsagePriceOverride(curUsage.getName(), curUsage.getUsageType(), tierPriceOverrides)); } else { resolvedUsageOverrides.add(null); } } return resolvedUsageOverrides; }
public static List<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(); for (Tier tier : usage.getTiers()) { boolean found = false; for (TieredBlock tierBlock : tier.getTieredBlocks()) { if (tierBlock.getUnit().getName().equals(unitType)) { result.add(tierBlock); found = true; break; } } // We expect this method to return an ordered list of TieredBlock, each for each tier. Preconditions.checkState(found, "Catalog issue in usage section '%s': Missing tierBlock definition for unit '%s'", usage.getName(), unitType); } return result; }
@Override public boolean apply(final UsagePriceOverride input) { return input != null && input.getName().equals(curUsage.getName()); } }).orNull();
public static List<Tier> getCapacityInArrearTier(final Usage usage) { Preconditions.checkArgument(usage.getBillingMode() == BillingMode.IN_ARREAR && usage.getUsageType() == UsageType.CAPACITY); Preconditions.checkArgument(usage.getTiers().length > 0); return ImmutableList.copyOf(usage.getTiers()); }
final UsageKey usageKey = new UsageKey(usage.getName(), event.getCatalogEffectiveDate()); existingInterval = usage.getUsageType() == UsageType.CAPACITY ? new ContiguousIntervalCapacityUsageInArrear(usage, accountId, invoiceId, rawSubscriptionUsage, existingTrackingIds, targetDate, rawUsageStartDate, usageDetailMode, internalTenantContext) : new ContiguousIntervalConsumableUsageInArrear(usage, accountId, invoiceId, rawSubscriptionUsage, existingTrackingIds, targetDate, rawUsageStartDate, usageDetailMode, internalTenantContext);
@Override protected void populateResults(final LocalDate startDate, final LocalDate endDate, 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) { 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)); } 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); final InvoiceItem item = new UsageInvoiceItem(invoiceId, accountId, getBundleId(), getSubscriptionId(), getProductName(), getPlanName(), getPhaseName(), usage.getName(), startDate, endDate, toBeBilledUsageDetail.getAmount(), toBeBilledUsageDetail.getTierPrice(), getCurrency(), toBeBilledUsageDetail.getQuantity(), itemDetails); result.add(item); } } else { final String itemDetails = toJson(toBeBilledUsageDetails); final InvoiceItem item = new UsageInvoiceItem(invoiceId, accountId, getBundleId(), getSubscriptionId(), getProductName(), getPlanName(), getPhaseName(), usage.getName(), startDate, endDate, amountToBill, null, getCurrency(), null, itemDetails); result.add(item); } } } }
private List<UsageJson> buildUsagesJson(final Usage[] usages) throws CurrencyValueNull { List<UsageJson> usagesJson = new ArrayList<UsageJson>(); for (int i = 0; i < usages.length; i++) { usagesJson.add(new UsageJson(usages[i].getBillingPeriod().toString(), buildTiers(usages[i].getTiers()))); } return usagesJson; }
private void addTransitionTimesForBillingEvent(final LocalDate startDate, final LocalDate endDate, final int bcd) { final BillingIntervalDetail bid = new BillingIntervalDetail(startDate, endDate, targetDate, bcd, usage.getBillingPeriod(), usage.getBillingMode()); int numberOfPeriod = 0; // First billingCycleDate prior startDate LocalDate nextBillCycleDate = bid.getFutureBillingDateFor(numberOfPeriod); while (!nextBillCycleDate.isAfter(endDate)) { if (transitionTimes.isEmpty() || nextBillCycleDate.isAfter(transitionTimes.get(transitionTimes.size() - 1))) { if (nextBillCycleDate.compareTo(rawUsageStartDate) >= 0) { transitionTimes.add(nextBillCycleDate); } } numberOfPeriod++; nextBillCycleDate = bid.getFutureBillingDateFor(numberOfPeriod); } }
Usage usage = null; for (Usage cur : planPhase.getUsages()) { if (cur.getName().equals(usageName)) { usage = cur; break; prettyUsageName = usage.getPrettyName();
List<UsagePriceOverride> getUsagePriceOverrides(PlanPhase curPhase, CatalogOverridePhaseDefinitionModelDao overriddenPhase, final InternalTenantContext context) { final List<UsagePriceOverride> usagePriceOverrides = new ArrayList<UsagePriceOverride>(); final List<CatalogOverrideUsageDefinitionModelDao> usageDefs = overrideDao.getOverriddenPhaseUsages(overriddenPhase.getRecordId(), context); for (int i = 0; i < curPhase.getUsages().length; i++) { final Usage curUsage = curPhase.getUsages()[i]; final CatalogOverrideUsageDefinitionModelDao overriddenUsage = Iterables.tryFind(usageDefs, new Predicate<CatalogOverrideUsageDefinitionModelDao>() { @Override public boolean apply(final CatalogOverrideUsageDefinitionModelDao input) { return input.getParentUsageName().equals(curUsage.getName()); } }).orNull(); if (overriddenUsage != null) { List<TierPriceOverride> tierPriceOverrides = getTierPriceOverrides(curUsage, overriddenUsage, context); usagePriceOverrides.add(new DefaultUsagePriceOverride(overriddenUsage.getParentUsageName(), curUsage.getUsageType(), tierPriceOverrides)); } } return usagePriceOverrides; }
@Nullable @Override public BillingPeriod apply(final Usage input) { return input.getBillingPeriod(); } }));
@Override public boolean apply(@Nullable final Usage input) { return input.getBillingMode() == BillingMode.IN_ARREAR; } })) {
@VisibleForTesting List<UsageConsumableInArrearTierUnitAggregate> computeToBeBilledConsumableInArrear(final RolledUpUnit roUnit, final List<UsageConsumableInArrearTierUnitAggregate> previousUsage) throws CatalogApiException { Preconditions.checkState(isBuilt.get()); final List<TieredBlock> tieredBlocks = getConsumableInArrearTieredBlocks(usage, roUnit.getUnitType()); switch (usage.getTierBlockPolicy()) { case ALL_TIERS: return computeToBeBilledConsumableInArrearWith_ALL_TIERS(tieredBlocks, previousUsage, roUnit.getAmount()); case TOP_TIER: return Arrays.asList(computeToBeBilledConsumableInArrearWith_TOP_TIER(tieredBlocks, previousUsage, roUnit.getAmount())); default: throw new IllegalStateException("Unknown TierBlockPolicy " + usage.getTierBlockPolicy()); } }
public PhasePriceJson(final String planName, final String phaseName, final String phaseType, final BigDecimal fixedPrice, final BigDecimal recurringPrice, final Usage[] usagePrices, final Currency currency) throws CatalogApiException { this.planName = planName; this.phaseName = phaseName; this.phaseType = phaseType; this.fixedPrice = fixedPrice; this.recurringPrice = recurringPrice; this.usagePrices = new LinkedList<UsagePriceJson>(); for (final Usage usage : usagePrices) { List<TierPriceJson> usageTierPrices = new LinkedList<TierPriceJson>(); for (final Tier tier : usage.getTiers()) { List<BlockPriceJson> blockPrices = new LinkedList<BlockPriceJson>(); for (final TieredBlock block : tier.getTieredBlocks()) { BlockPriceJson blockPriceJson = new BlockPriceJson(block.getUnit().getName(), block.getSize(), block.getPrice().getPrice(currency), block.getMax()); blockPrices.add(blockPriceJson); } TierPriceJson tierPriceJson = new TierPriceJson(blockPrices); usageTierPrices.add(tierPriceJson); } final UsagePriceJson usagePriceJson = new UsagePriceJson(usage.getName(), usage.getUsageType(), usage.getBillingMode(), usage.getTierBlockPolicy(), usageTierPrices); this.usagePrices.add(usagePriceJson); } }
private CatalogOverrideUsageDefinitionModelDao getOrCreateOverrideUsageDefinitionFromTransaction(final Usage parentUsage, Currency currency, final DateTime catalogEffectiveDate, final UsagePriceOverride override, final Handle inTransactionHandle, final InternalCallContext context){ final List<TierPriceOverride> resolvedTierOverrides = override.getTierPriceOverrides(); final CatalogOverrideTierDefinitionModelDao[] overrideTierDefinitionModelDaos = new CatalogOverrideTierDefinitionModelDao[resolvedTierOverrides.size()]; for (int i = 0; i < resolvedTierOverrides.size(); i++) { final TierPriceOverride curOverride = resolvedTierOverrides.get(i); if (curOverride != null) { Tier parentTier = parentUsage.getTiers()[i]; final CatalogOverrideTierDefinitionModelDao createdOverrideTierDefinitionModelDao = getOrCreateOverrideTierDefinitionFromTransaction(parentTier, curOverride, currency, catalogEffectiveDate, inTransactionHandle, context); overrideTierDefinitionModelDaos[i] = createdOverrideTierDefinitionModelDao; } } final CatalogOverrideUsageDefinitionSqlDao sqlDao = inTransactionHandle.attach(CatalogOverrideUsageDefinitionSqlDao.class); final List<Long> targetUsageDefinitionRecordIds = getOverrideUsageDefinitionFromTransaction(overrideTierDefinitionModelDaos, inTransactionHandle, context); List<CatalogOverrideUsageDefinitionModelDao> results = sqlDao.getByAttributes(parentUsage.getName(), context); for(CatalogOverrideUsageDefinitionModelDao usage : results) { if (targetUsageDefinitionRecordIds != null && targetUsageDefinitionRecordIds.contains(usage.getRecordId())) { return usage; } } final CatalogOverrideUsageDefinitionModelDao inputUsageDef = new CatalogOverrideUsageDefinitionModelDao(parentUsage.getName(), parentUsage.getUsageType().name(), currency.name(), null, null, catalogEffectiveDate); sqlDao.create(inputUsageDef, context); final Long recordId = sqlDao.getLastInsertId(); final CatalogOverrideUsageDefinitionModelDao resultUsageDef = sqlDao.getByRecordId(recordId, context); for (short i = 0; i < overrideTierDefinitionModelDaos.length; i++) { if (overrideTierDefinitionModelDaos[i] != null) { createCatalogOverrideUsageTierFromTransaction(i, overrideTierDefinitionModelDaos[i], resultUsageDef, inTransactionHandle, context); } } return resultUsageDef; }
@Override public boolean apply(final CatalogOverrideUsageDefinitionModelDao input) { return input.getParentUsageName().equals(curUsage.getName()); } }).orNull();
public static Set<String> getConsumableInArrearUnitTypes(final Usage usage) { Preconditions.checkArgument(usage.getBillingMode() == BillingMode.IN_ARREAR && usage.getUsageType() == UsageType.CONSUMABLE); Preconditions.checkArgument(usage.getTiers().length > 0); final Set<String> result = new HashSet<String>(); for (Tier tier : usage.getTiers()) { for (TieredBlock tierBlock : tier.getTieredBlocks()) { result.add(tierBlock.getUnit().getName()); } } return result; }
/** * Based on usage type compute new amount * * @param currentAmount * @param newAmount * @return */ private Long computeUpdatedAmount(@Nullable Long currentAmount, @Nullable Long newAmount) { currentAmount = currentAmount == null ? 0L : currentAmount; newAmount = newAmount == null ? 0L : newAmount; if (usage.getUsageType() == UsageType.CAPACITY) { return Math.max(currentAmount, newAmount); } else /* UsageType.CONSUMABLE */ { return currentAmount + newAmount; } }