Search in sources :

Example 21 with InvoiceApiException

use of org.killbill.billing.invoice.api.InvoiceApiException in project killbill by killbill.

the class DefaultInvoiceUserApi method triggerInvoiceGeneration.

@Override
public Invoice triggerInvoiceGeneration(final UUID accountId, @Nullable final LocalDate targetDate, final DryRunArguments dryRunArguments, final CallContext context) throws InvoiceApiException {
    final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(accountId, context);
    final Invoice result = dispatcher.processAccount(true, accountId, targetDate, dryRunArguments, internalContext);
    if (result == null) {
        throw new InvoiceApiException(ErrorCode.INVOICE_NOTHING_TO_DO, accountId, targetDate != null ? targetDate : "null");
    } else {
        return result;
    }
}
Also used : InvoiceApiException(org.killbill.billing.invoice.api.InvoiceApiException) Invoice(org.killbill.billing.invoice.api.Invoice) HtmlInvoice(org.killbill.billing.invoice.template.HtmlInvoice) DefaultInvoice(org.killbill.billing.invoice.model.DefaultInvoice) InternalCallContext(org.killbill.billing.callcontext.InternalCallContext)

Example 22 with InvoiceApiException

use of org.killbill.billing.invoice.api.InvoiceApiException in project killbill by killbill.

the class DefaultInvoiceDao method deleteCBA.

@Override
public void deleteCBA(final UUID accountId, final UUID invoiceId, final UUID invoiceItemId, final InternalCallContext context) throws InvoiceApiException {
    final List<Tag> invoicesTags = getInvoicesTags(context);
    transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<Void>() {

        @Override
        public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
            final InvoiceSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
            // Retrieve the invoice and make sure it belongs to the right account
            final InvoiceModelDao invoice = transactional.getById(invoiceId.toString(), context);
            if (invoice == null || !invoice.getAccountId().equals(accountId)) {
                throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
            }
            // Retrieve the invoice item and make sure it belongs to the right invoice
            final InvoiceItemSqlDao invoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
            final InvoiceItemModelDao cbaItem = invoiceItemSqlDao.getById(invoiceItemId.toString(), context);
            if (cbaItem == null || !cbaItem.getInvoiceId().equals(invoice.getId())) {
                throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_NOT_FOUND, invoiceItemId);
            }
            // First, adjust the same invoice with the CBA amount to "delete"
            final InvoiceItemModelDao cbaAdjItem = new InvoiceItemModelDao(context.getCreatedDate(), InvoiceItemType.CBA_ADJ, invoice.getId(), invoice.getAccountId(), null, null, null, null, null, null, context.getCreatedDate().toLocalDate(), null, cbaItem.getAmount().negate(), null, cbaItem.getCurrency(), cbaItem.getId());
            createInvoiceItemFromTransaction(invoiceItemSqlDao, cbaAdjItem, context);
            // Verify the final invoice balance is not negative
            invoiceDaoHelper.populateChildren(invoice, invoicesTags, entitySqlDaoWrapperFactory, context);
            if (InvoiceModelDaoHelper.getBalance(invoice).compareTo(BigDecimal.ZERO) < 0) {
                throw new InvoiceApiException(ErrorCode.INVOICE_WOULD_BE_NEGATIVE);
            }
            // If there is more account credit than CBA we adjusted, we're done.
            // Otherwise, we need to find further invoices on which this credit was consumed
            final BigDecimal accountCBA = cbaDao.getAccountCBAFromTransaction(entitySqlDaoWrapperFactory, context);
            if (accountCBA.compareTo(BigDecimal.ZERO) < 0) {
                if (accountCBA.compareTo(cbaItem.getAmount().negate()) < 0) {
                    throw new IllegalStateException("The account balance can't be lower than the amount adjusted");
                }
                final List<InvoiceModelDao> invoicesFollowing = getAllNonMigratedInvoicesByAccountAfterDate(transactional, invoice.getInvoiceDate(), context);
                invoiceDaoHelper.populateChildren(invoicesFollowing, invoicesTags, entitySqlDaoWrapperFactory, context);
                // The remaining amount to adjust (i.e. the amount of credits used on following invoices)
                // is the current account CBA balance (minus the sign)
                BigDecimal positiveRemainderToAdjust = accountCBA.negate();
                for (final InvoiceModelDao invoiceFollowing : invoicesFollowing) {
                    if (invoiceFollowing.getId().equals(invoice.getId())) {
                        continue;
                    }
                    // Add a single adjustment per invoice
                    BigDecimal positiveCBAAdjItemAmount = BigDecimal.ZERO;
                    for (final InvoiceItemModelDao cbaUsed : invoiceFollowing.getInvoiceItems()) {
                        // Ignore non CBA items or credits (CBA >= 0)
                        if (!InvoiceItemType.CBA_ADJ.equals(cbaUsed.getType()) || cbaUsed.getAmount().compareTo(BigDecimal.ZERO) >= 0) {
                            continue;
                        }
                        final BigDecimal positiveCBAUsedAmount = cbaUsed.getAmount().negate();
                        final BigDecimal positiveNextCBAAdjItemAmount;
                        if (positiveCBAUsedAmount.compareTo(positiveRemainderToAdjust) < 0) {
                            positiveNextCBAAdjItemAmount = positiveCBAUsedAmount;
                            positiveRemainderToAdjust = positiveRemainderToAdjust.subtract(positiveNextCBAAdjItemAmount);
                        } else {
                            positiveNextCBAAdjItemAmount = positiveRemainderToAdjust;
                            positiveRemainderToAdjust = BigDecimal.ZERO;
                        }
                        positiveCBAAdjItemAmount = positiveCBAAdjItemAmount.add(positiveNextCBAAdjItemAmount);
                        if (positiveRemainderToAdjust.compareTo(BigDecimal.ZERO) == 0) {
                            break;
                        }
                    }
                    // Add the adjustment on that invoice
                    final InvoiceItemModelDao nextCBAAdjItem = new InvoiceItemModelDao(context.getCreatedDate(), InvoiceItemType.CBA_ADJ, invoiceFollowing.getId(), invoice.getAccountId(), null, null, null, null, null, null, context.getCreatedDate().toLocalDate(), null, positiveCBAAdjItemAmount, null, cbaItem.getCurrency(), cbaItem.getId());
                    createInvoiceItemFromTransaction(invoiceItemSqlDao, nextCBAAdjItem, context);
                    if (positiveRemainderToAdjust.compareTo(BigDecimal.ZERO) == 0) {
                        break;
                    }
                }
            }
            return null;
        }
    });
}
Also used : InvoiceApiException(org.killbill.billing.invoice.api.InvoiceApiException) EventBusException(org.killbill.bus.api.PersistentBus.EventBusException) EntityPersistenceException(org.killbill.billing.entity.EntityPersistenceException) BigDecimal(java.math.BigDecimal) InvoiceApiException(org.killbill.billing.invoice.api.InvoiceApiException) EntitySqlDaoWrapperFactory(org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory) List(java.util.List) ImmutableList(com.google.common.collect.ImmutableList) LinkedList(java.util.LinkedList) Tag(org.killbill.billing.util.tag.Tag)

Example 23 with InvoiceApiException

use of org.killbill.billing.invoice.api.InvoiceApiException in project killbill by killbill.

the class InvoiceDaoHelper method computeItemAdjustmentsForTargetInvoiceItem.

private static void computeItemAdjustmentsForTargetInvoiceItem(final InvoiceItemModelDao targetInvoiceItem, final List<InvoiceItemModelDao> adjustedOrRepairedItems, final Map<UUID, BigDecimal> inputAdjInvoiceItem, final Map<UUID, BigDecimal> outputAdjInvoiceItem) throws InvoiceApiException {
    final BigDecimal originalItemAmount = targetInvoiceItem.getAmount();
    final BigDecimal maxAdjLeftAmount = computeItemAdjustmentAmount(originalItemAmount, adjustedOrRepairedItems);
    final BigDecimal proposedItemAmount = inputAdjInvoiceItem.get(targetInvoiceItem.getId());
    if (proposedItemAmount != null && proposedItemAmount.compareTo(maxAdjLeftAmount) > 0) {
        throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_ADJUSTMENT_AMOUNT_INVALID, proposedItemAmount, maxAdjLeftAmount);
    }
    final BigDecimal itemAmountToAdjust = MoreObjects.firstNonNull(proposedItemAmount, maxAdjLeftAmount);
    if (itemAmountToAdjust.compareTo(BigDecimal.ZERO) > 0) {
        outputAdjInvoiceItem.put(targetInvoiceItem.getId(), itemAmountToAdjust);
    }
}
Also used : InvoiceApiException(org.killbill.billing.invoice.api.InvoiceApiException) BigDecimal(java.math.BigDecimal)

Example 24 with InvoiceApiException

use of org.killbill.billing.invoice.api.InvoiceApiException in project killbill by killbill.

the class InvoiceListener method handleEventForInvoiceNotification.

public void handleEventForInvoiceNotification(final UUID subscriptionId, final DateTime eventDateTime, final UUID userToken, final Long accountRecordId, final Long tenantRecordId) {
    try {
        final InternalCallContext context = internalCallContextFactory.createInternalCallContext(tenantRecordId, accountRecordId, "Next Billing Date", CallOrigin.INTERNAL, UserType.SYSTEM, userToken);
        dispatcher.processSubscriptionForInvoiceNotification(subscriptionId, context.toLocalDate(eventDateTime), context);
    } catch (InvoiceApiException e) {
        log.warn("Unable to process subscriptionId='{}', eventDateTime='{}'", subscriptionId, eventDateTime, e);
    }
}
Also used : InvoiceApiException(org.killbill.billing.invoice.api.InvoiceApiException) InternalCallContext(org.killbill.billing.callcontext.InternalCallContext)

Example 25 with InvoiceApiException

use of org.killbill.billing.invoice.api.InvoiceApiException in project killbill by killbill.

the class FixedAndRecurringInvoiceItemGenerator method processRecurringEvent.

// Turn a set of events into a list of invoice items. Note that the dates on the invoice items will be rounded (granularity of a day)
private List<InvoiceItem> processRecurringEvent(final UUID invoiceId, final UUID accountId, final BillingEvent thisEvent, @Nullable final BillingEvent nextEvent, final LocalDate targetDate, final Currency currency, final InvoiceItemGeneratorLogger invoiceItemGeneratorLogger, final BillingMode billingMode, final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate, final InternalCallContext internalCallContext) throws InvoiceApiException {
    try {
        final List<InvoiceItem> items = new ArrayList<InvoiceItem>();
        // For FIXEDTERM phases we need to stop when the specified duration has been reached
        final LocalDate maxEndDate = thisEvent.getPlanPhase().getPhaseType() == PhaseType.FIXEDTERM ? thisEvent.getPlanPhase().getDuration().addToLocalDate(internalCallContext.toLocalDate(thisEvent.getEffectiveDate())) : null;
        // Handle recurring items
        final BillingPeriod billingPeriod = thisEvent.getBillingPeriod();
        if (billingPeriod != BillingPeriod.NO_BILLING_PERIOD) {
            final LocalDate startDate = internalCallContext.toLocalDate(thisEvent.getEffectiveDate());
            if (!startDate.isAfter(targetDate)) {
                final LocalDate endDate = (nextEvent == null) ? null : internalCallContext.toLocalDate(nextEvent.getEffectiveDate());
                final int billCycleDayLocal = thisEvent.getBillCycleDayLocal();
                final RecurringInvoiceItemDataWithNextBillingCycleDate itemDataWithNextBillingCycleDate;
                try {
                    itemDataWithNextBillingCycleDate = generateInvoiceItemData(startDate, endDate, targetDate, billCycleDayLocal, billingPeriod, billingMode);
                } catch (final InvalidDateSequenceException e) {
                    throw new InvoiceApiException(ErrorCode.INVOICE_INVALID_DATE_SEQUENCE, startDate, endDate, targetDate);
                }
                for (final RecurringInvoiceItemData itemDatum : itemDataWithNextBillingCycleDate.getItemData()) {
                    // Stop if there a maxEndDate and we have reached it
                    if (maxEndDate != null && maxEndDate.compareTo(itemDatum.getEndDate()) < 0) {
                        break;
                    }
                    final BigDecimal rate = thisEvent.getRecurringPrice(internalCallContext.toUTCDateTime(itemDatum.getStartDate()));
                    if (rate != null) {
                        final BigDecimal amount = KillBillMoney.of(itemDatum.getNumberOfCycles().multiply(rate), currency);
                        final RecurringInvoiceItem recurringItem = new RecurringInvoiceItem(invoiceId, accountId, thisEvent.getSubscription().getBundleId(), thisEvent.getSubscription().getId(), thisEvent.getPlan().getName(), thisEvent.getPlanPhase().getName(), itemDatum.getStartDate(), itemDatum.getEndDate(), amount, rate, currency);
                        items.add(recurringItem);
                    }
                }
                updatePerSubscriptionNextNotificationDate(thisEvent.getSubscription().getId(), itemDataWithNextBillingCycleDate.getNextBillingCycleDate(), items, billingMode, perSubscriptionFutureNotificationDate);
            }
        }
        // For debugging purposes
        invoiceItemGeneratorLogger.append(thisEvent, items);
        return items;
    } catch (final CatalogApiException e) {
        throw new InvoiceApiException(e);
    }
}
Also used : FixedPriceInvoiceItem(org.killbill.billing.invoice.model.FixedPriceInvoiceItem) RecurringInvoiceItem(org.killbill.billing.invoice.model.RecurringInvoiceItem) InvoiceItem(org.killbill.billing.invoice.api.InvoiceItem) BillingPeriod(org.killbill.billing.catalog.api.BillingPeriod) InvoiceDateUtils.calculateProRationBeforeFirstBillingPeriod(org.killbill.billing.invoice.generator.InvoiceDateUtils.calculateProRationBeforeFirstBillingPeriod) RecurringInvoiceItem(org.killbill.billing.invoice.model.RecurringInvoiceItem) ArrayList(java.util.ArrayList) RecurringInvoiceItemData(org.killbill.billing.invoice.model.RecurringInvoiceItemData) LocalDate(org.joda.time.LocalDate) BigDecimal(java.math.BigDecimal) InvalidDateSequenceException(org.killbill.billing.invoice.model.InvalidDateSequenceException) RecurringInvoiceItemDataWithNextBillingCycleDate(org.killbill.billing.invoice.model.RecurringInvoiceItemDataWithNextBillingCycleDate) InvoiceApiException(org.killbill.billing.invoice.api.InvoiceApiException) CatalogApiException(org.killbill.billing.catalog.api.CatalogApiException)

Aggregations

InvoiceApiException (org.killbill.billing.invoice.api.InvoiceApiException)56 UUID (java.util.UUID)29 Invoice (org.killbill.billing.invoice.api.Invoice)26 InvoiceItem (org.killbill.billing.invoice.api.InvoiceItem)24 BigDecimal (java.math.BigDecimal)23 LocalDate (org.joda.time.LocalDate)23 DefaultInvoice (org.killbill.billing.invoice.model.DefaultInvoice)19 Test (org.testng.annotations.Test)16 InternalCallContext (org.killbill.billing.callcontext.InternalCallContext)14 FixedPriceInvoiceItem (org.killbill.billing.invoice.model.FixedPriceInvoiceItem)14 RecurringInvoiceItem (org.killbill.billing.invoice.model.RecurringInvoiceItem)14 LinkedList (java.util.LinkedList)13 ItemAdjInvoiceItem (org.killbill.billing.invoice.model.ItemAdjInvoiceItem)12 RepairAdjInvoiceItem (org.killbill.billing.invoice.model.RepairAdjInvoiceItem)12 BillingEventSet (org.killbill.billing.junction.BillingEventSet)9 AccountApiException (org.killbill.billing.account.api.AccountApiException)7 MockPlan (org.killbill.billing.catalog.MockPlan)7 MockPlanPhase (org.killbill.billing.catalog.MockPlanPhase)7 MockBillingEventSet (org.killbill.billing.invoice.MockBillingEventSet)7 SubscriptionFutureNotificationDates (org.killbill.billing.invoice.generator.InvoiceWithMetadata.SubscriptionFutureNotificationDates)7