use of org.killbill.billing.invoice.usage.details.UsageInArrearAggregate 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.UsageInArrearAggregate in project killbill by killbill.
the class ContiguousIntervalUsageInArrear method computeMissingItemsAndNextNotificationDate.
/**
* Compute the missing usage invoice items based on what should be billed and what has been billed ($ amount comparison).
*
* @param existingUsage existing on disk usage items for the subscription
* @throws CatalogApiException
*/
public UsageInArrearItemsAndNextNotificationDate computeMissingItemsAndNextNotificationDate(final List<InvoiceItem> existingUsage) throws CatalogApiException, InvoiceApiException {
Preconditions.checkState(isBuilt.get());
if (transitionTimes.size() < 2) {
return new UsageInArrearItemsAndNextNotificationDate(ImmutableList.<InvoiceItem>of(), ImmutableSet.of(), computeNextNotificationDate());
}
final List<InvoiceItem> result = Lists.newLinkedList();
final RolledUpUnitsWithTracking allUsageWithTracking = getRolledUpUsage();
final List<RolledUpUsageWithMetadata> allUsage = allUsageWithTracking.getUsage();
final Set<TrackingRecordId> allTrackingIds = allUsageWithTracking.getTrackingIds();
final Set<TrackingRecordId> existingTrackingIds = extractTrackingIds(allExistingTrackingIds);
final Set<TrackingRecordId> newTrackingIds = Sets.filter(allTrackingIds, new Predicate<TrackingRecordId>() {
@Override
public boolean apply(final TrackingRecordId allRecord) {
return !Iterables.any(existingTrackingIds, new Predicate<TrackingRecordId>() {
@Override
public boolean apply(final TrackingRecordId existingRecord) {
return existingRecord.isSimilarRecord(allRecord);
}
});
}
});
// Each RolledUpUsage 'ru' is for a specific time period and across all units
for (final RolledUpUsageWithMetadata ru : allUsage) {
final InvoiceItem existingOverlappingItem = isContainedIntoExistingUsage(ru.getStart(), ru.getEnd(), existingUsage);
if (existingOverlappingItem != null) {
// In case of blocking situations, when re-running the invoicing code, already billed usage maybe have another start and end date
// because of blocking events. We need to skip these to avoid double billing (see gotchas in testWithPartialBlockBilling).
log.warn("Ignoring usage {} between start={} and end={} as it has already been invoiced by invoiceItemId={}", usage.getName(), ru.getStart(), ru.getEnd(), existingOverlappingItem.getId());
continue;
}
//
// Previously billed items:
//
// 1. Retrieves current price amount billed for that period of time (and usage section)
final Iterable<InvoiceItem> billedItems = getBilledItems(ru.getStart(), ru.getEnd(), existingUsage);
// 2. Verify whether previously built items have the item_details section
final boolean areAllBilledItemsWithDetails = areAllBilledItemsWithDetails(billedItems);
// 3. verify if we already billed that period - use to decide whether we should include $0 items when there is nothing to bill for.
final boolean isPeriodPreviouslyBilled = !Iterables.isEmpty(billedItems);
// 4. Computes total billed usage amount
final BigDecimal billedUsage = computeBilledUsage(billedItems);
final List<RolledUpUnit> rolledUpUnits = ru.getRolledUpUnits();
final UsageInArrearAggregate toBeBilledUsageDetails = getToBeBilledUsageDetails(ru.getStart(), ru.getEnd(), rolledUpUnits, billedItems, areAllBilledItemsWithDetails);
final BigDecimal toBeBilledUsageUnrounded = toBeBilledUsageDetails.getAmount();
// See https://github.com/killbill/killbill/issues/1124
final BigDecimal toBeBilledUsage = KillBillMoney.of(toBeBilledUsageUnrounded, getCurrency());
populateResults(ru.getStart(), ru.getEnd(), ru.getCatalogEffectiveDate(), billedUsage, toBeBilledUsage, toBeBilledUsageDetails, areAllBilledItemsWithDetails, isPeriodPreviouslyBilled, result);
}
final LocalDate nextNotificationDate = computeNextNotificationDate();
return new UsageInArrearItemsAndNextNotificationDate(result, newTrackingIds, nextNotificationDate);
}
Aggregations