Search in sources :

Example 1 with RecurringInvoiceItemData

use of org.killbill.billing.invoice.model.RecurringInvoiceItemData 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)
@VisibleForTesting
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 Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates, final InternalCallContext internalCallContext) throws InvoiceApiException {
    final List<InvoiceItem> items = new ArrayList<InvoiceItem>();
    final LocalDate thisEventEffectiveDate = internalCallContext.toLocalDate(thisEvent.getEffectiveDate());
    if (thisEventEffectiveDate.compareTo(targetDate) > 0) {
        return items;
    }
    // Handle recurring items
    final BillingPeriod billingPeriod = thisEvent.getBillingPeriod();
    if (billingPeriod != BillingPeriod.NO_BILLING_PERIOD) {
        final Plan currentPlan = thisEvent.getPlan();
        Preconditions.checkNotNull(currentPlan, "Unexpected null Plan name event = %s", thisEvent);
        final BillingMode recurringBillingMode = currentPlan.getRecurringBillingMode();
        final LocalDate startDate = thisEventEffectiveDate;
        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, recurringBillingMode);
            } catch (final InvalidDateSequenceException e) {
                throw new InvoiceApiException(ErrorCode.INVOICE_INVALID_DATE_SEQUENCE, startDate, endDate, targetDate);
            }
            for (final RecurringInvoiceItemData itemDatum : itemDataWithNextBillingCycleDate.getItemData()) {
                final BigDecimal rate = thisEvent.getRecurringPrice();
                if (rate != null) {
                    final BigDecimal amount = KillBillMoney.of(itemDatum.getNumberOfCycles().multiply(rate), currency);
                    final DateTime catalogEffectiveDate = thisEvent.getCatalogEffectiveDate() != null ? thisEvent.getCatalogEffectiveDate() : null;
                    final RecurringInvoiceItem recurringItem = new RecurringInvoiceItem(invoiceId, accountId, thisEvent.getBundleId(), thisEvent.getSubscriptionId(), currentPlan.getProduct().getName(), currentPlan.getName(), thisEvent.getPlanPhase().getName(), catalogEffectiveDate, itemDatum.getStartDate(), itemDatum.getEndDate(), amount, rate, currency);
                    items.add(recurringItem);
                }
            }
            updatePerSubscriptionNextNotificationDate(thisEvent.getSubscriptionId(), itemDataWithNextBillingCycleDate.getNextBillingCycleDate(), items, recurringBillingMode, perSubscriptionFutureNotificationDates);
        }
    } else {
        final SubscriptionFutureNotificationDates futureNotificationDates = perSubscriptionFutureNotificationDates.get(thisEvent.getSubscriptionId());
        if (futureNotificationDates != null) {
            futureNotificationDates.clearNextRecurringDate();
        }
    }
    // For debugging purposes
    invoiceItemGeneratorLogger.append(thisEvent, items);
    return items;
}
Also used : SubscriptionFutureNotificationDates(org.killbill.billing.invoice.generator.InvoiceWithMetadata.SubscriptionFutureNotificationDates) FixedPriceInvoiceItem(org.killbill.billing.invoice.model.FixedPriceInvoiceItem) RecurringInvoiceItem(org.killbill.billing.invoice.model.RecurringInvoiceItem) InvoiceItem(org.killbill.billing.invoice.api.InvoiceItem) InvoiceDateUtils.calculateProRationBeforeFirstBillingPeriod(org.killbill.billing.invoice.generator.InvoiceDateUtils.calculateProRationBeforeFirstBillingPeriod) BillingPeriod(org.killbill.billing.catalog.api.BillingPeriod) RecurringInvoiceItem(org.killbill.billing.invoice.model.RecurringInvoiceItem) ArrayList(java.util.ArrayList) RecurringInvoiceItemData(org.killbill.billing.invoice.model.RecurringInvoiceItemData) Plan(org.killbill.billing.catalog.api.Plan) LocalDate(org.joda.time.LocalDate) BigDecimal(java.math.BigDecimal) DateTime(org.joda.time.DateTime) InvalidDateSequenceException(org.killbill.billing.invoice.model.InvalidDateSequenceException) RecurringInvoiceItemDataWithNextBillingCycleDate(org.killbill.billing.invoice.model.RecurringInvoiceItemDataWithNextBillingCycleDate) InvoiceApiException(org.killbill.billing.invoice.api.InvoiceApiException) BillingMode(org.killbill.billing.catalog.api.BillingMode) VisibleForTesting(com.google.common.annotations.VisibleForTesting)

Example 2 with RecurringInvoiceItemData

use of org.killbill.billing.invoice.model.RecurringInvoiceItemData in project killbill by killbill.

the class FixedAndRecurringInvoiceItemGenerator method generateInvoiceItemData.

public RecurringInvoiceItemDataWithNextBillingCycleDate generateInvoiceItemData(final LocalDate startDate, @Nullable final LocalDate endDate, final LocalDate targetDate, final int billingCycleDayLocal, final BillingPeriod billingPeriod, final BillingMode billingMode) throws InvalidDateSequenceException {
    if (endDate != null && endDate.isBefore(startDate)) {
        throw new InvalidDateSequenceException();
    }
    if (targetDate.isBefore(startDate)) {
        throw new InvalidDateSequenceException();
    }
    final List<RecurringInvoiceItemData> results = new ArrayList<RecurringInvoiceItemData>();
    final BillingIntervalDetail billingIntervalDetail = new BillingIntervalDetail(startDate, endDate, targetDate, billingCycleDayLocal, billingPeriod, billingMode);
    // We are not billing for less than a day
    if (!billingIntervalDetail.hasSomethingToBill()) {
        return new RecurringInvoiceItemDataWithNextBillingCycleDate(results, billingIntervalDetail);
    }
    // 
    if (endDate != null && !endDate.isAfter(billingIntervalDetail.getFirstBillingCycleDate())) {
        final BigDecimal leadingProRationPeriods = calculateProRationBeforeFirstBillingPeriod(startDate, endDate, billingPeriod);
        final RecurringInvoiceItemData itemData = new RecurringInvoiceItemData(startDate, endDate, leadingProRationPeriods);
        results.add(itemData);
        return new RecurringInvoiceItemDataWithNextBillingCycleDate(results, billingIntervalDetail);
    }
    // 
    if (billingIntervalDetail.getFirstBillingCycleDate().isAfter(startDate)) {
        final BigDecimal leadingProRationPeriods = calculateProRationBeforeFirstBillingPeriod(startDate, billingIntervalDetail.getFirstBillingCycleDate(), billingPeriod);
        if (leadingProRationPeriods != null && leadingProRationPeriods.compareTo(BigDecimal.ZERO) > 0) {
            // Not common - add info in the logs for debugging purposes
            final RecurringInvoiceItemData itemData = new RecurringInvoiceItemData(startDate, billingIntervalDetail.getFirstBillingCycleDate(), leadingProRationPeriods);
            log.info("Adding pro-ration: {}", itemData);
            results.add(itemData);
        }
    }
    // 
    // Calculate the effectiveEndDate from the firstBillingCycleDate:
    // - If endDate != null and targetDate is after endDate => this is the endDate and will lead to a trailing pro-ration
    // - If not, this is the last billingCycleDate calculation right after the targetDate
    // 
    final LocalDate effectiveEndDate = billingIntervalDetail.getEffectiveEndDate();
    // 
    // Based on what we calculated previously, code recompute one more time the numberOfWholeBillingPeriods
    // 
    final LocalDate lastBillingCycleDate = billingIntervalDetail.getLastBillingCycleDate();
    final int numberOfWholeBillingPeriods = calculateNumberOfWholeBillingPeriods(billingIntervalDetail.getFirstBillingCycleDate(), lastBillingCycleDate, billingPeriod);
    for (int i = 0; i < numberOfWholeBillingPeriods; i++) {
        final LocalDate servicePeriodStartDate;
        if (!results.isEmpty()) {
            // Make sure the periods align, especially with the pro-ration calculations above
            servicePeriodStartDate = results.get(results.size() - 1).getEndDate();
        } else if (i == 0) {
            // Use the specified start date
            servicePeriodStartDate = startDate;
        } else {
            throw new IllegalStateException("We should at least have one invoice item!");
        }
        // Make sure to align the end date with the BCD
        final LocalDate servicePeriodEndDate = billingIntervalDetail.getFutureBillingDateFor(i + 1);
        results.add(new RecurringInvoiceItemData(servicePeriodStartDate, servicePeriodEndDate, BigDecimal.ONE));
    }
    // 
    if (effectiveEndDate.isAfter(lastBillingCycleDate)) {
        final BigDecimal trailingProRationPeriods = calculateProRationAfterLastBillingCycleDate(effectiveEndDate, lastBillingCycleDate, billingPeriod);
        if (trailingProRationPeriods.compareTo(BigDecimal.ZERO) > 0) {
            // Not common - add info in the logs for debugging purposes
            final RecurringInvoiceItemData itemData = new RecurringInvoiceItemData(lastBillingCycleDate, effectiveEndDate, trailingProRationPeriods);
            log.info("Adding trailing pro-ration: {}", itemData);
            results.add(itemData);
        }
    }
    return new RecurringInvoiceItemDataWithNextBillingCycleDate(results, billingIntervalDetail);
}
Also used : RecurringInvoiceItemDataWithNextBillingCycleDate(org.killbill.billing.invoice.model.RecurringInvoiceItemDataWithNextBillingCycleDate) 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)

Example 3 with RecurringInvoiceItemData

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

Example 4 with RecurringInvoiceItemData

use of org.killbill.billing.invoice.model.RecurringInvoiceItemData in project killbill by killbill.

the class ProRationTestBase method calculateNumberOfBillingCycles.

protected BigDecimal calculateNumberOfBillingCycles(final LocalDate startDate, final LocalDate targetDate, final int billingCycleDay) throws InvalidDateSequenceException {
    final RecurringInvoiceItemDataWithNextBillingCycleDate items = fixedAndRecurringInvoiceItemGenerator.generateInvoiceItemData(startDate, null, targetDate, billingCycleDay, getBillingPeriod(), getBillingMode());
    BigDecimal numberOfBillingCycles = ZERO;
    for (final RecurringInvoiceItemData item : items.getItemData()) {
        numberOfBillingCycles = numberOfBillingCycles.add(item.getNumberOfCycles());
    }
    return numberOfBillingCycles;
}
Also used : RecurringInvoiceItemDataWithNextBillingCycleDate(org.killbill.billing.invoice.model.RecurringInvoiceItemDataWithNextBillingCycleDate) RecurringInvoiceItemData(org.killbill.billing.invoice.model.RecurringInvoiceItemData) BigDecimal(java.math.BigDecimal)

Aggregations

BigDecimal (java.math.BigDecimal)4 RecurringInvoiceItemData (org.killbill.billing.invoice.model.RecurringInvoiceItemData)4 RecurringInvoiceItemDataWithNextBillingCycleDate (org.killbill.billing.invoice.model.RecurringInvoiceItemDataWithNextBillingCycleDate)4 ArrayList (java.util.ArrayList)3 LocalDate (org.joda.time.LocalDate)3 InvalidDateSequenceException (org.killbill.billing.invoice.model.InvalidDateSequenceException)3 BillingPeriod (org.killbill.billing.catalog.api.BillingPeriod)2 InvoiceApiException (org.killbill.billing.invoice.api.InvoiceApiException)2 InvoiceItem (org.killbill.billing.invoice.api.InvoiceItem)2 InvoiceDateUtils.calculateProRationBeforeFirstBillingPeriod (org.killbill.billing.invoice.generator.InvoiceDateUtils.calculateProRationBeforeFirstBillingPeriod)2 FixedPriceInvoiceItem (org.killbill.billing.invoice.model.FixedPriceInvoiceItem)2 RecurringInvoiceItem (org.killbill.billing.invoice.model.RecurringInvoiceItem)2 VisibleForTesting (com.google.common.annotations.VisibleForTesting)1 DateTime (org.joda.time.DateTime)1 BillingMode (org.killbill.billing.catalog.api.BillingMode)1 CatalogApiException (org.killbill.billing.catalog.api.CatalogApiException)1 Plan (org.killbill.billing.catalog.api.Plan)1 SubscriptionFutureNotificationDates (org.killbill.billing.invoice.generator.InvoiceWithMetadata.SubscriptionFutureNotificationDates)1