Example usage for org.joda.time LocalDate isAfter

List of usage examples for org.joda.time LocalDate isAfter

Introduction

In this page you can find the example usage for org.joda.time LocalDate isAfter.

Prototype

public boolean isAfter(ReadablePartial partial) 

Source Link

Document

Is this partial later than the specified partial.

Usage

From source file:org.killbill.billing.invoice.generator.BillingIntervalDetail.java

License:Apache License

private void calculateLastBillingCycleDate() {

    // Start from firstBillingCycleDate and billingPeriod until we pass the effectiveEndDate
    LocalDate proposedDate = firstBillingCycleDate;
    int numberOfPeriods = 0;
    while (!proposedDate.isAfter(effectiveEndDate)) {
        proposedDate = firstBillingCycleDate.plusMonths(numberOfPeriods * billingPeriod.getNumberOfMonths());
        numberOfPeriods += 1;// w w w .j ava  2s  . c  o  m
    }

    // Our proposed date is billingCycleDate prior to the effectiveEndDate
    proposedDate = proposedDate.plusMonths(-billingPeriod.getNumberOfMonths());
    proposedDate = alignProposedBillCycleDate(proposedDate, billingCycleDay);

    if (proposedDate.isBefore(firstBillingCycleDate)) {
        // Make sure not to go too far in the past
        lastBillingCycleDate = firstBillingCycleDate;
    } else {
        lastBillingCycleDate = proposedDate;
    }
}

From source file:org.killbill.billing.invoice.generator.DefaultInvoiceGenerator.java

License:Apache License

private List<InvoiceItem> generateUsageConsumableInArrearItems(final Account account, final UUID invoiceId,
        final BillingEventSet eventSet, @Nullable final List<Invoice> existingInvoices,
        final LocalDate targetDate, final InternalCallContext internalCallContext) throws InvoiceApiException {

    final Map<UUID, List<InvoiceItem>> perSubscriptionConsumableInArrearUsageItems = extractPerSubscriptionExistingConsumableInArrearUsageItems(
            eventSet.getUsages(), existingInvoices);
    try {/*from ww w  .  j a v a  2s.  c o  m*/
        final List<InvoiceItem> items = Lists.newArrayList();
        final Iterator<BillingEvent> events = eventSet.iterator();

        RawUsageOptimizerResult rawUsageOptimizerResult = 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 = new LocalDate(event.getEffectiveDate(),
                    event.getAccount().getTimeZone());
            if (eventLocalEffectiveDate.isAfter(targetDate)) {
                continue;
            }

            // Optimize to do the usage query only once after we know there are indeed some usage items
            if (rawUsageOptimizerResult == null && Iterables.any(event.getUsages(), new Predicate<Usage>() {
                @Override
                public boolean apply(@Nullable final Usage input) {
                    return (input.getUsageType() == UsageType.CONSUMABLE
                            && input.getBillingMode() == BillingMode.IN_ARREAR);
                }
            })) {
                rawUsageOptimizerResult = rawUsageOptimizer.getConsumableInArrearUsage(
                        new LocalDate(event.getEffectiveDate(), account.getTimeZone()), targetDate,
                        Iterables.concat(perSubscriptionConsumableInArrearUsageItems.values()),
                        eventSet.getUsages(), internalCallContext);
            }

            // None of the billing events report any usage (CONSUMABLE/IN_ARREAR) sections
            if (rawUsageOptimizerResult == null) {
                continue;
            }

            final UUID subscriptionId = event.getSubscription().getId();
            if (curSubscriptionId != null && !curSubscriptionId.equals(subscriptionId)) {
                final SubscriptionConsumableInArrear subscriptionConsumableInArrear = new SubscriptionConsumableInArrear(
                        invoiceId, curEvents, rawUsageOptimizerResult.getRawUsage(), targetDate,
                        rawUsageOptimizerResult.getRawUsageStartDate());
                final List<InvoiceItem> consumableInUsageArrearItems = perSubscriptionConsumableInArrearUsageItems
                        .get(curSubscriptionId);
                items.addAll(subscriptionConsumableInArrear.computeMissingUsageInvoiceItems(
                        consumableInUsageArrearItems != null ? consumableInUsageArrearItems
                                : ImmutableList.<InvoiceItem>of()));
                curEvents = Lists.newArrayList();
            }
            curSubscriptionId = subscriptionId;
            curEvents.add(event);
        }
        if (curSubscriptionId != null) {
            final SubscriptionConsumableInArrear subscriptionConsumableInArrear = new SubscriptionConsumableInArrear(
                    invoiceId, curEvents, rawUsageOptimizerResult.getRawUsage(), targetDate,
                    rawUsageOptimizerResult.getRawUsageStartDate());
            final List<InvoiceItem> consumableInUsageArrearItems = perSubscriptionConsumableInArrearUsageItems
                    .get(curSubscriptionId);
            items.addAll(subscriptionConsumableInArrear.computeMissingUsageInvoiceItems(
                    consumableInUsageArrearItems != null ? consumableInUsageArrearItems
                            : ImmutableList.<InvoiceItem>of()));
        }
        return items;

    } catch (CatalogApiException e) {
        throw new InvoiceApiException(e);
    }
}

From source file:org.killbill.billing.invoice.generator.DefaultInvoiceGenerator.java

License:Apache License

private List<InvoiceItem> processInAdvanceEvents(final UUID invoiceId, final UUID accountId,
        final BillingEvent thisEvent, @Nullable final BillingEvent nextEvent, final LocalDate targetDate,
        final Currency currency, final StringBuilder logStringBuilder) throws InvoiceApiException {
    final List<InvoiceItem> items = new ArrayList<InvoiceItem>();

    // Handle fixed price items
    final InvoiceItem fixedPriceInvoiceItem = generateFixedPriceItem(invoiceId, accountId, thisEvent,
            targetDate, currency);/*www . j  ava2s  .  c o  m*/
    if (fixedPriceInvoiceItem != null) {
        items.add(fixedPriceInvoiceItem);
    }

    // Handle recurring items
    final BillingPeriod billingPeriod = thisEvent.getBillingPeriod();
    if (billingPeriod != BillingPeriod.NO_BILLING_PERIOD) {
        final BillingModeGenerator billingModeGenerator = instantiateBillingMode(thisEvent.getBillingMode());
        final LocalDate startDate = new LocalDate(thisEvent.getEffectiveDate(), thisEvent.getTimeZone());

        if (!startDate.isAfter(targetDate)) {
            final LocalDate endDate = (nextEvent == null) ? null
                    : new LocalDate(nextEvent.getEffectiveDate(), nextEvent.getTimeZone());

            final int billCycleDayLocal = thisEvent.getBillCycleDayLocal();

            final List<RecurringInvoiceItemData> itemData;
            try {
                itemData = billingModeGenerator.generateInvoiceItemData(startDate, endDate, targetDate,
                        billCycleDayLocal, billingPeriod);
            } catch (InvalidDateSequenceException e) {
                throw new InvoiceApiException(ErrorCode.INVOICE_INVALID_DATE_SEQUENCE, startDate, endDate,
                        targetDate);
            }

            for (final RecurringInvoiceItemData itemDatum : itemData) {
                final BigDecimal rate = thisEvent.getRecurringPrice();

                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);
                }
            }
        }
    }

    // For debugging purposes
    logStringBuilder.append("\n").append(thisEvent);
    for (final InvoiceItem item : items) {
        logStringBuilder.append("\n\t").append(item);
    }

    return items;
}

From source file:org.killbill.billing.invoice.generator.FixedAndRecurringInvoiceItemGenerator.java

License:Apache License

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 {/*from   www  .ja  va  2  s  .c  o m*/
        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);
    }
}

From source file:org.killbill.billing.invoice.generator.FixedAndRecurringInvoiceItemGenerator.java

License:Apache License

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();
    }//w  ww. j a  va  2  s  .co m
    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 there is an endDate and that endDate is before our first coming firstBillingCycleDate, all we have to do
    // is to charge for that period
    //
    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);
    }

    //
    // Leading proration if
    // i) The first firstBillingCycleDate is strictly after our start date AND
    // ii) The endDate is is not null and is strictly after our firstBillingCycleDate (previous check)
    //
    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));
    }

    //
    // Now we check if indeed we need a trailing proration and add that incomplete item
    //
    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);
}

From source file:org.killbill.billing.invoice.generator.FixedAndRecurringInvoiceItemGenerator.java

License:Apache License

private InvoiceItem generateFixedPriceItem(final UUID invoiceId, final UUID accountId,
        final BillingEvent thisEvent, final LocalDate targetDate, final Currency currency,
        final InvoiceItemGeneratorLogger invoiceItemGeneratorLogger,
        final InternalCallContext internalCallContext) throws InvoiceApiException {
    final LocalDate roundedStartDate = internalCallContext.toLocalDate(thisEvent.getEffectiveDate());
    if (roundedStartDate.isAfter(targetDate)) {
        return null;
    } else {/*from w w  w  .j  a va  2  s.  co  m*/
        final BigDecimal fixedPrice = thisEvent.getFixedPrice();

        if (fixedPrice != null) {
            final FixedPriceInvoiceItem fixedPriceInvoiceItem = new FixedPriceInvoiceItem(invoiceId, accountId,
                    thisEvent.getSubscription().getBundleId(), thisEvent.getSubscription().getId(),
                    thisEvent.getPlan().getName(), thisEvent.getPlanPhase().getName(), roundedStartDate,
                    fixedPrice, currency);

            // For debugging purposes
            invoiceItemGeneratorLogger.append(thisEvent, fixedPriceInvoiceItem);

            return fixedPriceInvoiceItem;
        } else {
            return null;
        }
    }
}

From source file:org.killbill.billing.invoice.generator.UsageInvoiceItemGenerator.java

License:Apache License

@Override
public List<InvoiceItem> generateItems(final ImmutableAccountData account, final UUID invoiceId,
        final BillingEventSet eventSet, @Nullable final List<Invoice> existingInvoices,
        final LocalDate targetDate, final Currency targetCurrency,
        final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates,
        final InternalCallContext internalCallContext) throws InvoiceApiException {
    final Map<UUID, List<InvoiceItem>> perSubscriptionInArrearUsageItems = extractPerSubscriptionExistingInArrearUsageItems(
            eventSet.getUsages(), existingInvoices);
    try {/*from   ww w . j av a 2  s  .  c  om*/
        // Pretty-print the generated invoice items from the junction events
        final InvoiceItemGeneratorLogger invoiceItemGeneratorLogger = new InvoiceItemGeneratorLogger(invoiceId,
                account.getId(), "usage", log);

        final LocalDate minBillingEventDate = getMinBillingEventDate(eventSet, internalCallContext);

        final List<InvoiceItem> items = Lists.newArrayList();
        final Iterator<BillingEvent> events = eventSet.iterator();

        RawUsageOptimizerResult rawUsageOptimizerResult = 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 (rawUsageOptimizerResult == null && Iterables.any(event.getUsages(), new Predicate<Usage>() {
                @Override
                public boolean apply(@Nullable final Usage input) {
                    return input.getBillingMode() == BillingMode.IN_ARREAR;
                }
            })) {
                rawUsageOptimizerResult = rawUsageOptimizer.getInArrearUsage(minBillingEventDate, targetDate,
                        Iterables.concat(perSubscriptionInArrearUsageItems.values()), eventSet.getUsages(),
                        internalCallContext);
            }

            // None of the billing events report any usage IN_ARREAR sections
            if (rawUsageOptimizerResult == null) {
                continue;
            }

            final UUID subscriptionId = event.getSubscription().getId();
            if (curSubscriptionId != null && !curSubscriptionId.equals(subscriptionId)) {
                final SubscriptionUsageInArrear subscriptionUsageInArrear = new SubscriptionUsageInArrear(
                        account.getId(), invoiceId, curEvents, rawUsageOptimizerResult.getRawUsage(),
                        targetDate, rawUsageOptimizerResult.getRawUsageStartDate(), internalCallContext);
                final List<InvoiceItem> usageInArrearItems = perSubscriptionInArrearUsageItems
                        .get(curSubscriptionId);

                final SubscriptionUsageInArrearItemsAndNextNotificationDate subscriptionResult = subscriptionUsageInArrear
                        .computeMissingUsageInvoiceItems(usageInArrearItems != null ? usageInArrearItems
                                : ImmutableList.<InvoiceItem>of(), invoiceItemGeneratorLogger);
                final List<InvoiceItem> newInArrearUsageItems = subscriptionResult.getInvoiceItems();
                items.addAll(newInArrearUsageItems);
                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, rawUsageOptimizerResult.getRawUsage(), targetDate,
                    rawUsageOptimizerResult.getRawUsageStartDate(), internalCallContext);
            final List<InvoiceItem> usageInArrearItems = perSubscriptionInArrearUsageItems
                    .get(curSubscriptionId);

            final SubscriptionUsageInArrearItemsAndNextNotificationDate subscriptionResult = subscriptionUsageInArrear
                    .computeMissingUsageInvoiceItems(
                            usageInArrearItems != null ? usageInArrearItems : ImmutableList.<InvoiceItem>of(),
                            invoiceItemGeneratorLogger);
            final List<InvoiceItem> newInArrearUsageItems = subscriptionResult.getInvoiceItems();
            items.addAll(newInArrearUsageItems);
            updatePerSubscriptionNextNotificationUsageDate(curSubscriptionId,
                    subscriptionResult.getPerUsageNotificationDates(), BillingMode.IN_ARREAR,
                    perSubscriptionFutureNotificationDates);
        }

        invoiceItemGeneratorLogger.logItems();

        return items;
    } catch (final CatalogApiException e) {
        throw new InvoiceApiException(e);
    }
}

From source file:org.killbill.billing.invoice.model.InAdvanceBillingMode.java

License:Apache License

@Override
public List<RecurringInvoiceItemData> generateInvoiceItemData(final LocalDate startDate,
        @Nullable final LocalDate endDate, final LocalDate targetDate, final int billingCycleDayLocal,
        final BillingPeriod billingPeriod) throws InvalidDateSequenceException {
    if (endDate != null && endDate.isBefore(startDate)) {
        throw new InvalidDateSequenceException();
    }/*from  w ww  .  j a  v a2 s  .  co m*/
    if (targetDate.isBefore(startDate)) {
        throw new InvalidDateSequenceException();
    }

    final List<RecurringInvoiceItemData> results = new ArrayList<RecurringInvoiceItemData>();

    final BillingIntervalDetail billingIntervalDetail = new BillingIntervalDetail(startDate, endDate,
            targetDate, billingCycleDayLocal, billingPeriod);

    // We are not billing for less than a day (we could...)
    if (endDate != null && endDate.equals(startDate)) {
        return results;
    }
    //
    // If there is an endDate and that endDate is before our first coming firstBillingCycleDate, all we have to do
    // is to charge for that period
    //
    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 results;
    }

    //
    // Leading proration if
    // i) The first firstBillingCycleDate is strictly after our start date AND
    // ii) The endDate is is not null and is strictly after our firstBillingCycleDate (previous check)
    //
    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.size() > 0) {
            // 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));
    }

    //
    // Now we check if indeed we need a trailing proration and add that incomplete item
    //
    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 results;
}

From source file:org.killbill.billing.invoice.usage.ContiguousIntervalConsumableInArrear.java

License:Apache License

/**
 * Builds the transitionTimes associated to that usage section. Those are determined based on billing events for when to start and when to stop,
 * the per usage billingPeriod and finally the targetDate.
 * <p/>//from  ww w  .  j a  va 2s .c o m
 * Those transition dates define the well defined billing granularity periods that should be billed for that specific usage section.
 *
 * @param closedInterval whether there was a last billing event referencing the usage section or whether this is ongoing and
 *                       then targetDate will define the endDate.
 * @return
 */
public ContiguousIntervalConsumableInArrear build(final boolean closedInterval) {

    Preconditions.checkState(!isBuilt.get());
    Preconditions.checkState(
            (!closedInterval && billingEvents.size() >= 1) || (closedInterval && billingEvents.size() >= 2));

    final LocalDate startDate = new LocalDate(billingEvents.get(0).getEffectiveDate(), getAccountTimeZone());
    if (targetDate.isBefore(startDate)) {
        return this;
    }
    final LocalDate endDate = closedInterval
            ? new LocalDate(billingEvents.get(billingEvents.size() - 1).getEffectiveDate(),
                    getAccountTimeZone())
            : targetDate;

    final BillingIntervalDetail bid = new BillingIntervalDetail(startDate, endDate, targetDate, getBCD(),
            usage.getBillingPeriod());

    int numberOfPeriod = 0;
    // First billingCycleDate prior startDate
    LocalDate nextBillCycleDate = bid.getFutureBillingDateFor(numberOfPeriod);
    if (startDate.compareTo(rawUsageStartDate) >= 0) {
        transitionTimes.add(startDate);
    }
    while (!nextBillCycleDate.isAfter(endDate)) {
        if (nextBillCycleDate.isAfter(startDate)) {
            if (nextBillCycleDate.compareTo(rawUsageStartDate) >= 0) {
                transitionTimes.add(nextBillCycleDate);
            }
        }
        numberOfPeriod++;
        nextBillCycleDate = bid.getFutureBillingDateFor(numberOfPeriod);
    }
    isBuilt.set(true);
    return this;
}

From source file:org.killbill.billing.invoice.usage.ContiguousIntervalUsageInArrear.java

License:Apache License

/**
 * Builds the transitionTimes associated to that usage section. Those are determined based on billing events for when to start and when to stop,
 * the per usage billingPeriod and finally the targetDate.
 * <p/>//from  www. j a v a  2  s .  c  o  m
 * Those transition dates define the well defined billing granularity periods that should be billed for that specific usage section.
 *
 * @param closedInterval whether there was a last billing event referencing the usage section or whether this is ongoing and
 *                       then targetDate will define the endDate.
 */
public ContiguousIntervalUsageInArrear build(final boolean closedInterval) {

    Preconditions.checkState(!isBuilt.get());
    Preconditions.checkState(
            (!closedInterval && billingEvents.size() >= 1) || (closedInterval && billingEvents.size() >= 2));

    final LocalDate startDate = internalTenantContext.toLocalDate(billingEvents.get(0).getEffectiveDate());
    if (targetDate.isBefore(startDate)) {
        return this;
    }
    final LocalDate endDate = closedInterval
            ? internalTenantContext.toLocalDate(billingEvents.get(billingEvents.size() - 1).getEffectiveDate())
            : targetDate;

    final BillingIntervalDetail bid = new BillingIntervalDetail(startDate, endDate, targetDate, getBCD(),
            usage.getBillingPeriod(), usage.getBillingMode());

    int numberOfPeriod = 0;
    // First billingCycleDate prior startDate
    LocalDate nextBillCycleDate = bid.getFutureBillingDateFor(numberOfPeriod);
    if (startDate.compareTo(rawUsageStartDate) >= 0) {
        transitionTimes.add(startDate);
    }
    while (!nextBillCycleDate.isAfter(endDate)) {
        if (nextBillCycleDate.isAfter(startDate)) {
            if (nextBillCycleDate.compareTo(rawUsageStartDate) >= 0) {
                transitionTimes.add(nextBillCycleDate);
            }
        }
        numberOfPeriod++;
        nextBillCycleDate = bid.getFutureBillingDateFor(numberOfPeriod);
    }
    if (closedInterval && transitionTimes.size() > 0
            && endDate.isAfter(transitionTimes.get(transitionTimes.size() - 1))) {
        transitionTimes.add(endDate);
    }
    isBuilt.set(true);
    return this;
}