killbill-aplcache
Changes
invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java 70(+32 -38)
invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java 19(+10 -9)
invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java 77(+39 -38)
Details
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
index 1370254..55106c2 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
@@ -48,12 +48,10 @@ import org.killbill.billing.invoice.tree.AccountItemTree;
import org.killbill.billing.junction.BillingEvent;
import org.killbill.billing.junction.BillingEventSet;
import org.killbill.billing.util.currency.KillBillMoney;
-import org.killbill.clock.Clock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
-import com.google.inject.Inject;
import static org.killbill.billing.invoice.generator.InvoiceDateUtils.calculateNumberOfWholeBillingPeriods;
import static org.killbill.billing.invoice.generator.InvoiceDateUtils.calculateProRationAfterLastBillingCycleDate;
@@ -63,16 +61,9 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
private static final Logger log = LoggerFactory.getLogger(FixedAndRecurringInvoiceItemGenerator.class);
- private final Clock clock;
-
- @Inject
- public FixedAndRecurringInvoiceItemGenerator(final Clock clock) {
- this.clock = clock;
- }
-
public List<InvoiceItem> generateItems(final ImmutableAccountData account, final UUID invoiceId, final BillingEventSet eventSet,
@Nullable final List<Invoice> existingInvoices, final LocalDate targetDate,
- final Currency targetCurrency, Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate,
+ final Currency targetCurrency, final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate,
final InternalCallContext internalCallContext) throws InvoiceApiException {
final AccountItemTree accountItemTree = new AccountItemTree(account.getId(), invoiceId);
if (existingInvoices != null) {
@@ -100,16 +91,12 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate,
@Nullable final List<Invoice> existingInvoices,
final InternalCallContext internalCallContext) throws InvoiceApiException {
- if (events.size() == 0) {
+ if (events.isEmpty()) {
return;
}
// Pretty-print the generated invoice items from the junction events
- final StringBuilder logStringBuilder = new StringBuilder("Proposed Invoice items for invoiceId='")
- .append(invoiceId)
- .append("', accountId='")
- .append(accountId)
- .append("'");
+ final InvoiceItemGeneratorLogger invoiceItemGeneratorLogger = new InvoiceItemGeneratorLogger(invoiceId, accountId, "recurring", log);
final Iterator<BillingEvent> eventIt = events.iterator();
BillingEvent nextEvent = eventIt.next();
@@ -119,29 +106,34 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
if (!events.getSubscriptionIdsWithAutoInvoiceOff().
contains(thisEvent.getSubscription().getId())) { // don't consider events for subscriptions that have auto_invoice_off
final BillingEvent adjustedNextEvent = (thisEvent.getSubscription().getId() == nextEvent.getSubscription().getId()) ? nextEvent : null;
- final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, thisEvent, adjustedNextEvent, targetDate, currency, logStringBuilder, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, internalCallContext);
+ final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, thisEvent, adjustedNextEvent, targetDate, currency, invoiceItemGeneratorLogger, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, internalCallContext);
proposedItems.addAll(newProposedItems);
}
}
- final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, nextEvent, null, targetDate, currency, logStringBuilder, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, internalCallContext);
+ final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, nextEvent, null, targetDate, currency, invoiceItemGeneratorLogger, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, internalCallContext);
proposedItems.addAll(newProposedItems);
- log.info(logStringBuilder.toString());
-
- return;
+ invoiceItemGeneratorLogger.logItems();
}
@VisibleForTesting
void processFixedBillingEvents(final UUID invoiceId, final UUID accountId, final BillingEventSet events, final LocalDate targetDate,
final Currency currency, final List<InvoiceItem> proposedItems, final InternalCallContext internalCallContext) throws InvoiceApiException {
+ if (events.isEmpty()) {
+ return;
+ }
+
InvoiceItem prevItem = null;
+ // Pretty-print the generated invoice items from the junction events
+ final InvoiceItemGeneratorLogger invoiceItemGeneratorLogger = new InvoiceItemGeneratorLogger(invoiceId, accountId, "fixed", log);
+
final Iterator<BillingEvent> eventIt = events.iterator();
while (eventIt.hasNext()) {
final BillingEvent thisEvent = eventIt.next();
- final InvoiceItem currentFixedPriceItem = generateFixedPriceItem(invoiceId, accountId, thisEvent, targetDate, currency, internalCallContext);
+ final InvoiceItem currentFixedPriceItem = generateFixedPriceItem(invoiceId, accountId, thisEvent, targetDate, currency, invoiceItemGeneratorLogger, internalCallContext);
if (!isSameDayAndSameSubscription(prevItem, thisEvent, internalCallContext) && prevItem != null) {
proposedItems.add(prevItem);
}
@@ -151,6 +143,8 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
if (prevItem != null) {
proposedItems.add(prevItem);
}
+
+ invoiceItemGeneratorLogger.logItems();
}
@VisibleForTesting
@@ -168,12 +162,11 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
// 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 StringBuilder logStringBuilder, final BillingMode billingMode,
+ 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
@@ -194,7 +187,7 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
final RecurringInvoiceItemDataWithNextBillingCycleDate itemDataWithNextBillingCycleDate;
try {
itemDataWithNextBillingCycleDate = generateInvoiceItemData(startDate, endDate, targetDate, billCycleDayLocal, billingPeriod, billingMode);
- } catch (InvalidDateSequenceException e) {
+ } catch (final InvalidDateSequenceException e) {
throw new InvoiceApiException(ErrorCode.INVOICE_INVALID_DATE_SEQUENCE, startDate, endDate, targetDate);
}
for (final RecurringInvoiceItemData itemDatum : itemDataWithNextBillingCycleDate.getItemData()) {
@@ -224,14 +217,9 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
}
// For debugging purposes
- logStringBuilder.append("\n")
- .append(thisEvent);
- for (final InvoiceItem item : items) {
- logStringBuilder.append("\n\t")
- .append(item);
- }
- return items;
+ invoiceItemGeneratorLogger.append(thisEvent, items);
+ return items;
} catch (final CatalogApiException e) {
throw new InvoiceApiException(e);
}
@@ -334,7 +322,7 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
for (int i = 0; i < numberOfWholeBillingPeriods; i++) {
final LocalDate servicePeriodStartDate;
- if (results.size() > 0) {
+ 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) {
@@ -365,7 +353,8 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
}
private InvoiceItem generateFixedPriceItem(final UUID invoiceId, final UUID accountId, final BillingEvent thisEvent,
- final LocalDate targetDate, final Currency currency, final InternalCallContext internalCallContext) throws InvoiceApiException {
+ 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;
@@ -373,10 +362,15 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
final BigDecimal fixedPrice = thisEvent.getFixedPrice();
if (fixedPrice != null) {
- return new FixedPriceInvoiceItem(invoiceId, accountId, thisEvent.getSubscription().getBundleId(),
- thisEvent.getSubscription().getId(),
- thisEvent.getPlan().getName(), thisEvent.getPlanPhase().getName(),
- roundedStartDate, fixedPrice, currency);
+ 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;
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceItemGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceItemGenerator.java
index d9b3090..eb2ce88 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceItemGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceItemGenerator.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -17,6 +17,7 @@
package org.killbill.billing.invoice.generator;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -32,6 +33,7 @@ import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.generator.InvoiceWithMetadata.SubscriptionFutureNotificationDates;
import org.killbill.billing.junction.BillingEventSet;
+import org.slf4j.Logger;
public abstract class InvoiceItemGenerator {
@@ -40,4 +42,60 @@ public abstract class InvoiceItemGenerator {
final Currency targetCurrency, Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate,
final InternalCallContext context) throws InvoiceApiException;
+ public static class InvoiceItemGeneratorLogger {
+
+ private final UUID invoiceId;
+ private final UUID accountId;
+ private final String type;
+ private final Logger delegate;
+
+ private StringBuilder logStringBuilder = null;
+
+ public InvoiceItemGeneratorLogger(final UUID invoiceId, final UUID accountId, final String type, final Logger delegate) {
+ this.invoiceId = invoiceId;
+ this.accountId = accountId;
+ this.type = type;
+ this.delegate = delegate;
+ }
+
+ public void append(final Object event, final Collection<InvoiceItem> items) {
+ if (items.isEmpty()) {
+ return;
+ }
+ append(event, items.toArray(new InvoiceItem[items.size()]));
+ }
+
+ public void append(final Object event, final InvoiceItem... items) {
+ if (items.length == 0) {
+ return;
+ }
+
+ getLogStringBuilder().append("\n")
+ .append(event);
+
+ for (final InvoiceItem item : items) {
+ getLogStringBuilder().append("\n\t")
+ .append(item);
+ }
+ }
+
+ public void logItems() {
+ if (logStringBuilder != null) {
+ delegate.info(getLogStringBuilder().toString());
+ }
+ }
+
+ private StringBuilder getLogStringBuilder() {
+ if (logStringBuilder == null) {
+ logStringBuilder = new StringBuilder("Proposed ").append(type)
+ .append(" items for invoiceId='")
+ .append(invoiceId)
+ .append("', accountId='")
+ .append(accountId)
+ .append("'");
+ }
+
+ return logStringBuilder;
+ }
+ }
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java
index d94b8f9..850b8a6 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java
@@ -79,6 +79,8 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
final InternalCallContext internalCallContext) throws InvoiceApiException {
final Map<UUID, List<InvoiceItem>> perSubscriptionConsumableInArrearUsageItems = extractPerSubscriptionExistingConsumableInArrearUsageItems(eventSet.getUsages(), existingInvoices);
try {
+ // 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);
@@ -118,7 +120,7 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
final SubscriptionConsumableInArrear subscriptionConsumableInArrear = new SubscriptionConsumableInArrear(account.getId(), invoiceId, curEvents, rawUsageOptimizerResult.getRawUsage(), targetDate, rawUsageOptimizerResult.getRawUsageStartDate(), internalCallContext);
final List<InvoiceItem> consumableInUsageArrearItems = perSubscriptionConsumableInArrearUsageItems.get(curSubscriptionId);
- final SubscriptionConsumableInArrearItemsAndNextNotificationDate subscriptionResult = subscriptionConsumableInArrear.computeMissingUsageInvoiceItems(consumableInUsageArrearItems != null ? consumableInUsageArrearItems : ImmutableList.<InvoiceItem>of());
+ final SubscriptionConsumableInArrearItemsAndNextNotificationDate subscriptionResult = subscriptionConsumableInArrear.computeMissingUsageInvoiceItems(consumableInUsageArrearItems != null ? consumableInUsageArrearItems : ImmutableList.<InvoiceItem>of(), invoiceItemGeneratorLogger);
final List<InvoiceItem> newInArrearUsageItems = subscriptionResult.getInvoiceItems();
items.addAll(newInArrearUsageItems);
updatePerSubscriptionNextNotificationUsageDate(curSubscriptionId, subscriptionResult.getPerUsageNotificationDates(), BillingMode.IN_ARREAR, perSubscriptionFutureNotificationDates);
@@ -131,23 +133,23 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
final SubscriptionConsumableInArrear subscriptionConsumableInArrear = new SubscriptionConsumableInArrear(account.getId(), invoiceId, curEvents, rawUsageOptimizerResult.getRawUsage(), targetDate, rawUsageOptimizerResult.getRawUsageStartDate(), internalCallContext);
final List<InvoiceItem> consumableInUsageArrearItems = perSubscriptionConsumableInArrearUsageItems.get(curSubscriptionId);
- final SubscriptionConsumableInArrearItemsAndNextNotificationDate subscriptionResult = subscriptionConsumableInArrear.computeMissingUsageInvoiceItems(consumableInUsageArrearItems != null ? consumableInUsageArrearItems : ImmutableList.<InvoiceItem>of());
+ final SubscriptionConsumableInArrearItemsAndNextNotificationDate subscriptionResult = subscriptionConsumableInArrear.computeMissingUsageInvoiceItems(consumableInUsageArrearItems != null ? consumableInUsageArrearItems : ImmutableList.<InvoiceItem>of(), invoiceItemGeneratorLogger);
final List<InvoiceItem> newInArrearUsageItems = subscriptionResult.getInvoiceItems();
items.addAll(newInArrearUsageItems);
updatePerSubscriptionNextNotificationUsageDate(curSubscriptionId, subscriptionResult.getPerUsageNotificationDates(), BillingMode.IN_ARREAR, perSubscriptionFutureNotificationDates);
}
- return items;
- } catch (CatalogApiException e) {
+ invoiceItemGeneratorLogger.logItems();
+
+ return items;
+ } catch (final CatalogApiException e) {
throw new InvoiceApiException(e);
}
}
private LocalDate getMinBillingEventDate(final BillingEventSet eventSet, final InternalCallContext internalCallContext) {
DateTime minDate = null;
- final Iterator<BillingEvent> events = eventSet.iterator();
- while (events.hasNext()) {
- final BillingEvent cur = events.next();
+ for (final BillingEvent cur : eventSet) {
if (minDate == null || minDate.compareTo(cur.getEffectiveDate()) > 0) {
minDate = cur.getEffectiveDate();
}
@@ -171,7 +173,6 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
}
private Map<UUID, List<InvoiceItem>> extractPerSubscriptionExistingConsumableInArrearUsageItems(final Map<String, Usage> knownUsage, @Nullable final List<Invoice> existingInvoices) {
-
if (existingInvoices == null || existingInvoices.isEmpty()) {
return ImmutableMap.of();
}
@@ -194,7 +195,7 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
}
}));
- for (InvoiceItem cur : usageConsumableInArrearItems) {
+ for (final InvoiceItem cur : usageConsumableInArrearItems) {
List<InvoiceItem> perSubscriptionUsageItems = result.get(cur.getSubscriptionId());
if (perSubscriptionUsageItems == null) {
perSubscriptionUsageItems = new LinkedList<InvoiceItem>();
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
index 97cb002..dff9142 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -460,14 +460,14 @@ public class InvoiceDispatcher {
private void logInvoiceWithItems(final ImmutableAccountData account, final Invoice invoice, final LocalDate targetDate, final Set<UUID> adjustedUniqueOtherInvoiceId, final boolean isRealInvoiceWithItems) {
final StringBuilder tmp = new StringBuilder();
if (isRealInvoiceWithItems) {
- tmp.append(String.format("Generated invoiceId='%s', numberOfItems='%d', accountId='%s', targetDate='%s':\n", invoice.getId(), invoice.getNumberOfItems(), account.getId(), targetDate));
+ tmp.append(String.format("Generated invoiceId='%s', numberOfItems='%d', accountId='%s', targetDate='%s':", invoice.getId(), invoice.getNumberOfItems(), account.getId(), targetDate));
} else {
final String adjustedInvoices = JOINER_COMMA.join(adjustedUniqueOtherInvoiceId.toArray(new UUID[adjustedUniqueOtherInvoiceId.size()]));
tmp.append(String.format("Adjusting existing invoiceId='%s', numberOfItems='%d', accountId='%s', targetDate='%s':\n",
adjustedInvoices, invoice.getNumberOfItems(), account.getId(), targetDate));
}
for (final InvoiceItem item : invoice.getInvoiceItems()) {
- tmp.append(String.format("\t item = %s\n", item));
+ tmp.append(String.format("\n\t item = %s", item));
}
log.info(tmp.toString());
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java
index 3e9c5e8..65f5449 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java
@@ -105,7 +105,6 @@ public class ContiguousIntervalConsumableInArrear {
*
* @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) {
@@ -143,30 +142,10 @@ public class ContiguousIntervalConsumableInArrear {
return this;
}
- public class ConsumableInArrearItemsAndNextNotificationDate {
- private final List<InvoiceItem> invoiceItems;
- private final LocalDate nextNotificationDate;
-
- public ConsumableInArrearItemsAndNextNotificationDate(final List<InvoiceItem> invoiceItems, final LocalDate nextNotificationDate) {
- this.invoiceItems = invoiceItems;
- this.nextNotificationDate = nextNotificationDate;
- }
-
- public List<InvoiceItem> getInvoiceItems() {
- return invoiceItems;
- }
-
- public LocalDate getNextNotificationDate() {
- return nextNotificationDate;
- }
- }
-
-
/**
* Compute the missing usage invoice items based on what should be billed and what has been billed ($ amount comparison).
*
* @param existingUsage existing on disk usage items for the subscription
- * @return
* @throws CatalogApiException
*/
public ConsumableInArrearItemsAndNextNotificationDate computeMissingItemsAndNextNotificationDate(final List<InvoiceItem> existingUsage) throws CatalogApiException {
@@ -183,10 +162,10 @@ public class ContiguousIntervalConsumableInArrear {
// We start by generating 'marker' USAGE items with $0 that will allow to correctly insert the next notification for when there is no USAGE to bill.
// Those will be removed by the invoicing code later so as to not end up with superfluous $0 items
LocalDate prevDate = null;
- for (LocalDate curDate : transitionTimes) {
+ for (final LocalDate curDate : transitionTimes) {
if (prevDate != null) {
- InvoiceItem item = new UsageInvoiceItem(invoiceId, accountId, getBundleId(), getSubscriptionId(), getPlanName(),
- getPhaseName(), usage.getName(), prevDate, curDate, BigDecimal.ZERO, getCurrency());
+ final InvoiceItem item = new UsageInvoiceItem(invoiceId, accountId, getBundleId(), getSubscriptionId(), getPlanName(),
+ getPhaseName(), usage.getName(), prevDate, curDate, BigDecimal.ZERO, getCurrency());
result.add(item);
}
prevDate = curDate;
@@ -213,8 +192,8 @@ public class ContiguousIntervalConsumableInArrear {
if (!billedItems.iterator().hasNext() || billedUsage.compareTo(toBeBilledUsage) < 0) {
final BigDecimal amountToBill = toBeBilledUsage.subtract(billedUsage);
if (amountToBill.compareTo(BigDecimal.ZERO) > 0) {
- InvoiceItem item = new UsageInvoiceItem(invoiceId, accountId, getBundleId(), getSubscriptionId(), getPlanName(),
- getPhaseName(), usage.getName(), ru.getStart(), ru.getEnd(), amountToBill, getCurrency());
+ final InvoiceItem item = new UsageInvoiceItem(invoiceId, accountId, getBundleId(), getSubscriptionId(), getPlanName(),
+ getPhaseName(), usage.getName(), ru.getStart(), ru.getEnd(), amountToBill, getCurrency());
result.add(item);
}
}
@@ -246,10 +225,8 @@ public class ContiguousIntervalConsumableInArrear {
return result;
}
-
@VisibleForTesting
List<RolledUpUsage> getRolledUpUsage() {
-
final Iterator<RawUsage> rawUsageIterator = rawSubscriptionUsage.iterator();
if (!rawUsageIterator.hasNext()) {
return ImmutableList.of();
@@ -281,7 +258,7 @@ public class ContiguousIntervalConsumableInArrear {
// matching RolledUpUsage for that interval, and we'll detect that in the 'computeMissingItems' logic
//
LocalDate prevDate = null;
- for (LocalDate curDate : transitionTimes) {
+ for (final LocalDate curDate : transitionTimes) {
if (prevDate != null) {
@@ -291,8 +268,8 @@ public class ContiguousIntervalConsumableInArrear {
// Start consuming prevRawUsage element if it exists and falls into the range
if (prevRawUsage != null) {
if (prevRawUsage.getDate().compareTo(prevDate) >= 0 && prevRawUsage.getDate().compareTo(curDate) < 0) {
- Long currentAmount = perRangeUnitToAmount.get(prevRawUsage.getUnitType());
- Long updatedAmount = (currentAmount != null) ? currentAmount + prevRawUsage.getAmount() : prevRawUsage.getAmount();
+ final Long currentAmount = perRangeUnitToAmount.get(prevRawUsage.getUnitType());
+ final Long updatedAmount = (currentAmount != null) ? currentAmount + prevRawUsage.getAmount() : prevRawUsage.getAmount();
perRangeUnitToAmount.put(prevRawUsage.getUnitType(), updatedAmount);
prevRawUsage = null;
}
@@ -312,8 +289,8 @@ public class ContiguousIntervalConsumableInArrear {
break;
}
- Long currentAmount = perRangeUnitToAmount.get(curRawUsage.getUnitType());
- Long updatedAmount = (currentAmount != null) ? currentAmount + curRawUsage.getAmount() : curRawUsage.getAmount();
+ final Long currentAmount = perRangeUnitToAmount.get(curRawUsage.getUnitType());
+ final Long updatedAmount = (currentAmount != null) ? currentAmount + curRawUsage.getAmount() : curRawUsage.getAmount();
perRangeUnitToAmount.put(curRawUsage.getUnitType(), updatedAmount);
}
}
@@ -346,7 +323,7 @@ public class ContiguousIntervalConsumableInArrear {
BigDecimal result = BigDecimal.ZERO;
final List<TieredBlock> tieredBlocks = getConsumableInArrearTieredBlocks(usage, unitType);
int remainingUnits = nbUnits.intValue();
- for (TieredBlock tieredBlock : tieredBlocks) {
+ for (final TieredBlock tieredBlock : tieredBlocks) {
final int blockTierSize = tieredBlock.getSize().intValue();
final int tmp = remainingUnits / blockTierSize + (remainingUnits % blockTierSize == 0 ? 0 : 1);
@@ -372,7 +349,7 @@ public class ContiguousIntervalConsumableInArrear {
Preconditions.checkState(isBuilt.get());
BigDecimal billedAmount = BigDecimal.ZERO;
- for (InvoiceItem ii : filteredUsageForInterval) {
+ for (final InvoiceItem ii : filteredUsageForInterval) {
billedAmount = billedAmount.add(ii.getAmount());
}
// Return the billed $ amount (not the # of units)
@@ -415,7 +392,6 @@ public class ContiguousIntervalConsumableInArrear {
return billingEvents.get(0).getBillCycleDayLocal();
}
-
public UUID getBundleId() {
return billingEvents.get(0).getSubscription().getBundleId();
}
@@ -437,8 +413,33 @@ public class ContiguousIntervalConsumableInArrear {
return billingEvents.get(0).getCurrency();
}
- public DateTimeZone getAccountTimeZone() {
- return billingEvents.get(0).getTimeZone();
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("ContiguousIntervalConsumableInArrear{");
+ sb.append("transitionTimes=").append(transitionTimes);
+ sb.append(", billingEvents=").append(billingEvents);
+ sb.append(", rawSubscriptionUsage=").append(rawSubscriptionUsage);
+ sb.append(", rawUsageStartDate=").append(rawUsageStartDate);
+ sb.append('}');
+ return sb.toString();
}
+ public class ConsumableInArrearItemsAndNextNotificationDate {
+
+ private final List<InvoiceItem> invoiceItems;
+ private final LocalDate nextNotificationDate;
+
+ public ConsumableInArrearItemsAndNextNotificationDate(final List<InvoiceItem> invoiceItems, final LocalDate nextNotificationDate) {
+ this.invoiceItems = invoiceItems;
+ this.nextNotificationDate = nextNotificationDate;
+ }
+
+ public List<InvoiceItem> getInvoiceItems() {
+ return invoiceItems;
+ }
+
+ public LocalDate getNextNotificationDate() {
+ return nextNotificationDate;
+ }
+ }
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/RawUsageOptimizer.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/RawUsageOptimizer.java
index 854a91f..1d3efeb 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/RawUsageOptimizer.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/RawUsageOptimizer.java
@@ -68,15 +68,14 @@ public class RawUsageOptimizer {
public RawUsageOptimizerResult getConsumableInArrearUsage(final LocalDate firstEventStartDate, final LocalDate targetDate, final Iterable<InvoiceItem> existingUsageItems, final Map<String, Usage> knownUsage, final InternalCallContext internalCallContext) {
final LocalDate targetStartDate = config.getMaxRawUsagePreviousPeriod(internalCallContext) > 0 ? getOptimizedRawUsageStartDate(firstEventStartDate, targetDate, existingUsageItems, knownUsage, internalCallContext) : firstEventStartDate;
- log.info("ConsumableInArrear accountRecordId='{}', rawUsageStartDate='{}', firstEventStartDate='{}'",
- internalCallContext.getAccountRecordId(), targetStartDate, firstEventStartDate);
+ log.debug("ConsumableInArrear accountRecordId='{}', rawUsageStartDate='{}', firstEventStartDate='{}'",
+ internalCallContext.getAccountRecordId(), targetStartDate, firstEventStartDate);
final List<RawUsage> rawUsageData = usageApi.getRawUsageForAccount(targetStartDate, targetDate, internalCallContext);
- return new RawUsageOptimizerResult(firstEventStartDate, targetStartDate, rawUsageData);
+ return new RawUsageOptimizerResult(targetStartDate, rawUsageData);
}
@VisibleForTesting
LocalDate getOptimizedRawUsageStartDate(final LocalDate firstEventStartDate, final LocalDate targetDate, final Iterable<InvoiceItem> existingUsageItems, final Map<String, Usage> knownUsage, final InternalCallContext internalCallContext) {
-
if (!existingUsageItems.iterator().hasNext()) {
return firstEventStartDate;
@@ -99,7 +98,7 @@ public class RawUsageOptimizer {
//
final LocalDate[] perBillingPeriodMostRecentConsumableInArrearItemEndDate = new LocalDate[BillingPeriod.values().length - 1]; // Exclude the NO_BILLING_PERIOD
int idx = 0;
- for (BillingPeriod bp : BillingPeriod.values()) {
+ for (final BillingPeriod bp : BillingPeriod.values()) {
if (bp != BillingPeriod.NO_BILLING_PERIOD) {
final LocalDate makerDateThanCannotBeChosenAsTheMinOfAllDates = InvoiceDateUtils.advanceByNPeriods(targetDate, bp, config.getMaxRawUsagePreviousPeriod(internalCallContext));
perBillingPeriodMostRecentConsumableInArrearItemEndDate[idx++] = (knownUsageBillingPeriod.contains(bp)) ? null : makerDateThanCannotBeChosenAsTheMinOfAllDates;
@@ -124,7 +123,7 @@ public class RawUsageOptimizer {
// Extract the min from all the dates
LocalDate targetStartDate = null;
idx = 0;
- for (BillingPeriod bp : BillingPeriod.values()) {
+ for (final BillingPeriod bp : BillingPeriod.values()) {
if (bp != BillingPeriod.NO_BILLING_PERIOD) {
final LocalDate tmp = perBillingPeriodMostRecentConsumableInArrearItemEndDate[idx];
final LocalDate targetBillingPeriodDate = tmp != null ? InvoiceDateUtils.recedeByNPeriods(tmp, bp, config.getMaxRawUsagePreviousPeriod(internalCallContext)) : null;
@@ -152,20 +151,14 @@ public class RawUsageOptimizer {
public static class RawUsageOptimizerResult {
- private final LocalDate firstEventStartDate;
private final LocalDate rawUsageStartDate;
private final List<RawUsage> rawUsage;
- public RawUsageOptimizerResult(final LocalDate firstEventStartDate, final LocalDate rawUsageStartDate, final List<RawUsage> rawUsage) {
- this.firstEventStartDate = firstEventStartDate;
+ public RawUsageOptimizerResult(final LocalDate rawUsageStartDate, final List<RawUsage> rawUsage) {
this.rawUsageStartDate = rawUsageStartDate;
this.rawUsage = rawUsage;
}
- public LocalDate getFirstEventStartDate() {
- return firstEventStartDate;
- }
-
public LocalDate getRawUsageStartDate() {
return rawUsageStartDate;
}
@@ -174,5 +167,4 @@ public class RawUsageOptimizer {
return rawUsage;
}
}
-
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java
index 28b6aca..2f93a43 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java
@@ -34,6 +34,7 @@ import org.killbill.billing.catalog.api.CatalogApiException;
import org.killbill.billing.catalog.api.Usage;
import org.killbill.billing.catalog.api.UsageType;
import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.invoice.generator.InvoiceItemGenerator.InvoiceItemGeneratorLogger;
import org.killbill.billing.invoice.usage.ContiguousIntervalConsumableInArrear.ConsumableInArrearItemsAndNextNotificationDate;
import org.killbill.billing.junction.BillingEvent;
import org.killbill.billing.usage.RawUsage;
@@ -101,36 +102,35 @@ public class SubscriptionConsumableInArrear {
}));
}
-
/**
* Based on billing events, (@code existingUsage} and targetDate, figure out what remains to be billed.
*
* @param existingUsage the existing on disk usage items.
- * @return
* @throws CatalogApiException
*/
- public SubscriptionConsumableInArrearItemsAndNextNotificationDate computeMissingUsageInvoiceItems(final List<InvoiceItem> existingUsage) throws CatalogApiException {
-
+ public SubscriptionConsumableInArrearItemsAndNextNotificationDate computeMissingUsageInvoiceItems(final List<InvoiceItem> existingUsage, final InvoiceItemGeneratorLogger invoiceItemGeneratorLogger) throws CatalogApiException {
final SubscriptionConsumableInArrearItemsAndNextNotificationDate result = new SubscriptionConsumableInArrearItemsAndNextNotificationDate();
final List<ContiguousIntervalConsumableInArrear> billingEventTransitionTimePeriods = computeInArrearUsageInterval();
- for (ContiguousIntervalConsumableInArrear usageInterval : billingEventTransitionTimePeriods) {
- result.addConsumableInArrearItemsAndNextNotificationDate(usageInterval.getUsage().getName(), usageInterval.computeMissingItemsAndNextNotificationDate(existingUsage));
+ for (final ContiguousIntervalConsumableInArrear usageInterval : billingEventTransitionTimePeriods) {
+ final ConsumableInArrearItemsAndNextNotificationDate newItemsAndDate = usageInterval.computeMissingItemsAndNextNotificationDate(existingUsage);
+
+ // For debugging purposes
+ invoiceItemGeneratorLogger.append(usageInterval, newItemsAndDate.getInvoiceItems());
+
+ result.addConsumableInArrearItemsAndNextNotificationDate(usageInterval.getUsage().getName(), newItemsAndDate);
}
return result;
}
-
-
@VisibleForTesting
List<ContiguousIntervalConsumableInArrear> computeInArrearUsageInterval() {
-
final List<ContiguousIntervalConsumableInArrear> usageIntervals = Lists.newLinkedList();
final Map<String, ContiguousIntervalConsumableInArrear> inFlightInArrearUsageIntervals = new HashMap<String, ContiguousIntervalConsumableInArrear>();
final Set<String> allSeenUsage = new HashSet<String>();
- for (BillingEvent event : subscriptionBillingEvents) {
+ for (final BillingEvent event : subscriptionBillingEvents) {
// Extract all in arrear /consumable usage section for that billing event.
final List<Usage> usages = findConsumableInArrearUsages(event);
@@ -144,7 +144,7 @@ public class SubscriptionConsumableInArrear {
// All inflight usage interval are candidates to be closed unless we see that current billing event referencing the same usage section.
final Set<String> toBeClosed = new HashSet<String>(allSeenUsage);
- for (Usage usage : usages) {
+ for (final Usage usage : usages) {
// Add inflight usage interval if non existent
ContiguousIntervalConsumableInArrear existingInterval = inFlightInArrearUsageIntervals.get(usage.getName());
@@ -159,7 +159,7 @@ public class SubscriptionConsumableInArrear {
}
// Build the usage interval that are no longer referenced
- for (String usageName : toBeClosed) {
+ for (final String usageName : toBeClosed) {
final ContiguousIntervalConsumableInArrear interval = inFlightInArrearUsageIntervals.remove(usageName);
if (interval != null) {
interval.addBillingEvent(event);
@@ -167,20 +167,20 @@ public class SubscriptionConsumableInArrear {
}
}
}
- for (String usageName : inFlightInArrearUsageIntervals.keySet()) {
+ for (final String usageName : inFlightInArrearUsageIntervals.keySet()) {
usageIntervals.add(inFlightInArrearUsageIntervals.get(usageName).build(false));
}
inFlightInArrearUsageIntervals.clear();
return usageIntervals;
}
- List<Usage> findConsumableInArrearUsages(final BillingEvent event) {
- if (event.getUsages().size() == 0) {
+ private List<Usage> findConsumableInArrearUsages(final BillingEvent event) {
+ if (event.getUsages().isEmpty()) {
return Collections.emptyList();
}
final List<Usage> result = Lists.newArrayList();
- for (Usage usage : event.getUsages()) {
+ for (final Usage usage : event.getUsages()) {
if (usage.getUsageType() != UsageType.CONSUMABLE ||
usage.getBillingMode() != BillingMode.IN_ARREAR) {
continue;
@@ -191,6 +191,7 @@ public class SubscriptionConsumableInArrear {
}
public class SubscriptionConsumableInArrearItemsAndNextNotificationDate {
+
private List<InvoiceItem> invoiceItems;
private Map<String, LocalDate> perUsageNotificationDates;
@@ -224,5 +225,4 @@ public class SubscriptionConsumableInArrear {
return perUsageNotificationDates != null ? perUsageNotificationDates : ImmutableMap.<String, LocalDate>of();
}
}
-
}
diff --git a/usage/src/main/java/org/killbill/billing/usage/api/svcs/DefaultRawUsage.java b/usage/src/main/java/org/killbill/billing/usage/api/svcs/DefaultRawUsage.java
index 7ce8718..f7a50f0 100644
--- a/usage/src/main/java/org/killbill/billing/usage/api/svcs/DefaultRawUsage.java
+++ b/usage/src/main/java/org/killbill/billing/usage/api/svcs/DefaultRawUsage.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -55,4 +55,15 @@ public class DefaultRawUsage implements RawUsage {
public Long getAmount() {
return amount;
}
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("DefaultRawUsage{");
+ sb.append("subscriptionId=").append(subscriptionId);
+ sb.append(", recordDate=").append(recordDate);
+ sb.append(", unitType='").append(unitType).append('\'');
+ sb.append(", amount=").append(amount);
+ sb.append('}');
+ return sb.toString();
+ }
}