Search in sources :

Example 1 with InvalidDateSequenceException

use of org.killbill.billing.invoice.model.InvalidDateSequenceException 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 2 with InvalidDateSequenceException

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

the class ProRationTestBase method testCalculateNumberOfBillingCycles.

protected void testCalculateNumberOfBillingCycles(final LocalDate startDate, final LocalDate endDate, final LocalDate targetDate, final int billingCycleDay, final BigDecimal expectedValue) throws InvalidDateSequenceException {
    try {
        final BigDecimal numberOfBillingCycles;
        numberOfBillingCycles = calculateNumberOfBillingCycles(startDate, endDate, targetDate, billingCycleDay);
        assertEquals(numberOfBillingCycles.compareTo(expectedValue), 0, "Actual: " + numberOfBillingCycles.toString() + "; expected: " + expectedValue.toString());
    } catch (InvalidDateSequenceException idse) {
        throw idse;
    } catch (Exception e) {
        fail("Unexpected exception: " + e.getMessage());
    }
}
Also used : BigDecimal(java.math.BigDecimal) InvalidDateSequenceException(org.killbill.billing.invoice.model.InvalidDateSequenceException) InvalidDateSequenceException(org.killbill.billing.invoice.model.InvalidDateSequenceException)

Example 3 with InvalidDateSequenceException

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

BigDecimal (java.math.BigDecimal)3 InvalidDateSequenceException (org.killbill.billing.invoice.model.InvalidDateSequenceException)3 ArrayList (java.util.ArrayList)2 LocalDate (org.joda.time.LocalDate)2 RecurringInvoiceItemData (org.killbill.billing.invoice.model.RecurringInvoiceItemData)2 RecurringInvoiceItemDataWithNextBillingCycleDate (org.killbill.billing.invoice.model.RecurringInvoiceItemDataWithNextBillingCycleDate)2 BillingPeriod (org.killbill.billing.catalog.api.BillingPeriod)1 CatalogApiException (org.killbill.billing.catalog.api.CatalogApiException)1 InvoiceApiException (org.killbill.billing.invoice.api.InvoiceApiException)1 InvoiceItem (org.killbill.billing.invoice.api.InvoiceItem)1 InvoiceDateUtils.calculateProRationBeforeFirstBillingPeriod (org.killbill.billing.invoice.generator.InvoiceDateUtils.calculateProRationBeforeFirstBillingPeriod)1 FixedPriceInvoiceItem (org.killbill.billing.invoice.model.FixedPriceInvoiceItem)1 RecurringInvoiceItem (org.killbill.billing.invoice.model.RecurringInvoiceItem)1