Search in sources :

Example 1 with ExistingInvoiceMetadata

use of org.killbill.billing.invoice.dao.ExistingInvoiceMetadata 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)

Aggregations

HashSet (java.util.HashSet)1 UUID (java.util.UUID)1 DateTime (org.joda.time.DateTime)1 LocalDate (org.joda.time.LocalDate)1 AccountApiException (org.killbill.billing.account.api.AccountApiException)1 ImmutableAccountData (org.killbill.billing.account.api.ImmutableAccountData)1 InternalCallContext (org.killbill.billing.callcontext.InternalCallContext)1 BusInternalEvent (org.killbill.billing.events.BusInternalEvent)1 InvoiceItem (org.killbill.billing.invoice.api.InvoiceItem)1 DefaultNullInvoiceEvent (org.killbill.billing.invoice.api.user.DefaultNullInvoiceEvent)1 ExistingInvoiceMetadata (org.killbill.billing.invoice.dao.ExistingInvoiceMetadata)1 InvoiceItemModelDao (org.killbill.billing.invoice.dao.InvoiceItemModelDao)1 InvoiceModelDao (org.killbill.billing.invoice.dao.InvoiceModelDao)1 InvoiceTrackingModelDao (org.killbill.billing.invoice.dao.InvoiceTrackingModelDao)1 InvoiceWithMetadata (org.killbill.billing.invoice.generator.InvoiceWithMetadata)1 TrackingRecordId (org.killbill.billing.invoice.generator.InvoiceWithMetadata.TrackingRecordId)1 DefaultInvoice (org.killbill.billing.invoice.model.DefaultInvoice)1 ItemAdjInvoiceItem (org.killbill.billing.invoice.model.ItemAdjInvoiceItem)1 ParentInvoiceItem (org.killbill.billing.invoice.model.ParentInvoiceItem)1 CallContext (org.killbill.billing.util.callcontext.CallContext)1