Search in sources :

Example 1 with TrackingRecordId

use of org.killbill.billing.invoice.generator.InvoiceWithMetadata.TrackingRecordId in project killbill by killbill.

the class InvoiceDispatcher method processAccountWithLockAndInputTargetDate.

private InvoiceWithFutureNotifications processAccountWithLockAndInputTargetDate(final UUID accountId, final LocalDate originalTargetDate, final BillingEventSet billingEvents, final AccountInvoices accountInvoices, @Nullable final DryRunInfo dryRunInfo, final boolean isRescheduled, final LinkedList<PluginProperty> pluginProperties, final Map<InvoiceTiming, Long> invoiceTimings, final InternalCallContext internalCallContext) throws InvoiceApiException {
    final boolean isDryRun = dryRunInfo != null;
    final CallContext callContext = buildCallContext(internalCallContext);
    final ImmutableAccountData account;
    try {
        account = accountApi.getImmutableAccountDataById(accountId, internalCallContext);
    } catch (final AccountApiException e) {
        log.error("Unable to generate invoice for accountId='{}', a future notification has NOT been recorded", accountId, e);
        long startNano = System.nanoTime();
        invoicePluginDispatcher.onFailureCall(originalTargetDate, null, accountInvoices.getInvoices(), isDryRun, isRescheduled, callContext, pluginProperties, internalCallContext);
        invoiceTimings.put(InvoiceTiming.PLUGINS_COMPLETION_CALL, System.nanoTime() - startNano);
        return null;
    }
    long startNano = System.nanoTime();
    final DateTime rescheduleDate = invoicePluginDispatcher.priorCall(originalTargetDate, accountInvoices.getInvoices(), isDryRun, isRescheduled, callContext, pluginProperties, internalCallContext);
    invoiceTimings.put(InvoiceTiming.PLUGINS_PRIOR_CALL, System.nanoTime() - startNano);
    if (rescheduleDate != null) {
        if (isDryRun) {
            log.warn("Ignoring rescheduleDate='{}', delayed scheduling is unsupported in dry-run", rescheduleDate);
        } else {
            final FutureAccountNotifications futureAccountNotifications = createNextFutureNotificationDate(rescheduleDate, billingEvents, internalCallContext);
            setFutureNotifications(account, futureAccountNotifications, internalCallContext);
        }
        return null;
    }
    startNano = System.nanoTime();
    final InvoiceWithMetadata invoiceWithMetadata = generateKillBillInvoice(account, originalTargetDate, billingEvents, accountInvoices, dryRunInfo, internalCallContext);
    invoiceTimings.put(InvoiceTiming.INVOICE_GENERATION, System.nanoTime() - startNano);
    final DefaultInvoice invoice = invoiceWithMetadata.getInvoice();
    // Compute future notifications
    final FutureAccountNotifications futureAccountNotifications = createNextFutureNotificationDate(invoiceWithMetadata, billingEvents, internalCallContext);
    // If invoice comes back null, there is nothing new to generate, we can bail early
    if (invoice == null) {
        startNano = System.nanoTime();
        invoicePluginDispatcher.onSuccessCall(originalTargetDate, null, accountInvoices.getInvoices(), isDryRun, isRescheduled, callContext, pluginProperties, internalCallContext);
        invoiceTimings.put(InvoiceTiming.PLUGINS_COMPLETION_CALL, System.nanoTime() - startNano);
        if (isDryRun) {
            log.info("Generated null dryRun invoice for accountId='{}', targetDate='{}'", accountId, originalTargetDate);
        } else {
            log.info("Generated null invoice for accountId='{}', targetDate='{}'", accountId, originalTargetDate);
            final BusInternalEvent event = new DefaultNullInvoiceEvent(accountId, clock.getUTCToday(), internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId(), internalCallContext.getUserToken());
            // Although we have a null invoice, it could be as a result of removing $0 USAGE (config#isUsageZeroAmountDisabled)
            // and so we may still need to set the CTD for such subscriptions.
            startNano = System.nanoTime();
            setChargedThroughDatesNoExceptions(invoiceWithMetadata.getChargeThroughDates(), internalCallContext);
            invoiceTimings.put(InvoiceTiming.SET_CHARGE_THROUGH_DT, System.nanoTime() - startNano);
            setFutureNotifications(account, futureAccountNotifications, internalCallContext);
            postEvent(event);
        }
        return null;
    }
    final LocalDate actualTargetDate = invoice.getTargetDate();
    boolean success = false;
    try {
        // Generate missing credit (> 0 for generation and < 0 for use) prior we call the plugin(s)
        final InvoiceItem cbaItemPreInvoicePlugins = computeCBAOnExistingInvoice(invoice, internalCallContext);
        if (cbaItemPreInvoicePlugins != null) {
            invoice.addInvoiceItem(cbaItemPreInvoicePlugins);
        }
        // 
        // Ask external invoice plugins if additional items (tax, etc) shall be added to the invoice
        // 
        startNano = System.nanoTime();
        final boolean invoiceUpdated = invoicePluginDispatcher.updateOriginalInvoiceWithPluginInvoiceItems(invoice, isDryRun, callContext, pluginProperties, internalCallContext);
        invoiceTimings.put(InvoiceTiming.PLUGINS_ADDITIONAL_ITEMS, System.nanoTime() - startNano);
        if (invoiceUpdated) {
            // Remove the temporary CBA item as we need to re-compute CBA
            if (cbaItemPreInvoicePlugins != null) {
                invoice.removeInvoiceItem(cbaItemPreInvoicePlugins);
            }
            // Use credit after we call the plugin (https://github.com/killbill/killbill/issues/637)
            final InvoiceItem cbaItemPostInvoicePlugins = computeCBAOnExistingInvoice(invoice, internalCallContext);
            if (cbaItemPostInvoicePlugins != null) {
                invoice.addInvoiceItem(cbaItemPostInvoicePlugins);
            }
        }
        if (!isDryRun) {
            // Compute whether this is a new invoice object (or just some adjustments on an existing invoice), and extract invoiceIds for later use
            final Set<UUID> uniqueInvoiceIds = getUniqueInvoiceIds(invoice);
            final boolean isRealInvoiceWithItems = uniqueInvoiceIds.remove(invoice.getId());
            final Set<UUID> adjustedUniqueOtherInvoiceId = uniqueInvoiceIds;
            logInvoiceWithItems(account, invoice, actualTargetDate, adjustedUniqueOtherInvoiceId, isRealInvoiceWithItems);
            // Transformation to Invoice -> InvoiceModelDao
            final InvoiceModelDao invoiceModelDao = new InvoiceModelDao(invoice);
            final List<InvoiceItemModelDao> invoiceItemModelDaos = transformToInvoiceModelDao(invoice.getInvoiceItems());
            invoiceModelDao.addInvoiceItems(invoiceItemModelDaos);
            final Set<InvoiceTrackingModelDao> trackingIds = new HashSet<>();
            for (final TrackingRecordId cur : invoiceWithMetadata.getTrackingIds()) {
                trackingIds.add(new InvoiceTrackingModelDao(cur.getTrackingId(), cur.getInvoiceId(), cur.getSubscriptionId(), cur.getUnitType(), cur.getRecordDate()));
            }
            // Commit invoice on disk
            final ExistingInvoiceMetadata existingInvoiceMetadata = new ExistingInvoiceMetadata(accountInvoices.getInvoices());
            startNano = System.nanoTime();
            commitInvoiceAndSetFutureNotifications(account, invoiceModelDao, billingEvents, trackingIds, futureAccountNotifications, existingInvoiceMetadata, internalCallContext);
            invoiceTimings.put(InvoiceTiming.COMMIT_INVOICE, System.nanoTime() - startNano);
            startNano = System.nanoTime();
            setChargedThroughDatesNoExceptions(invoiceWithMetadata.getChargeThroughDates(), internalCallContext);
            invoiceTimings.put(InvoiceTiming.SET_CHARGE_THROUGH_DT, System.nanoTime() - startNano);
            success = true;
        }
    } finally {
        // Make sure we always set future notifications in case of errors
        if (!isDryRun && !success) {
            setFutureNotifications(account, futureAccountNotifications, internalCallContext);
        }
        if (isDryRun || success) {
            final DefaultInvoice refreshedInvoice = isDryRun ? invoice : new DefaultInvoice(invoiceDao.getById(invoice.getId(), internalCallContext));
            startNano = System.nanoTime();
            invoicePluginDispatcher.onSuccessCall(actualTargetDate, refreshedInvoice, accountInvoices.getInvoices(), isDryRun, isRescheduled, callContext, pluginProperties, internalCallContext);
            invoiceTimings.put(InvoiceTiming.PLUGINS_COMPLETION_CALL, System.nanoTime() - startNano);
        } else {
            startNano = System.nanoTime();
            invoicePluginDispatcher.onFailureCall(actualTargetDate, invoice, accountInvoices.getInvoices(), isDryRun, isRescheduled, callContext, pluginProperties, internalCallContext);
            invoiceTimings.put(InvoiceTiming.PLUGINS_COMPLETION_CALL, System.nanoTime() - startNano);
        }
    }
    return new InvoiceWithFutureNotifications(invoice, futureAccountNotifications);
}
Also used : ImmutableAccountData(org.killbill.billing.account.api.ImmutableAccountData) TrackingRecordId(org.killbill.billing.invoice.generator.InvoiceWithMetadata.TrackingRecordId) InvoiceItem(org.killbill.billing.invoice.api.InvoiceItem) ItemAdjInvoiceItem(org.killbill.billing.invoice.model.ItemAdjInvoiceItem) ParentInvoiceItem(org.killbill.billing.invoice.model.ParentInvoiceItem) InvoiceModelDao(org.killbill.billing.invoice.dao.InvoiceModelDao) BusInternalEvent(org.killbill.billing.events.BusInternalEvent) ExistingInvoiceMetadata(org.killbill.billing.invoice.dao.ExistingInvoiceMetadata) InvoiceWithMetadata(org.killbill.billing.invoice.generator.InvoiceWithMetadata) CallContext(org.killbill.billing.util.callcontext.CallContext) InternalCallContext(org.killbill.billing.callcontext.InternalCallContext) LocalDate(org.joda.time.LocalDate) DateTime(org.joda.time.DateTime) DefaultNullInvoiceEvent(org.killbill.billing.invoice.api.user.DefaultNullInvoiceEvent) InvoiceTrackingModelDao(org.killbill.billing.invoice.dao.InvoiceTrackingModelDao) InvoiceItemModelDao(org.killbill.billing.invoice.dao.InvoiceItemModelDao) AccountApiException(org.killbill.billing.account.api.AccountApiException) UUID(java.util.UUID) DefaultInvoice(org.killbill.billing.invoice.model.DefaultInvoice) HashSet(java.util.HashSet)

Example 2 with TrackingRecordId

use of org.killbill.billing.invoice.generator.InvoiceWithMetadata.TrackingRecordId in project killbill by killbill.

the class TestUsageInArrearBase method checkTrackingIds.

// 
// Each input `RawUsage` should end up creating one TrackingRecordId
// Regardless of how test records trackingId -- grouped in one, or multiple calls-- and regardless of test matrix,  the logics below should remain true.
// 
protected void checkTrackingIds(final List<RawUsageRecord> rawUsages, final Set<TrackingRecordId> trackingRecords) {
    // Verify we have same input and output
    assertEquals(rawUsages.size(), trackingRecords.size());
    final Map<String, List<RawUsageRecord>> trackingIdMapping = new HashMap<>();
    for (final RawUsageRecord u : rawUsages) {
        if (!trackingIdMapping.containsKey(u.getTrackingId())) {
            trackingIdMapping.put(u.getTrackingId(), new ArrayList<>());
        }
        trackingIdMapping.get(u.getTrackingId()).add(u);
    }
    final Set<String> trackingIds = ImmutableSet.copyOf(Iterables.transform(trackingRecords, new Function<TrackingRecordId, String>() {

        @Override
        public String apply(final TrackingRecordId input) {
            return input.getTrackingId();
        }
    }));
    // Verify the per trackingId input matches the per trackingId output
    assertEquals(trackingIdMapping.size(), trackingIds.size());
    for (final String id : trackingIdMapping.keySet()) {
        final List<RawUsageRecord> rawUsageForId = trackingIdMapping.get(id);
        for (RawUsageRecord u : rawUsageForId) {
            final TrackingRecordId found = Iterables.tryFind(trackingRecords, new Predicate<TrackingRecordId>() {

                @Override
                public boolean apply(final TrackingRecordId input) {
                    return input.getTrackingId().equals(u.getTrackingId()) && input.getRecordDate().equals(u.getDate()) && input.getUnitType().equals(u.getUnitType());
                }
            }).orNull();
            assertNotNull(found, "Cannot find tracking Id " + u.getTrackingId());
            assertEquals(found.getSubscriptionId(), subscriptionId);
            assertEquals(found.getInvoiceId(), invoiceId);
            assertEquals(found.getRecordDate(), u.getDate());
            assertEquals(found.getUnitType(), u.getUnitType());
        }
    }
}
Also used : Function(com.google.common.base.Function) TrackingRecordId(org.killbill.billing.invoice.generator.InvoiceWithMetadata.TrackingRecordId) RawUsageRecord(org.killbill.billing.usage.api.RawUsageRecord) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) List(java.util.List) Predicate(com.google.common.base.Predicate)

Example 3 with TrackingRecordId

use of org.killbill.billing.invoice.generator.InvoiceWithMetadata.TrackingRecordId in project killbill by killbill.

the class RawUsageOptimizer method getInArrearUsage.

public RawUsageOptimizerResult getInArrearUsage(final LocalDate firstEventStartDate, final LocalDate targetDate, final Iterable<InvoiceItem> existingUsageItems, final Map<String, Usage> knownUsage, @Nullable final DryRunInfo dryRunInfo, final InternalCallContext internalCallContext) {
    final int configRawUsagePreviousPeriod = config.getMaxRawUsagePreviousPeriod(internalCallContext);
    final LocalDate optimizedStartDate = configRawUsagePreviousPeriod >= 0 ? getOptimizedRawUsageStartDate(firstEventStartDate, targetDate, existingUsageItems, knownUsage, internalCallContext) : firstEventStartDate;
    log.debug("RawUsageOptimizerResult accountRecordId='{}', configRawUsagePreviousPeriod='{}', firstEventStartDate='{}', optimizedStartDate='{}',  targetDate='{}'", internalCallContext.getAccountRecordId(), configRawUsagePreviousPeriod, firstEventStartDate, optimizedStartDate, targetDate);
    final List<RawUsageRecord> rawUsageData = usageApi.getRawUsageForAccount(optimizedStartDate, targetDate, dryRunInfo, internalCallContext);
    final List<InvoiceTrackingModelDao> trackingIds = invoiceDao.getTrackingsByDateRange(optimizedStartDate, targetDate, internalCallContext);
    final Set<TrackingRecordId> existingTrackingIds = new HashSet<TrackingRecordId>();
    for (final InvoiceTrackingModelDao invoiceTrackingModelDao : trackingIds) {
        existingTrackingIds.add(new TrackingRecordId(invoiceTrackingModelDao.getTrackingId(), invoiceTrackingModelDao.getInvoiceId(), invoiceTrackingModelDao.getSubscriptionId(), invoiceTrackingModelDao.getUnitType(), invoiceTrackingModelDao.getRecordDate()));
    }
    return new RawUsageOptimizerResult(optimizedStartDate, rawUsageData, existingTrackingIds);
}
Also used : TrackingRecordId(org.killbill.billing.invoice.generator.InvoiceWithMetadata.TrackingRecordId) RawUsageRecord(org.killbill.billing.usage.api.RawUsageRecord) InvoiceTrackingModelDao(org.killbill.billing.invoice.dao.InvoiceTrackingModelDao) LocalDate(org.joda.time.LocalDate) HashSet(java.util.HashSet)

Example 4 with TrackingRecordId

use of org.killbill.billing.invoice.generator.InvoiceWithMetadata.TrackingRecordId 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);
}
Also used : TrackingRecordId(org.killbill.billing.invoice.generator.InvoiceWithMetadata.TrackingRecordId) InvoiceItem(org.killbill.billing.invoice.api.InvoiceItem) UsageInvoiceItem(org.killbill.billing.invoice.model.UsageInvoiceItem) LocalDate(org.joda.time.LocalDate) BigDecimal(java.math.BigDecimal) RolledUpUnit(org.killbill.billing.usage.api.RolledUpUnit) UsageInArrearAggregate(org.killbill.billing.invoice.usage.details.UsageInArrearAggregate)

Example 5 with TrackingRecordId

use of org.killbill.billing.invoice.generator.InvoiceWithMetadata.TrackingRecordId in project killbill by killbill.

the class UsageInvoiceItemGenerator method generateItems.

@Override
public InvoiceGeneratorResult generateItems(final ImmutableAccountData account, final UUID invoiceId, final BillingEventSet eventSet, final AccountInvoices existingInvoices, final LocalDate targetDate, final Currency targetCurrency, final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates, final DryRunInfo dryRunInfo, final InternalCallContext internalCallContext) throws InvoiceApiException {
    final Map<UUID, List<InvoiceItem>> perSubscriptionInArrearUsageItems = extractPerSubscriptionExistingInArrearUsageItems(eventSet.getUsages(), existingInvoices.getInvoices());
    try {
        // Pretty-print the generated invoice items from the junction events
        final InvoiceItemGeneratorLogger invoiceItemGeneratorLogger = new InvoiceItemGeneratorLogger(invoiceId, account.getId(), "usage", log);
        final UsageDetailMode usageDetailMode = invoiceConfig.getItemResultBehaviorMode(internalCallContext);
        final LocalDate minBillingEventDate = getMinBillingEventDate(eventSet, internalCallContext);
        final Set<TrackingRecordId> trackingIds = new HashSet<>();
        final List<InvoiceItem> items = Lists.newArrayList();
        final Iterator<BillingEvent> events = eventSet.iterator();
        final boolean isDryRun = dryRunInfo != null;
        RawUsageOptimizerResult rawUsgRes = null;
        List<BillingEvent> curEvents = Lists.newArrayList();
        UUID curSubscriptionId = null;
        while (events.hasNext()) {
            final BillingEvent event = events.next();
            // Skip events that are posterior to the targetDate
            final LocalDate eventLocalEffectiveDate = internalCallContext.toLocalDate(event.getEffectiveDate());
            if (eventLocalEffectiveDate.isAfter(targetDate)) {
                continue;
            }
            // Optimize to do the usage query only once after we know there are indeed some usage items
            if (rawUsgRes == null && Iterables.any(event.getUsages(), new Predicate<Usage>() {

                @Override
                public boolean apply(final Usage input) {
                    return input.getBillingMode() == BillingMode.IN_ARREAR;
                }
            })) {
                rawUsgRes = rawUsageOptimizer.getInArrearUsage(minBillingEventDate, targetDate, Iterables.concat(perSubscriptionInArrearUsageItems.values()), eventSet.getUsages(), dryRunInfo, internalCallContext);
                // Ask Kill Bill team for an optimal configuration based on your use case ;-)
                if (existingInvoices.getCutoffDate() != null && existingInvoices.getCutoffDate().compareTo(rawUsgRes.getRawUsageStartDate()) > 0) {
                    log.warn("Detected an invoice cuttOff date={}, and usage optimized start date= {} that could lead to some issues", existingInvoices.getCutoffDate(), rawUsgRes.getRawUsageStartDate());
                }
            }
            // None of the billing events report any usage IN_ARREAR sections
            if (rawUsgRes == null) {
                continue;
            }
            final UUID subscriptionId = event.getSubscriptionId();
            if (curSubscriptionId != null && !curSubscriptionId.equals(subscriptionId)) {
                final SubscriptionUsageInArrear subscriptionUsageInArrear = new SubscriptionUsageInArrear(account.getId(), invoiceId, curEvents, rawUsgRes.getRawUsage(), rawUsgRes.getExistingTrackingIds(), targetDate, rawUsgRes.getRawUsageStartDate(), usageDetailMode, invoiceConfig, internalCallContext);
                final List<InvoiceItem> usageInArrearItems = perSubscriptionInArrearUsageItems.get(curSubscriptionId);
                final SubscriptionUsageInArrearItemsAndNextNotificationDate subscriptionResult = subscriptionUsageInArrear.computeMissingUsageInvoiceItems(usageInArrearItems != null ? usageInArrearItems : ImmutableList.<InvoiceItem>of(), invoiceItemGeneratorLogger, isDryRun);
                final List<InvoiceItem> newInArrearUsageItems = subscriptionResult.getInvoiceItems();
                items.addAll(newInArrearUsageItems);
                trackingIds.addAll(subscriptionResult.getTrackingIds());
                updatePerSubscriptionNextNotificationUsageDate(curSubscriptionId, subscriptionResult.getPerUsageNotificationDates(), BillingMode.IN_ARREAR, perSubscriptionFutureNotificationDates);
                curEvents = Lists.newArrayList();
            }
            curSubscriptionId = subscriptionId;
            curEvents.add(event);
        }
        if (curSubscriptionId != null) {
            final SubscriptionUsageInArrear subscriptionUsageInArrear = new SubscriptionUsageInArrear(account.getId(), invoiceId, curEvents, rawUsgRes.getRawUsage(), rawUsgRes.getExistingTrackingIds(), targetDate, rawUsgRes.getRawUsageStartDate(), usageDetailMode, invoiceConfig, internalCallContext);
            final List<InvoiceItem> usageInArrearItems = perSubscriptionInArrearUsageItems.get(curSubscriptionId);
            final SubscriptionUsageInArrearItemsAndNextNotificationDate subscriptionResult = subscriptionUsageInArrear.computeMissingUsageInvoiceItems(usageInArrearItems != null ? usageInArrearItems : ImmutableList.<InvoiceItem>of(), invoiceItemGeneratorLogger, isDryRun);
            final List<InvoiceItem> newInArrearUsageItems = subscriptionResult.getInvoiceItems();
            items.addAll(newInArrearUsageItems);
            trackingIds.addAll(subscriptionResult.getTrackingIds());
            updatePerSubscriptionNextNotificationUsageDate(curSubscriptionId, subscriptionResult.getPerUsageNotificationDates(), BillingMode.IN_ARREAR, perSubscriptionFutureNotificationDates);
        }
        invoiceItemGeneratorLogger.logItems();
        return new InvoiceGeneratorResult(items, trackingIds);
    } catch (final CatalogApiException e) {
        throw new InvoiceApiException(e);
    }
}
Also used : TrackingRecordId(org.killbill.billing.invoice.generator.InvoiceWithMetadata.TrackingRecordId) Usage(org.killbill.billing.catalog.api.Usage) InvoiceItem(org.killbill.billing.invoice.api.InvoiceItem) SubscriptionUsageInArrearItemsAndNextNotificationDate(org.killbill.billing.invoice.usage.SubscriptionUsageInArrear.SubscriptionUsageInArrearItemsAndNextNotificationDate) UsageDetailMode(org.killbill.billing.util.config.definition.InvoiceConfig.UsageDetailMode) LocalDate(org.joda.time.LocalDate) Predicate(com.google.common.base.Predicate) InvoiceApiException(org.killbill.billing.invoice.api.InvoiceApiException) CatalogApiException(org.killbill.billing.catalog.api.CatalogApiException) RawUsageOptimizerResult(org.killbill.billing.invoice.usage.RawUsageOptimizer.RawUsageOptimizerResult) ImmutableList(com.google.common.collect.ImmutableList) LinkedList(java.util.LinkedList) List(java.util.List) BillingEvent(org.killbill.billing.junction.BillingEvent) SubscriptionUsageInArrear(org.killbill.billing.invoice.usage.SubscriptionUsageInArrear) UUID(java.util.UUID) HashSet(java.util.HashSet)

Aggregations

TrackingRecordId (org.killbill.billing.invoice.generator.InvoiceWithMetadata.TrackingRecordId)7 LocalDate (org.joda.time.LocalDate)6 HashSet (java.util.HashSet)4 InvoiceItem (org.killbill.billing.invoice.api.InvoiceItem)4 RawUsageRecord (org.killbill.billing.usage.api.RawUsageRecord)4 ArrayList (java.util.ArrayList)3 Predicate (com.google.common.base.Predicate)2 BigDecimal (java.math.BigDecimal)2 HashMap (java.util.HashMap)2 List (java.util.List)2 UUID (java.util.UUID)2 DateTime (org.joda.time.DateTime)2 InvoiceApiException (org.killbill.billing.invoice.api.InvoiceApiException)2 InvoiceTrackingModelDao (org.killbill.billing.invoice.dao.InvoiceTrackingModelDao)2 UsageInvoiceItem (org.killbill.billing.invoice.model.UsageInvoiceItem)2 BillingEvent (org.killbill.billing.junction.BillingEvent)2 RolledUpUnit (org.killbill.billing.usage.api.RolledUpUnit)2 VisibleForTesting (com.google.common.annotations.VisibleForTesting)1 Function (com.google.common.base.Function)1 ImmutableList (com.google.common.collect.ImmutableList)1