protected BillingEvent precedingBillingEventForSubscription(final DateTime disabledDurationStart, final SortedSet<BillingEvent> subscriptionBillingEvents) { if (disabledDurationStart == null) { return null; } // We look for the first billingEvent strictly prior our disabledDurationStart or null if none BillingEvent prev = null; for (final BillingEvent event : subscriptionBillingEvents) { if (!event.getEffectiveDate().isBefore(disabledDurationStart)) { return prev; } else { prev = event; } } return prev; }
private LocalDate getMinBillingEventDate(final BillingEventSet eventSet, final InternalCallContext internalCallContext) { DateTime minDate = null; for (final BillingEvent cur : eventSet) { if (minDate == null || minDate.compareTo(cur.getEffectiveDate()) > 0) { minDate = cur.getEffectiveDate(); } } return internalCallContext.toLocalDate(minDate); }
protected SortedSet<BillingEvent> eventsToRemove(final List<DisabledDuration> disabledDuration, final SortedSet<BillingEvent> subscriptionBillingEvents) { final SortedSet<BillingEvent> result = new TreeSet<BillingEvent>(); for (final DisabledDuration duration : disabledDuration) { for (final BillingEvent event : subscriptionBillingEvents) { if (duration.getEnd() == null || event.getEffectiveDate().isBefore(duration.getEnd())) { if (!event.getEffectiveDate().isBefore(duration.getStart())) { result.add(event); } } else { //after the last event of the pair no need to keep checking break; } } } return result; }
private Integer computeAccountBCD(final BillingEventSet result) throws CatalogApiException { BillingEvent oldestAccountAlignedBillingEvent = null; for (final BillingEvent event : result) { if (event.getBillingAlignment() != BillingAlignment.ACCOUNT) { continue; } final BigDecimal recurringPrice = event.getRecurringPrice(event.getEffectiveDate()); final boolean hasRecurringPrice = recurringPrice != null; // Note: could be zero (BCD would still be set, by convention) final boolean hasUsage = event.getUsages() != null && !event.getUsages().isEmpty(); if (!hasRecurringPrice && !hasUsage) { // Nothing to bill, ignored for the purpose of BCD calculation continue; } if (oldestAccountAlignedBillingEvent == null || event.getEffectiveDate().compareTo(oldestAccountAlignedBillingEvent.getEffectiveDate()) < 0 || (event.getEffectiveDate().compareTo(oldestAccountAlignedBillingEvent.getEffectiveDate()) == 0 && event.getTotalOrdering().compareTo(oldestAccountAlignedBillingEvent.getTotalOrdering()) < 0)) { oldestAccountAlignedBillingEvent = event; } } if (oldestAccountAlignedBillingEvent == null) { return null; } // BCD in the account timezone final int accountBCDCandidate = oldestAccountAlignedBillingEvent.getBillCycleDayLocal(); Preconditions.checkState(accountBCDCandidate > 0, "Wrong Account BCD calculation for event: " + oldestAccountAlignedBillingEvent); return accountBCDCandidate; }
(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 LocalDate transitionStartDate = internalTenantContext.toLocalDate(billingEvent.getEffectiveDate()); if (i == billingEvents.size() - 1) { addTransitionTimesForBillingEvent(transitionStartDate, endDate, billingEvent.getBillCycleDayLocal()); } else { final BillingEvent nextBillingEvent = billingEvents.get(i + 1); final LocalDate nextEndDate = internalTenantContext.toLocalDate(nextBillingEvent.getEffectiveDate()); addTransitionTimesForBillingEvent(transitionStartDate, nextEndDate, billingEvent.getBillCycleDayLocal());
private Map<UUID, DateTime> getNextTransitionsForSubscriptions(final BillingEventSet billingEvents) { final DateTime now = clock.getUTCNow(); final Map<UUID, DateTime> result = new HashMap<UUID, DateTime>(); for (final BillingEvent evt : billingEvents) { final UUID subscriptionId = evt.getSubscription().getId(); final DateTime evtEffectiveDate = evt.getEffectiveDate(); if (evtEffectiveDate.compareTo(now) <= 0) { continue; } final DateTime nextUpcomingPerSubscriptionDate = result.get(subscriptionId); if (nextUpcomingPerSubscriptionDate == null || nextUpcomingPerSubscriptionDate.compareTo(evtEffectiveDate) > 0) { result.put(subscriptionId, evtEffectiveDate); } } return result; }
private LocalDate computeNextNotificationDate() { LocalDate result = null; final Iterator<BillingEvent> eventIt = billingEvents.iterator(); BillingEvent nextEvent = eventIt.next(); while (eventIt.hasNext()) { final BillingEvent thisEvent = nextEvent; nextEvent = eventIt.next(); final LocalDate startDate = internalTenantContext.toLocalDate(thisEvent.getEffectiveDate()); final LocalDate endDate = internalTenantContext.toLocalDate(nextEvent.getEffectiveDate()); final BillingIntervalDetail bid = new BillingIntervalDetail(startDate, endDate, targetDate, thisEvent.getBillCycleDayLocal(), usage.getBillingPeriod(), BillingMode.IN_ARREAR); final LocalDate nextBillingCycleDate = bid.getNextBillingCycleDate(); result = (result == null || result.compareTo(nextBillingCycleDate) < 0) ? nextBillingCycleDate : result; } final LocalDate startDate = internalTenantContext.toLocalDate(nextEvent.getEffectiveDate()); final BillingIntervalDetail bid = new BillingIntervalDetail(startDate, null, targetDate, nextEvent.getBillCycleDayLocal(), usage.getBillingPeriod(), BillingMode.IN_ARREAR); final LocalDate nextBillingCycleDate = bid.getNextBillingCycleDate(); result = (result == null || result.compareTo(nextBillingCycleDate) < 0) ? nextBillingCycleDate : result; return result; }
@VisibleForTesting boolean isSameDayAndSameSubscription(final InvoiceItem prevComputedFixedItem, final BillingEvent currentBillingEvent, final InternalCallContext internalCallContext) { final LocalDate curLocalEffectiveDate = internalCallContext.toLocalDate(currentBillingEvent.getEffectiveDate()); if (prevComputedFixedItem != null && /* If we have computed a previous item */ prevComputedFixedItem.getStartDate().compareTo(curLocalEffectiveDate) == 0 && /* The current billing event happens at the same date */ prevComputedFixedItem.getSubscriptionId().compareTo(currentBillingEvent.getSubscription().getId()) == 0 /* The current billing event happens for the same subscription */) { return true; } else { return false; } }
protected ContiguousIntervalConsumableUsageInArrear createContiguousIntervalConsumableInArrear(final DefaultUsage usage, final List<RawUsage> rawUsages, final LocalDate targetDate, final boolean closedInterval, UsageDetailMode detailMode, final BillingEvent... events) { final ContiguousIntervalConsumableUsageInArrear intervalConsumableInArrear = new ContiguousIntervalConsumableUsageInArrear(usage, accountId, invoiceId, rawUsages, EMPTY_EXISTING_TRACKING_IDS, targetDate, new LocalDate(events[0].getEffectiveDate()), detailMode, internalCallContext); for (final BillingEvent event : events) { intervalConsumableInArrear.addBillingEvent(event); } intervalConsumableInArrear.build(closedInterval); return intervalConsumableInArrear; }
protected ContiguousIntervalCapacityUsageInArrear createContiguousIntervalCapacityInArrear(final DefaultUsage usage, final List<RawUsage> rawUsages, final LocalDate targetDate, final boolean closedInterval, UsageDetailMode detailMode, final BillingEvent... events) { final ContiguousIntervalCapacityUsageInArrear intervalCapacityInArrear = new ContiguousIntervalCapacityUsageInArrear(usage, accountId, invoiceId, rawUsages, EMPTY_EXISTING_TRACKING_IDS, targetDate, new LocalDate(events[0].getEffectiveDate()), detailMode, internalCallContext); for (final BillingEvent event : events) { intervalCapacityInArrear.addBillingEvent(event); } intervalCapacityInArrear.build(closedInterval); return intervalCapacityInArrear; }
protected BillingEvent createMockBillingEvent(final int bcd, final DateTime effectiveDate, final BillingPeriod billingPeriod, final List<Usage> usages) { final BillingEvent result = Mockito.mock(BillingEvent.class); Mockito.when(result.getCurrency()).thenReturn(Currency.BTC); Mockito.when(result.getBillCycleDayLocal()).thenReturn(bcd); Mockito.when(result.getEffectiveDate()).thenReturn(effectiveDate); Mockito.when(result.getBillingPeriod()).thenReturn(billingPeriod); final Account account = Mockito.mock(Account.class); Mockito.when(account.getId()).thenReturn(accountId); final SubscriptionBase subscription = Mockito.mock(SubscriptionBase.class); Mockito.when(subscription.getId()).thenReturn(subscriptionId); Mockito.when(subscription.getBundleId()).thenReturn(bundleId); Mockito.when(result.getSubscription()).thenReturn(subscription); final Product product = Mockito.mock(Product.class); Mockito.when(product.getName()).thenReturn(productName); final Plan plan = Mockito.mock(Plan.class); Mockito.when(plan.getName()).thenReturn(planName); Mockito.when(plan.getProduct()).thenReturn(product); Mockito.when(result.getPlan()).thenReturn(plan); final PlanPhase phase = Mockito.mock(PlanPhase.class); Mockito.when(phase.getName()).thenReturn(phaseName); Mockito.when(result.getPlanPhase()).thenReturn(phase); Mockito.when(result.getUsages()).thenReturn(usages); return result; }
final BillingEvent event = events.next(); final LocalDate eventLocalEffectiveDate = internalCallContext.toLocalDate(event.getEffectiveDate()); if (eventLocalEffectiveDate.isAfter(targetDate)) { continue;
@Override public int compareTo(final BillingEvent e1) { if (!getSubscription().getId().equals(e1.getSubscription().getId())) { // First order by subscription return getSubscription().getId().compareTo(e1.getSubscription().getId()); } else { // subscriptions are the same if (!getEffectiveDate().equals(e1.getEffectiveDate())) { // Secondly order by date return getEffectiveDate().compareTo(e1.getEffectiveDate()); } else { // dates and subscriptions are the same return getTotalOrdering().compareTo(e1.getTotalOrdering()); } } } };
return getSubscription().getId().compareTo(e1.getSubscription().getId()); } else { // subscriptions are the same if (!getEffectiveDate().equals(e1.getEffectiveDate())) { // Secondly order by date return getEffectiveDate().compareTo(e1.getEffectiveDate()); } else { // dates and subscriptions are the same
thisEvent.getPlanPhase().getDuration().addToLocalDate(internalCallContext.toLocalDate(thisEvent.getEffectiveDate())) : null; final LocalDate startDate = internalCallContext.toLocalDate(thisEvent.getEffectiveDate()); final LocalDate endDate = (nextEvent == null) ? null : internalCallContext.toLocalDate(nextEvent.getEffectiveDate());
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 { final BigDecimal fixedPrice = thisEvent.getFixedPrice(); if (fixedPrice != null) { final FixedPriceInvoiceItem fixedPriceInvoiceItem = new FixedPriceInvoiceItem(invoiceId, accountId, thisEvent.getSubscription().getBundleId(), thisEvent.getSubscription().getId(), thisEvent.getPlan().getProduct().getName(), thisEvent.getPlan().getName(), thisEvent.getPlanPhase().getName(), roundedStartDate, fixedPrice, currency); // For debugging purposes invoiceItemGeneratorLogger.append(thisEvent, fixedPriceInvoiceItem); return fixedPriceInvoiceItem; } else { return null; } } }