List of usage examples for org.joda.time LocalDate compareTo
public int compareTo(ReadablePartial partial)
From source file:org.killbill.billing.entitlement.engine.core.DefaultEventsStream.java
License:Apache License
private void computeEntitlementEffectiveEndDate() { LocalDate result = null;/*from w ww . j ava 2 s. c om*/ BlockingState lastEntry; lastEntry = (!subscriptionEntitlementStates.isEmpty()) ? subscriptionEntitlementStates.get(subscriptionEntitlementStates.size() - 1) : null; if (lastEntry != null && DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(lastEntry.getStateName())) { result = new LocalDate(lastEntry.getEffectiveDate(), account.getTimeZone()); } lastEntry = (!bundleEntitlementStates.isEmpty()) ? bundleEntitlementStates.get(bundleEntitlementStates.size() - 1) : null; if (lastEntry != null && DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(lastEntry.getStateName())) { final LocalDate localDate = new LocalDate(lastEntry.getEffectiveDate(), account.getTimeZone()); result = ((result == null) || (localDate.compareTo(result) < 0)) ? localDate : result; } lastEntry = (!accountEntitlementStates.isEmpty()) ? accountEntitlementStates.get(accountEntitlementStates.size() - 1) : null; if (lastEntry != null && DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(lastEntry.getStateName())) { final LocalDate localDate = new LocalDate(lastEntry.getEffectiveDate(), account.getTimeZone()); result = ((result == null) || (localDate.compareTo(result) < 0)) ? localDate : result; } entitlementEffectiveEndDate = result; }
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 w ww. ja v a 2 s .c om*/ 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
private void updatePerSubscriptionNextNotificationDate(final UUID subscriptionId, final LocalDate nextBillingCycleDate, final List<InvoiceItem> newProposedItems, final BillingMode billingMode, final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates) { LocalDate nextNotificationDate = null; switch (billingMode) { case IN_ADVANCE: for (final InvoiceItem item : newProposedItems) { if ((item.getEndDate() != null) && (item.getAmount() == null || item.getAmount().compareTo(BigDecimal.ZERO) >= 0)) { if (nextNotificationDate == null) { nextNotificationDate = item.getEndDate(); } else { nextNotificationDate = nextNotificationDate.compareTo(item.getEndDate()) > 0 ? nextNotificationDate : item.getEndDate(); }//from w w w . j av a2 s. c o m } } break; case IN_ARREAR: nextNotificationDate = nextBillingCycleDate; break; default: throw new IllegalStateException("Unrecognized billing mode " + billingMode); } if (nextNotificationDate != null) { SubscriptionFutureNotificationDates subscriptionFutureNotificationDates = perSubscriptionFutureNotificationDates .get(subscriptionId); if (subscriptionFutureNotificationDates == null) { subscriptionFutureNotificationDates = new SubscriptionFutureNotificationDates(billingMode); perSubscriptionFutureNotificationDates.put(subscriptionId, subscriptionFutureNotificationDates); } subscriptionFutureNotificationDates.updateNextRecurringDateIfRequired(nextNotificationDate); } }
From source file:org.killbill.billing.invoice.generator.FixedAndRecurringInvoiceItemGenerator.java
License:Apache License
@VisibleForTesting void safetyBounds(final Iterable<InvoiceItem> resultingItems, final Multimap<UUID, LocalDate> createdItemsPerDayPerSubscription, final InternalTenantContext internalCallContext) throws InvoiceApiException { // Trigger an exception if we detect the creation of similar items for a given subscription // See https://github.com/killbill/killbill/issues/664 if (config.isSanitySafetyBoundEnabled(internalCallContext)) { final Map<UUID, Multimap<LocalDate, InvoiceItem>> fixedItemsPerDateAndSubscription = new HashMap<UUID, Multimap<LocalDate, InvoiceItem>>(); final Map<UUID, Multimap<Range<LocalDate>, InvoiceItem>> recurringItemsPerServicePeriodAndSubscription = new HashMap<UUID, Multimap<Range<LocalDate>, InvoiceItem>>(); for (final InvoiceItem resultingItem : resultingItems) { if (resultingItem.getInvoiceItemType() == InvoiceItemType.FIXED) { if (fixedItemsPerDateAndSubscription.get(resultingItem.getSubscriptionId()) == null) { fixedItemsPerDateAndSubscription.put(resultingItem.getSubscriptionId(), LinkedListMultimap.<LocalDate, InvoiceItem>create()); }/*w ww . jav a2s . co m*/ fixedItemsPerDateAndSubscription.get(resultingItem.getSubscriptionId()) .put(resultingItem.getStartDate(), resultingItem); final Collection<InvoiceItem> resultingInvoiceItems = fixedItemsPerDateAndSubscription .get(resultingItem.getSubscriptionId()).get(resultingItem.getStartDate()); if (resultingInvoiceItems.size() > 1) { throw new InvoiceApiException(ErrorCode.UNEXPECTED_ERROR, String.format( "SAFETY BOUND TRIGGERED Multiple FIXED items for subscriptionId='%s', startDate='%s', resultingItems=%s", resultingItem.getSubscriptionId(), resultingItem.getStartDate(), resultingInvoiceItems)); } } else if (resultingItem.getInvoiceItemType() == InvoiceItemType.RECURRING) { if (recurringItemsPerServicePeriodAndSubscription .get(resultingItem.getSubscriptionId()) == null) { recurringItemsPerServicePeriodAndSubscription.put(resultingItem.getSubscriptionId(), LinkedListMultimap.<Range<LocalDate>, InvoiceItem>create()); } final Range<LocalDate> interval = Range.<LocalDate>closedOpen(resultingItem.getStartDate(), resultingItem.getEndDate()); recurringItemsPerServicePeriodAndSubscription.get(resultingItem.getSubscriptionId()) .put(interval, resultingItem); final Collection<InvoiceItem> resultingInvoiceItems = recurringItemsPerServicePeriodAndSubscription .get(resultingItem.getSubscriptionId()).get(interval); if (resultingInvoiceItems.size() > 1) { throw new InvoiceApiException(ErrorCode.UNEXPECTED_ERROR, String.format( "SAFETY BOUND TRIGGERED Multiple RECURRING items for subscriptionId='%s', startDate='%s', endDate='%s', resultingItems=%s", resultingItem.getSubscriptionId(), resultingItem.getStartDate(), resultingItem.getEndDate(), resultingInvoiceItems)); } } } } // Trigger an exception if we create too many invoice items for a subscription on a given day if (config.getMaxDailyNumberOfItemsSafetyBound(internalCallContext) == -1) { // Safety bound disabled return; } for (final InvoiceItem invoiceItem : resultingItems) { if (invoiceItem.getSubscriptionId() != null) { final LocalDate resultingItemCreationDay = trackInvoiceItemCreatedDay(invoiceItem, createdItemsPerDayPerSubscription, internalCallContext); final Collection<LocalDate> creationDaysForSubscription = createdItemsPerDayPerSubscription .get(invoiceItem.getSubscriptionId()); int i = 0; for (final LocalDate creationDayForSubscription : creationDaysForSubscription) { if (creationDayForSubscription.compareTo(resultingItemCreationDay) == 0) { i++; if (i > config.getMaxDailyNumberOfItemsSafetyBound(internalCallContext)) { // Proposed items have already been logged throw new InvoiceApiException(ErrorCode.UNEXPECTED_ERROR, String.format("SAFETY BOUND TRIGGERED subscriptionId='%s', resultingItem=%s", invoiceItem.getSubscriptionId(), invoiceItem)); } } } } } }
From source file:org.killbill.billing.invoice.InvoiceDispatcher.java
License:Apache License
@VisibleForTesting FutureAccountNotifications createNextFutureNotificationDate(final Iterable<InvoiceItemModelDao> invoiceItems, final BillingEventSet billingEvents, final DateAndTimeZoneContext dateAndTimeZoneContext, final InternalCallContext context) { final Map<UUID, List<SubscriptionNotification>> result = new HashMap<UUID, List<SubscriptionNotification>>(); final Map<String, LocalDate> perSubscriptionUsage = new HashMap<String, LocalDate>(); // For each subscription that has a positive (amount) recurring item, create the date // at which we should be called back for next invoice. ////from www .j a v a2s. c om for (final InvoiceItemModelDao item : invoiceItems) { List<SubscriptionNotification> perSubscriptionCallback = result.get(item.getSubscriptionId()); if (perSubscriptionCallback == null && (item.getType() == InvoiceItemType.RECURRING || item.getType() == InvoiceItemType.USAGE)) { perSubscriptionCallback = new ArrayList<SubscriptionNotification>(); result.put(item.getSubscriptionId(), perSubscriptionCallback); } switch (item.getType()) { case RECURRING: if ((item.getEndDate() != null) && (item.getAmount() == null || item.getAmount().compareTo(BigDecimal.ZERO) >= 0)) { perSubscriptionCallback.add(new SubscriptionNotification( dateAndTimeZoneContext.computeUTCDateTimeFromLocalDate(item.getEndDate()), true)); } break; case USAGE: final String key = item.getSubscriptionId().toString() + ":" + item.getUsageName(); final LocalDate perSubscriptionUsageRecurringDate = perSubscriptionUsage.get(key); if (perSubscriptionUsageRecurringDate == null || perSubscriptionUsageRecurringDate.compareTo(item.getEndDate()) < 0) { perSubscriptionUsage.put(key, item.getEndDate()); } break; default: // Ignore } } for (final String key : perSubscriptionUsage.keySet()) { final String[] parts = key.split(":"); final UUID subscriptionId = UUID.fromString(parts[0]); final List<SubscriptionNotification> perSubscriptionCallback = result.get(subscriptionId); final String usageName = parts[1]; final LocalDate endDate = perSubscriptionUsage.get(key); final DateTime subscriptionUsageCallbackDate = getNextUsageBillingDate(subscriptionId, usageName, endDate, dateAndTimeZoneContext, billingEvents); perSubscriptionCallback.add(new SubscriptionNotification(subscriptionUsageCallbackDate, true)); } // If dryRunNotification is enabled we also need to fetch the upcoming PHASE dates (we add SubscriptionNotification with isForInvoiceNotificationTrigger = false) final boolean isInvoiceNotificationEnabled = invoiceConfig.getDryRunNotificationSchedule().getMillis() > 0; if (isInvoiceNotificationEnabled) { final Map<UUID, DateTime> upcomingPhasesForSubscriptions = subscriptionApi .getNextFutureEventForSubscriptions(SubscriptionBaseTransitionType.PHASE, context); for (UUID cur : upcomingPhasesForSubscriptions.keySet()) { final DateTime curDate = upcomingPhasesForSubscriptions.get(cur); List<SubscriptionNotification> resultValue = result.get(cur); if (resultValue == null) { resultValue = new ArrayList<SubscriptionNotification>(); } resultValue.add(new SubscriptionNotification(curDate, false)); result.put(cur, resultValue); } } return new FutureAccountNotifications(dateAndTimeZoneContext, result); }
From source file:org.killbill.billing.invoice.notification.DefaultNextBillingDatePoster.java
License:Apache License
private void insertNextBillingFromTransactionInternal( final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId, final UUID subscriptionId, final Boolean isDryRunForInvoiceNotification, final DateTime futureNotificationTime, final DateTime targetDate, final DateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext) { final NotificationQueue nextBillingQueue; try {/*from w ww . j ava2s .c o m*/ nextBillingQueue = notificationQueueService.getNotificationQueue( DefaultInvoiceService.INVOICE_SERVICE_NAME, DefaultNextBillingDateNotifier.NEXT_BILLING_DATE_NOTIFIER_QUEUE); // If we see existing notification for the same date (and isDryRunForInvoiceNotification mode), we don't insert a new notification final List<NotificationEventWithMetadata<NextBillingDateNotificationKey>> futureNotifications = nextBillingQueue .getFutureNotificationFromTransactionForSearchKeys(internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId(), entitySqlDaoWrapperFactory.getHandle().getConnection()); final NotificationEventWithMetadata<NextBillingDateNotificationKey> existingFutureNotificationWithSameDate = Iterables .tryFind(futureNotifications, new Predicate<NotificationEventWithMetadata<NextBillingDateNotificationKey>>() { @Override public boolean apply( final NotificationEventWithMetadata<NextBillingDateNotificationKey> input) { final boolean isEventDryRunForNotifications = input.getEvent() .isDryRunForInvoiceNotification() != null ? input.getEvent().isDryRunForInvoiceNotification() : false; final LocalDate notificationEffectiveLocaleDate = new LocalDate( futureNotificationTime, accountDateAndTimeZoneContext.getAccountTimeZone()); final LocalDate eventEffectiveLocaleDate = new LocalDate( input.getEffectiveDate(), accountDateAndTimeZoneContext.getAccountTimeZone()); return notificationEffectiveLocaleDate.compareTo(eventEffectiveLocaleDate) == 0 && ((isDryRunForInvoiceNotification && isEventDryRunForNotifications) || (!isDryRunForInvoiceNotification && !isEventDryRunForNotifications)); } }) .orNull(); if (existingFutureNotificationWithSameDate == null) { log.info("Queuing next billing date notification at {} for subscriptionId {}", futureNotificationTime.toString(), subscriptionId.toString()); nextBillingQueue.recordFutureNotificationFromTransaction( entitySqlDaoWrapperFactory.getHandle().getConnection(), futureNotificationTime, new NextBillingDateNotificationKey(subscriptionId, targetDate, isDryRunForInvoiceNotification), internalCallContext.getUserToken(), internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId()); } else if (log.isDebugEnabled()) { log.debug( "********************* SKIPPING Queuing next billing date notification at {} for subscriptionId {} *******************", futureNotificationTime.toString(), subscriptionId.toString()); } } catch (final NoSuchNotificationQueue e) { log.error("Attempting to put items on a non-existent queue (NextBillingDateNotifier).", e); } catch (final IOException e) { log.error("Failed to serialize notificationKey for subscriptionId {}", subscriptionId); } }
From source file:org.killbill.billing.invoice.notification.ParentInvoiceCommitmentPoster.java
License:Apache License
public void insertParentInvoiceFromTransactionInternal( final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID invoiceId, final DateTime futureNotificationTime, final InternalCallContext internalCallContext) { final NotificationQueue commitInvoiceQueue; try {//from www .ja v a2 s . co m commitInvoiceQueue = notificationQueueService.getNotificationQueue( DefaultInvoiceService.INVOICE_SERVICE_NAME, ParentInvoiceCommitmentNotifier.PARENT_INVOICE_COMMITMENT_NOTIFIER_QUEUE); // If we see existing notification for the same date we don't insert a new notification final Iterable<NotificationEventWithMetadata<ParentInvoiceCommitmentNotificationKey>> futureNotifications = commitInvoiceQueue .getFutureNotificationFromTransactionForSearchKeys(internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId(), entitySqlDaoWrapperFactory.getHandle().getConnection()); boolean existingFutureNotificationWithSameDate = false; for (final NotificationEventWithMetadata<ParentInvoiceCommitmentNotificationKey> input : futureNotifications) { final LocalDate notificationEffectiveLocaleDate = internalCallContext .toLocalDate(futureNotificationTime); final LocalDate eventEffectiveLocaleDate = internalCallContext .toLocalDate(input.getEffectiveDate()); if (notificationEffectiveLocaleDate.compareTo(eventEffectiveLocaleDate) == 0) { existingFutureNotificationWithSameDate = true; } // Go through all results to close the connection } if (!existingFutureNotificationWithSameDate) { log.info("Queuing parent invoice commitment notification at {} for invoiceId {}", futureNotificationTime.toString(), invoiceId.toString()); commitInvoiceQueue.recordFutureNotificationFromTransaction( entitySqlDaoWrapperFactory.getHandle().getConnection(), futureNotificationTime, new ParentInvoiceCommitmentNotificationKey(invoiceId), internalCallContext.getUserToken(), internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId()); } else if (log.isDebugEnabled()) { log.debug( "********************* SKIPPING Queuing parent invoice commitment notification at {} for invoiceId {} *******************", futureNotificationTime.toString(), invoiceId.toString()); } } catch (final NoSuchNotificationQueue e) { log.error("Attempting to put items on a non-existent queue (ParentInvoiceCommitmentNotifier).", e); } catch (final IOException e) { log.error("Failed to serialize notificationKey for invoiceId {}", invoiceId); } }
From source file:org.killbill.billing.invoice.tree.Item.java
License:Apache License
public InvoiceItem toProratedInvoiceItem(final LocalDate newStartDate, final LocalDate newEndDate) { int nbTotalDays = Days.daysBetween(startDate, endDate).getDays(); final boolean prorated = !(newStartDate.compareTo(startDate) == 0 && newEndDate.compareTo(endDate) == 0); // Pro-ration is built by using the startDate, endDate and amount of this item instead of using the rate and a potential full period. final BigDecimal positiveAmount = prorated ? InvoiceDateUtils .calculateProrationBetweenDates(newStartDate, newEndDate, nbTotalDays).multiply(amount) : amount; if (action == ItemAction.ADD) { return new RecurringInvoiceItem(id, createdDate, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, newStartDate, newEndDate, KillBillMoney.of(positiveAmount, currency), rate, currency);/* ww w . j a v a2 s . com*/ } else { // We first compute the maximum amount after adjustment and that sets the amount limit of how much can be repaired. final BigDecimal maxAvailableAmountAfterAdj = amount.subtract(adjustedAmount); final BigDecimal maxAvailableAmountForRepair = maxAvailableAmountAfterAdj .subtract(currentRepairedAmount); final BigDecimal positiveAmountForRepair = positiveAmount.compareTo(maxAvailableAmountForRepair) <= 0 ? positiveAmount : maxAvailableAmountForRepair; return positiveAmountForRepair.compareTo(BigDecimal.ZERO) > 0 ? new RepairAdjInvoiceItem(targetInvoiceId, accountId, newStartDate, newEndDate, KillBillMoney.of(positiveAmountForRepair.negate(), currency), currency, linkedId) : null; } }
From source file:org.killbill.billing.invoice.tree.NodeInterval.java
License:Apache License
/** * Build the tree by calling the callback on the last node in the tree or remaining part with no children. * * @param callback the callback which perform the build logic. * @return whether or not the parent NodeInterval should ignore the period covered by the child (NodeInterval) *///from w ww . j a v a 2s .c o m public void build(final BuildNodeCallback callback) { Preconditions.checkNotNull(callback); if (leftChild == null) { callback.onLastNode(this); return; } LocalDate curDate = start; NodeInterval curChild = leftChild; while (curChild != null) { if (curChild.getStart().compareTo(curDate) > 0) { callback.onMissingInterval(this, curDate, curChild.getStart()); } curChild.build(callback); // Note that skip to child endDate, meaning that we always consider the child [start end] curDate = curChild.getEnd(); curChild = curChild.getRightSibling(); } // Finally if there is a hole at the end, we build the missing piece from ourselves if (curDate.compareTo(end) < 0) { callback.onMissingInterval(this, curDate, end); } return; }
From source file:org.killbill.billing.invoice.tree.NodeInterval.java
License:Apache License
@JsonIgnore public boolean isPartitionedByChildren() { if (leftChild == null) { return false; }/* www .j a va 2s.c om*/ LocalDate curDate = start; NodeInterval curChild = leftChild; while (curChild != null) { if (curChild.getStart().compareTo(curDate) > 0) { return false; } curDate = curChild.getEnd(); curChild = curChild.getRightSibling(); } return (curDate.compareTo(end) == 0); }