killbill-memoizeit
Changes
invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java 96(+72 -24)
Details
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java
index af96f5d..070348d 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java
@@ -44,10 +44,11 @@ import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.generator.InvoiceWithMetadata.SubscriptionFutureNotificationDates;
import org.killbill.billing.invoice.model.BillingModeGenerator;
+import org.killbill.billing.invoice.model.DefaultBillingModeGenerator;
import org.killbill.billing.invoice.model.DefaultInvoice;
import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
-import org.killbill.billing.invoice.model.DefaultBillingModeGenerator;
import org.killbill.billing.invoice.model.InvalidDateSequenceException;
import org.killbill.billing.invoice.model.RecurringInvoiceItem;
import org.killbill.billing.invoice.model.RecurringInvoiceItemData;
@@ -92,12 +93,12 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
* adjusts target date to the maximum invoice target date, if future invoices exist
*/
@Override
- public Invoice generateInvoice(final Account account, @Nullable final BillingEventSet events,
- @Nullable final List<Invoice> existingInvoices,
- final LocalDate targetDate,
- final Currency targetCurrency, final InternalCallContext context) throws InvoiceApiException {
+ public InvoiceWithMetadata generateInvoice(final Account account, @Nullable final BillingEventSet events,
+ @Nullable final List<Invoice> existingInvoices,
+ final LocalDate targetDate,
+ final Currency targetCurrency, final InternalCallContext context) throws InvoiceApiException {
if ((events == null) || (events.size() == 0) || events.isAccountAutoInvoiceOff()) {
- return null;
+ return new InvoiceWithMetadata(null, ImmutableMap.<UUID, SubscriptionFutureNotificationDates>of());
}
validateTargetDate(targetDate);
@@ -105,21 +106,25 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
final Invoice invoice = new DefaultInvoice(account.getId(), new LocalDate(clock.getUTCNow(), account.getTimeZone()), adjustedTargetDate, targetCurrency);
final UUID invoiceId = invoice.getId();
+ final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates = new HashMap<UUID, SubscriptionFutureNotificationDates>();
- final List<InvoiceItem> inAdvanceItems = generateFixedAndRecurringInvoiceItems(account.getId(), invoiceId, events, existingInvoices, adjustedTargetDate, targetCurrency);
- invoice.addInvoiceItems(inAdvanceItems);
+ final List<InvoiceItem> fixedAndRecurringItems = generateFixedAndRecurringInvoiceItems(account.getId(), invoiceId, events, existingInvoices, adjustedTargetDate, targetCurrency, perSubscriptionFutureNotificationDates);
+ invoice.addInvoiceItems(fixedAndRecurringItems);
- final List<InvoiceItem> usageItems = generateUsageConsumableInArrearItems(account, invoiceId, events, existingInvoices, targetDate, context);
+ final List<InvoiceItem> usageItems = generateUsageConsumableInArrearItems(account, invoiceId, events, existingInvoices, targetDate, perSubscriptionFutureNotificationDates, context);
invoice.addInvoiceItems(usageItems);
- return invoice.getInvoiceItems().size() != 0 ? invoice : null;
+ return new InvoiceWithMetadata(invoice.getInvoiceItems().isEmpty() ? null : invoice, perSubscriptionFutureNotificationDates);
}
private List<InvoiceItem> generateUsageConsumableInArrearItems(final Account account,
final UUID invoiceId, final BillingEventSet eventSet,
@Nullable final List<Invoice> existingInvoices, final LocalDate targetDate,
+ final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates,
final InternalCallContext internalCallContext) throws InvoiceApiException {
+ final Map<String, LocalDate> perSubscriptionUsage = new HashMap<String, LocalDate>();
+
final Map<UUID, List<InvoiceItem>> perSubscriptionConsumableInArrearUsageItems = extractPerSubscriptionExistingConsumableInArrearUsageItems(eventSet.getUsages(), existingInvoices);
try {
final List<InvoiceItem> items = Lists.newArrayList();
@@ -157,7 +162,11 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
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()));
+
+ final List<InvoiceItem> newProposedItems = subscriptionConsumableInArrear.computeMissingUsageInvoiceItems(consumableInUsageArrearItems != null ? consumableInUsageArrearItems : ImmutableList.<InvoiceItem>of());
+ items.addAll(newProposedItems);
+ updatePerSubscriptionNextNotificationUsageDate(curSubscriptionId, newProposedItems, perSubscriptionFutureNotificationDates);
+
curEvents = Lists.newArrayList();
}
curSubscriptionId = subscriptionId;
@@ -166,7 +175,10 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
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()));
+
+ final List<InvoiceItem> newProposedItems = subscriptionConsumableInArrear.computeMissingUsageInvoiceItems(consumableInUsageArrearItems != null ? consumableInUsageArrearItems : ImmutableList.<InvoiceItem>of());
+ items.addAll(newProposedItems);
+ updatePerSubscriptionNextNotificationUsageDate(curSubscriptionId, newProposedItems, perSubscriptionFutureNotificationDates);
}
return items;
@@ -175,6 +187,19 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
}
}
+
+ private void updatePerSubscriptionNextNotificationUsageDate(final UUID subscriptionId, final List<InvoiceItem> newProposedItems, Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates) {
+ for (final InvoiceItem item : newProposedItems) {
+ SubscriptionFutureNotificationDates subscriptionFutureNotificationDates = perSubscriptionFutureNotificationDates.get(subscriptionId);
+ if (subscriptionFutureNotificationDates == null) {
+ subscriptionFutureNotificationDates = new SubscriptionFutureNotificationDates();
+ perSubscriptionFutureNotificationDates.put(subscriptionId, subscriptionFutureNotificationDates);
+ }
+ subscriptionFutureNotificationDates.updateNextUsageDateIfRequired(item.getUsageName(), item.getEndDate());
+ }
+ }
+
+
private Map<UUID, List<InvoiceItem>> extractPerSubscriptionExistingConsumableInArrearUsageItems(final Map<String, Usage> knownUsage, @Nullable final List<Invoice> existingInvoices) {
if (existingInvoices == null || existingInvoices.isEmpty()) {
@@ -212,7 +237,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
private List<InvoiceItem> generateFixedAndRecurringInvoiceItems(final UUID accountId, final UUID invoiceId, final BillingEventSet eventSet,
@Nullable final List<Invoice> existingInvoices, final LocalDate targetDate,
- final Currency targetCurrency) throws InvoiceApiException {
+ final Currency targetCurrency, Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate) throws InvoiceApiException {
final AccountItemTree accountItemTree = new AccountItemTree(accountId, invoiceId);
if (existingInvoices != null) {
for (final Invoice invoice : existingInvoices) {
@@ -228,8 +253,8 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
// Generate list of proposed invoice items based on billing events from junction-- proposed items are ALL items since beginning of time
final List<InvoiceItem> proposedItems = new ArrayList<InvoiceItem>();
- generateRecurringInvoiceItems(invoiceId, accountId, eventSet, targetDate, targetCurrency, proposedItems);
- processFixedPriceEvents(invoiceId, accountId, eventSet, targetDate, targetCurrency, proposedItems);
+ processRecurringBillingEvents(invoiceId, accountId, eventSet, targetDate, targetCurrency, proposedItems, perSubscriptionFutureNotificationDate);
+ processFixedBillingEvents(invoiceId, accountId, eventSet, targetDate, targetCurrency, proposedItems);
accountItemTree.mergeWithProposedItems(proposedItems);
return accountItemTree.getResultingItemList();
@@ -258,8 +283,10 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
return maxDate;
}
- private List<InvoiceItem> generateRecurringInvoiceItems(final UUID invoiceId, final UUID accountId, final BillingEventSet events,
- final LocalDate targetDate, final Currency currency, final List<InvoiceItem> proposedItems) throws InvoiceApiException {
+ private List<InvoiceItem> processRecurringBillingEvents(final UUID invoiceId, final UUID accountId, final BillingEventSet events,
+ final LocalDate targetDate, final Currency currency, final List<InvoiceItem> proposedItems,
+ Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate) throws InvoiceApiException {
+
if (events.size() == 0) {
return proposedItems;
}
@@ -278,17 +305,39 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
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;
- proposedItems.addAll(processRecurringEvents(invoiceId, accountId, thisEvent, adjustedNextEvent, targetDate, currency, logStringBuilder, events.getRecurringBillingMode()));
+ final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, thisEvent, adjustedNextEvent, targetDate, currency, logStringBuilder, events.getRecurringBillingMode());
+ proposedItems.addAll(newProposedItems);
+ updatePerSubscriptionNextNotificationDate(thisEvent.getSubscription().getId(), newProposedItems, perSubscriptionFutureNotificationDate);
}
}
- proposedItems.addAll(processRecurringEvents(invoiceId, accountId, nextEvent, null, targetDate, currency, logStringBuilder, events.getRecurringBillingMode()));
+ final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, nextEvent, null, targetDate, currency, logStringBuilder, events.getRecurringBillingMode());
+ proposedItems.addAll(newProposedItems);
+ updatePerSubscriptionNextNotificationDate(nextEvent.getSubscription().getId(), newProposedItems, perSubscriptionFutureNotificationDate);
log.info(logStringBuilder.toString());
return proposedItems;
}
- private List<InvoiceItem> processFixedPriceEvents(final UUID invoiceId, final UUID accountId, final BillingEventSet events, final LocalDate targetDate, final Currency currency, final List<InvoiceItem> proposedItems) {
+ private void updatePerSubscriptionNextNotificationDate(final UUID subscriptionId, final List<InvoiceItem> newProposedItems, Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates) {
+ for (final InvoiceItem item : newProposedItems) {
+ if ((item.getEndDate() != null) &&
+ (item.getAmount() == null ||
+ item.getAmount().compareTo(BigDecimal.ZERO) >= 0)) {
+ SubscriptionFutureNotificationDates subscriptionFutureNotificationDates = perSubscriptionFutureNotificationDates.get(subscriptionId);
+ if (subscriptionFutureNotificationDates == null) {
+ subscriptionFutureNotificationDates = new SubscriptionFutureNotificationDates();
+ perSubscriptionFutureNotificationDates.put(subscriptionId, subscriptionFutureNotificationDates);
+ }
+ subscriptionFutureNotificationDates.updateNextRecurringDateIfRequired(item.getEndDate());
+ }
+ }
+ }
+
+
+
+
+ private List<InvoiceItem> processFixedBillingEvents(final UUID invoiceId, final UUID accountId, final BillingEventSet events, final LocalDate targetDate, final Currency currency, final List<InvoiceItem> proposedItems) {
final Iterator<BillingEvent> eventIt = events.iterator();
while (eventIt.hasNext()) {
final BillingEvent thisEvent = eventIt.next();
@@ -301,11 +350,10 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
return proposedItems;
}
-
// 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> processRecurringEvents(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) throws InvoiceApiException {
+ 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) throws InvoiceApiException {
final List<InvoiceItem> items = new ArrayList<InvoiceItem>();
// Handle recurring items
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceGenerator.java
index 8023ee9..3ff29a6 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceGenerator.java
@@ -32,6 +32,6 @@ import org.killbill.billing.junction.BillingEventSet;
public interface InvoiceGenerator {
- public Invoice generateInvoice(Account account, @Nullable BillingEventSet events, @Nullable List<Invoice> existingInvoices,
+ public InvoiceWithMetadata generateInvoice(Account account, @Nullable BillingEventSet events, @Nullable List<Invoice> existingInvoices,
LocalDate targetDate, Currency targetCurrency, final InternalCallContext context) throws InvoiceApiException;
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceWithMetadata.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceWithMetadata.java
new file mode 100644
index 0000000..a319245
--- /dev/null
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceWithMetadata.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 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
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.invoice.generator;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.LocalDate;
+import org.killbill.billing.invoice.api.Invoice;
+
+public class InvoiceWithMetadata {
+
+ private final Invoice invoice;
+ private final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates;
+
+ public static class SubscriptionFutureNotificationDates {
+
+ private LocalDate nextRecurringDate;
+ private Map<String, LocalDate> nextUsageDates;
+
+ public SubscriptionFutureNotificationDates() {
+ this.nextRecurringDate = null;
+ this.nextUsageDates = null;
+ }
+
+ public void updateNextRecurringDateIfRequired(final LocalDate nextRecurringDateCandidate) {
+ nextRecurringDate = getMaxDate(nextRecurringDate, nextRecurringDateCandidate);
+ }
+
+ public void updateNextUsageDateIfRequired(final String usageName, final LocalDate nextUsageDateCandidate) {
+ if (nextUsageDates == null) {
+ nextUsageDates = new HashMap<String, LocalDate>();
+ }
+ final LocalDate nextUsageDate = getMaxDate(nextUsageDates.get(usageName), nextUsageDateCandidate);
+ nextUsageDates.put(usageName, nextUsageDate);
+ }
+
+ public LocalDate getNextRecurringDate() {
+ return nextRecurringDate;
+ }
+
+ public Map<String, LocalDate> getNextUsageDates() {
+ return nextUsageDates;
+ }
+
+ private static LocalDate getMaxDate(@Nullable final LocalDate existingDate, final LocalDate nextDateCandidate) {
+ if (existingDate == null) {
+ return nextDateCandidate;
+ } else {
+ return nextDateCandidate.compareTo(existingDate) > 0 ? nextDateCandidate : existingDate;
+ }
+ }
+
+ }
+
+ public InvoiceWithMetadata(final Invoice invoice, final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates) {
+ this.invoice = invoice;
+ this.perSubscriptionFutureNotificationDates = perSubscriptionFutureNotificationDates;
+ }
+
+ public Invoice getInvoice() {
+ return invoice;
+ }
+
+ public Map<UUID, SubscriptionFutureNotificationDates> getPerSubscriptionFutureNotificationDates() {
+ return perSubscriptionFutureNotificationDates;
+ }
+
+}
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 d229093..6f4b247 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -68,6 +68,8 @@ import org.killbill.billing.invoice.dao.InvoiceItemModelDao;
import org.killbill.billing.invoice.dao.InvoiceModelDao;
import org.killbill.billing.invoice.generator.BillingIntervalDetail;
import org.killbill.billing.invoice.generator.InvoiceGenerator;
+import org.killbill.billing.invoice.generator.InvoiceWithMetadata;
+import org.killbill.billing.invoice.generator.InvoiceWithMetadata.SubscriptionFutureNotificationDates;
import org.killbill.billing.invoice.model.DefaultInvoice;
import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
import org.killbill.billing.invoice.model.InvoiceItemFactory;
@@ -203,7 +205,7 @@ public class InvoiceDispatcher {
}
}
- public Invoice processAccount(final UUID accountId, final DateTime targetDate,
+ public Invoice processAccount(final UUID accountId, @Nullable final DateTime targetDate,
@Nullable final DryRunArguments dryRunArguments, final InternalCallContext context) throws InvoiceApiException {
GlobalLock lock = null;
try {
@@ -226,12 +228,14 @@ public class InvoiceDispatcher {
@Nullable final DryRunArguments dryRunArguments, final InternalCallContext context) throws InvoiceApiException {
final boolean isDryRun = dryRunArguments != null;
- // inputTargetDateTime is only allowed in dryRun mode to have the system compute it
+ // A null inputTargetDateTime is only allowed in dryRun mode to have the system compute it
Preconditions.checkArgument(inputTargetDateTime != null || isDryRun, "inputTargetDateTime is required in non dryRun mode");
try {
// Make sure to first set the BCD if needed then get the account object (to have the BCD set)
final BillingEventSet billingEvents = billingApi.getBillingEventsForAccountAndUpdateAccountBCD(accountId, dryRunArguments, context);
-
+ if (billingEvents.isEmpty()) {
+ return null;
+ }
final List<DateTime> candidateDateTimes = (inputTargetDateTime != null) ? ImmutableList.of(inputTargetDateTime) : getUpcomingInvoiceCandidateDates(context);
for (final DateTime curTargetDateTime : candidateDateTimes) {
final Invoice invoice = processAccountWithLockAndInputTargetDate(accountId, curTargetDateTime, billingEvents, isDryRun, context);
@@ -250,9 +254,7 @@ public class InvoiceDispatcher {
final BillingEventSet billingEvents, final boolean isDryRun, final InternalCallContext context) throws InvoiceApiException {
try {
final Account account = accountApi.getAccountById(accountId, context);
- final DateAndTimeZoneContext dateAndTimeZoneContext = billingEvents.iterator().hasNext() ?
- new DateAndTimeZoneContext(billingEvents.iterator().next().getEffectiveDate(), account.getTimeZone(), clock) :
- null;
+ final DateAndTimeZoneContext dateAndTimeZoneContext = new DateAndTimeZoneContext(billingEvents.iterator().next().getEffectiveDate(), account.getTimeZone(), clock);
final List<Invoice> invoices = billingEvents.isAccountAutoInvoiceOff() ?
ImmutableList.<Invoice>of() :
@@ -265,20 +267,30 @@ public class InvoiceDispatcher {
}));
final Currency targetCurrency = account.getCurrency();
+ final LocalDate targetDate = dateAndTimeZoneContext.computeTargetDate(targetDateTime);
+ final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, billingEvents, invoices, targetDate, targetCurrency, context);
+ final Invoice invoice = invoiceWithMetadata.getInvoice();
+
+ // Compute future notifications
+ final FutureAccountNotifications futureAccountNotifications = createNextFutureNotificationDate(invoiceWithMetadata, billingEvents, dateAndTimeZoneContext, context);
- final LocalDate targetDate = (dateAndTimeZoneContext != null && targetDateTime != null) ? dateAndTimeZoneContext.computeTargetDate(targetDateTime) : null;
- final Invoice invoice = targetDate != null ? generator.generateInvoice(account, billingEvents, invoices, targetDate, targetCurrency, context) : null;
//
+
// If invoice comes back null, there is nothing new to generate, we can bail early
//
if (invoice == null) {
- log.info("Generated null invoice for accountId {} and targetDate {} (targetDateTime {})", new Object[]{accountId, targetDate, targetDateTime});
- if (!isDryRun) {
+ if (isDryRun) {
+ log.info("Generated null dryRun invoice for accountId {} and targetDate {} (targetDateTime {})", new Object[]{accountId, targetDate, targetDateTime});
+ } else {
+ log.info("Generated null invoice for accountId {} and targetDate {} (targetDateTime {})", new Object[]{accountId, targetDate, targetDateTime});
+
final BusInternalEvent event = new DefaultNullInvoiceEvent(accountId, clock.getUTCToday(),
context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
+
+ commitInvoiceAndSetFutureNotifications(account, null, ImmutableList.<InvoiceItemModelDao>of(), futureAccountNotifications, false, context);
postEvent(event, accountId, context);
}
- return invoice;
+ return null;
}
// Generate missing credit (> 0 for generation and < 0 for use) prior we call the plugin
@@ -286,14 +298,39 @@ public class InvoiceDispatcher {
if (cbaItem != null) {
invoice.addInvoiceItem(cbaItem);
}
-
//
// Ask external invoice plugins if additional items (tax, etc) shall be added to the invoice
//
final CallContext callContext = buildCallContext(context);
invoice.addInvoiceItems(invoicePluginDispatcher.getAdditionalInvoiceItems(invoice, callContext));
+
+
+
if (!isDryRun) {
- commitInvoiceStateAndNotifyAccountIfConfigured(account, invoice, billingEvents, dateAndTimeZoneContext, targetDate, context);
+
+ // Compute whether this is a new invoice object (or just some adjustments on an existing invoice), and extract invoiceIds for later use
+ final Set<UUID> uniqueInvoiceIds = getUniqueInvoiceIds(invoice);
+ final boolean isRealInvoiceWithItems = uniqueInvoiceIds.remove(invoice.getId());
+ final Set<UUID> adjustedUniqueOtherInvoiceId = uniqueInvoiceIds;
+
+ logInvoiceWithItems(account, invoice, targetDate, adjustedUniqueOtherInvoiceId, isRealInvoiceWithItems);
+
+ // Transformation to Invoice -> InvoiceModelDao
+ final InvoiceModelDao invoiceModelDao = new InvoiceModelDao(invoice);
+ final Iterable<InvoiceItemModelDao> invoiceItemModelDaos = transformToInvoiceModelDao(invoice.getInvoiceItems());
+
+ // Commit invoice on disk
+ final boolean isThereAnyItemsLeft = commitInvoiceAndSetFutureNotifications(account, invoiceModelDao, invoiceItemModelDaos, futureAccountNotifications, isRealInvoiceWithItems, context);
+
+ final boolean isRealInvoiceWithNonEmptyItems = isThereAnyItemsLeft ? isRealInvoiceWithItems : false;
+
+
+ setChargedThroughDates(dateAndTimeZoneContext, invoice.getInvoiceItems(FixedPriceInvoiceItem.class), invoice.getInvoiceItems(RecurringInvoiceItem.class), context);
+
+ // TODO we should send bus events when we commit the ionvoice on disk in commitInvoice
+ postEvents(account, invoice, adjustedUniqueOtherInvoiceId, isRealInvoiceWithNonEmptyItems, context);
+
+ notifyAccountIfEnabled(account, invoice, isRealInvoiceWithNonEmptyItems, context);
}
return invoice;
} catch (final AccountApiException e) {
@@ -305,38 +342,88 @@ public class InvoiceDispatcher {
}
}
- private void commitInvoiceStateAndNotifyAccountIfConfigured(final Account account, final Invoice invoice, final BillingEventSet billingEvents, final DateAndTimeZoneContext dateAndTimeZoneContext, final LocalDate targetDate, final InternalCallContext context) throws SubscriptionBaseApiException, InvoiceApiException {
- boolean isRealInvoiceWithNonEmptyItems = false;
- // Extract the set of invoiceId for which we see items that don't belong to current generated invoice
- final Set<UUID> adjustedUniqueOtherInvoiceId = new TreeSet<UUID>();
- adjustedUniqueOtherInvoiceId.addAll(Collections2.transform(invoice.getInvoiceItems(), new Function<InvoiceItem, UUID>() {
+
+ private FutureAccountNotifications createNextFutureNotificationDate(final InvoiceWithMetadata invoiceWithMetadata, final BillingEventSet billingEvents, final DateAndTimeZoneContext dateAndTimeZoneContext, final InternalCallContext context) {
+
+ final Map<UUID, List<SubscriptionNotification>> result = new HashMap<UUID, List<SubscriptionNotification>>();
+
+ for (final UUID subscriptionId : invoiceWithMetadata.getPerSubscriptionFutureNotificationDates().keySet()) {
+
+ final List<SubscriptionNotification> perSubscriptionNotifications = new ArrayList<SubscriptionNotification>();
+
+ final SubscriptionFutureNotificationDates subscriptionFutureNotificationDates = invoiceWithMetadata.getPerSubscriptionFutureNotificationDates().get(subscriptionId);
+ // Add next recurring date if any
+ if (subscriptionFutureNotificationDates.getNextRecurringDate() != null) {
+ perSubscriptionNotifications.add(new SubscriptionNotification(dateAndTimeZoneContext.computeUTCDateTimeFromLocalDate(subscriptionFutureNotificationDates.getNextRecurringDate()), true));
+ }
+ // Add next usage dates if any
+ if (subscriptionFutureNotificationDates.getNextUsageDates() != null) {
+ for (String usageName : subscriptionFutureNotificationDates.getNextUsageDates().keySet()) {
+ final LocalDate nextNotificationDateForUsage = subscriptionFutureNotificationDates.getNextUsageDates().get(usageName);
+ final DateTime subscriptionUsageCallbackDate = getNextUsageBillingDate(subscriptionId, usageName, nextNotificationDateForUsage, dateAndTimeZoneContext, billingEvents);
+ perSubscriptionNotifications.add(new SubscriptionNotification(subscriptionUsageCallbackDate, true));
+ }
+ }
+ if (!perSubscriptionNotifications.isEmpty()) {
+ result.put(subscriptionId, perSubscriptionNotifications);
+ }
+ }
+
+ // 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);
+ }
+
+ private Iterable<InvoiceItemModelDao> transformToInvoiceModelDao(final List<InvoiceItem> invoiceItems) {
+ return Iterables.transform(invoiceItems,
+ new Function<InvoiceItem, InvoiceItemModelDao>() {
+ @Override
+ public InvoiceItemModelDao apply(final InvoiceItem input) {
+ return new InvoiceItemModelDao(input);
+ }
+ });
+ }
+
+ private Set<UUID> getUniqueInvoiceIds(final Invoice invoice) {
+ final Set<UUID> uniqueInvoiceIds = new TreeSet<UUID>();
+ uniqueInvoiceIds.addAll(Collections2.transform(invoice.getInvoiceItems(), new Function<InvoiceItem, UUID>() {
@Nullable
@Override
public UUID apply(@Nullable final InvoiceItem input) {
return input.getInvoiceId();
}
}));
- boolean isRealInvoiceWithItems = adjustedUniqueOtherInvoiceId.remove(invoice.getId());
+ return uniqueInvoiceIds;
+ }
+
+ private void logInvoiceWithItems(final Account account, final Invoice invoice, final LocalDate targetDate, final Set<UUID> adjustedUniqueOtherInvoiceId, final boolean isRealInvoiceWithItems) {
if (isRealInvoiceWithItems) {
log.info("Generated invoice {} with {} items for accountId {} and targetDate {}", new Object[]{invoice.getId(), invoice.getNumberOfItems(), account.getId(), targetDate});
} else {
final Joiner joiner = Joiner.on(",");
final String adjustedInvoices = joiner.join(adjustedUniqueOtherInvoiceId.toArray(new UUID[adjustedUniqueOtherInvoiceId.size()]));
log.info("Adjusting existing invoices {} with {} items for accountId {} and targetDate {})", new Object[]{adjustedInvoices, invoice.getNumberOfItems(),
- account.getId(), targetDate});
+ account.getId(), targetDate});
}
+ }
- // Transformation to Invoice -> InvoiceModelDao
- final InvoiceModelDao invoiceModelDao = new InvoiceModelDao(invoice);
- final Iterable<InvoiceItemModelDao> invoiceItemModelDaos = Iterables.transform(invoice.getInvoiceItems(),
- new Function<InvoiceItem, InvoiceItemModelDao>() {
- @Override
- public InvoiceItemModelDao apply(final InvoiceItem input) {
- return new InvoiceItemModelDao(input);
- }
- });
- final FutureAccountNotifications futureAccountNotifications = createNextFutureNotificationDate(invoiceItemModelDaos, billingEvents, dateAndTimeZoneContext, context);
+ private boolean commitInvoiceAndSetFutureNotifications(final Account account, final InvoiceModelDao invoiceModelDao,
+ final Iterable<InvoiceItemModelDao> invoiceItemModelDaos,
+ final FutureAccountNotifications futureAccountNotifications,
+ boolean isRealInvoiceWithItems, final InternalCallContext context) throws SubscriptionBaseApiException, InvoiceApiException {
// We filter any zero amount for USAGE items prior we generate the invoice, which may leave us with an invoice with no items;
// we recompute the isRealInvoiceWithItems flag based on what is left (the call to invoice is still necessary to set the future notifications).
final Iterable<InvoiceItemModelDao> filteredInvoiceItemModelDaos = Iterables.filter(invoiceItemModelDaos, new Predicate<InvoiceItemModelDao>() {
@@ -347,17 +434,15 @@ public class InvoiceDispatcher {
});
final boolean isThereAnyItemsLeft = filteredInvoiceItemModelDaos.iterator().hasNext();
- isRealInvoiceWithNonEmptyItems = isThereAnyItemsLeft ? isRealInvoiceWithItems : false;
-
if (isThereAnyItemsLeft) {
invoiceDao.createInvoice(invoiceModelDao, ImmutableList.copyOf(filteredInvoiceItemModelDaos), isRealInvoiceWithItems, futureAccountNotifications, context);
} else {
invoiceDao.setFutureAccountNotificationsForEmptyInvoice(account.getId(), futureAccountNotifications, context);
}
+ return isThereAnyItemsLeft;
+ }
- final List<InvoiceItem> fixedPriceInvoiceItems = invoice.getInvoiceItems(FixedPriceInvoiceItem.class);
- final List<InvoiceItem> recurringInvoiceItems = invoice.getInvoiceItems(RecurringInvoiceItem.class);
- setChargedThroughDates(dateAndTimeZoneContext, fixedPriceInvoiceItems, recurringInvoiceItems, context);
+ private void postEvents(final Account account, final Invoice invoice, final Set<UUID> adjustedUniqueOtherInvoiceId, final boolean isRealInvoiceWithNonEmptyItems, final InternalCallContext context) {
final List<InvoiceInternalEvent> events = new ArrayList<InvoiceInternalEvent>();
if (isRealInvoiceWithNonEmptyItems) {
@@ -370,11 +455,12 @@ public class InvoiceDispatcher {
context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
events.add(event);
}
-
for (final InvoiceInternalEvent event : events) {
postEvent(event, account.getId(), context);
}
+ }
+ private void notifyAccountIfEnabled(final Account account, final Invoice invoice, final boolean isRealInvoiceWithNonEmptyItems, final InternalCallContext context) throws InvoiceApiException {
if (account.isNotifiedForInvoices() && isRealInvoiceWithNonEmptyItems) {
// Need to re-hydrate the invoice object to get the invoice number (record id)
// API_FIX InvoiceNotifier public API?
@@ -382,6 +468,7 @@ public class InvoiceDispatcher {
}
}
+
private InvoiceItem computeCBAOnExistingInvoice(final Invoice invoice, final InternalCallContext context) throws InvoiceApiException {
// Transformation to Invoice -> InvoiceModelDao
final InvoiceModelDao invoiceModelDao = new InvoiceModelDao(invoice);
@@ -405,75 +492,6 @@ public class InvoiceDispatcher {
return internalCallContextFactory.createCallContext(context);
}
- @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.
- //
- 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);
- }
-
private DateTime getNextUsageBillingDate(final UUID subscriptionId, final String usageName, final LocalDate chargedThroughDate, final DateAndTimeZoneContext dateAndTimeZoneContext, final BillingEventSet billingEvents) {
final Usage usage = billingEvents.getUsages().get(usageName);
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultBillingModeGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultBillingModeGenerator.java
index cb8f6f8..29bcc1f 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultBillingModeGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultBillingModeGenerator.java
@@ -54,7 +54,7 @@ public class DefaultBillingModeGenerator implements BillingModeGenerator {
final BillingIntervalDetail billingIntervalDetail = new BillingIntervalDetail(startDate, endDate, targetDate, billingCycleDayLocal, billingPeriod, billingMode);
- // We are not billing for less than a day (we could...)
+ // We are not billing for less than a day
if (!billingIntervalDetail.hasSomethingToBill()) {
return results;
}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
index 97cd232..e1ad03a 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
@@ -53,6 +53,7 @@ import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.invoice.api.InvoicePayment;
import org.killbill.billing.invoice.api.InvoicePaymentType;
+import org.killbill.billing.invoice.generator.InvoiceWithMetadata;
import org.killbill.billing.invoice.model.CreditAdjInvoiceItem;
import org.killbill.billing.invoice.model.CreditBalanceAdjInvoiceItem;
import org.killbill.billing.invoice.model.DefaultInvoice;
@@ -556,7 +557,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final UUID accountId = account.getId();
final UUID bundleId = UUID.randomUUID();
final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
- final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
+ final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
invoiceUtil.createInvoice(invoice1, true, context);
final LocalDate startDate = new LocalDate(2011, 3, 1);
@@ -1109,7 +1110,8 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final BillingEventSet events = new MockBillingEventSet();
events.add(event1);
- final Invoice invoice1 = generator.generateInvoice(account, events, invoiceList, targetDate, Currency.USD, context);
+ final InvoiceWithMetadata invoiceWithMetadata1 = generator.generateInvoice(account, events, invoiceList, targetDate, Currency.USD, context);
+ final Invoice invoice1 = invoiceWithMetadata1.getInvoice();
assertEquals(invoice1.getBalance(), KillBillMoney.of(TEN, invoice1.getCurrency()));
invoiceList.add(invoice1);
@@ -1127,7 +1129,9 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// second invoice should be for one half (14/28 days) the difference between the rate plans
// this is a temporary state, since it actually contains an adjusting item that properly belong to invoice 1
- final Invoice invoice2 = generator.generateInvoice(account, events, invoiceList, targetDate, Currency.USD, context);
+ final InvoiceWithMetadata invoiceWithMetadata2 = generator.generateInvoice(account, events, invoiceList, targetDate, Currency.USD, context);
+ final Invoice invoice2 = invoiceWithMetadata2.getInvoice();
+
assertEquals(invoice2.getBalance(), KillBillMoney.of(FIVE, invoice2.getCurrency()));
invoiceList.add(invoice2);
@@ -1159,7 +1163,8 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
events.add(event);
final LocalDate targetDate = invoiceUtil.buildDate(2011, 1, 15);
- final Invoice invoice = generator.generateInvoice(account, events, null, targetDate, Currency.USD, context);
+ final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, targetDate, Currency.USD, context);
+ final Invoice invoice = invoiceWithMetadata.getInvoice();
// expect one pro-ration item and one full-period item
assertEquals(invoice.getNumberOfItems(), 2);
@@ -1202,7 +1207,8 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
events.add(event1);
final UUID accountId = account.getId();
- final Invoice invoice1 = generator.generateInvoice(account, events, null, new LocalDate(effectiveDate1), Currency.USD, context);
+ final InvoiceWithMetadata invoiceWithMetadata1 = generator.generateInvoice(account, events, null, new LocalDate(effectiveDate1), Currency.USD, context);
+ final Invoice invoice1 = invoiceWithMetadata1.getInvoice();
assertNotNull(invoice1);
assertEquals(invoice1.getNumberOfItems(), 1);
assertEquals(invoice1.getBalance().compareTo(ZERO), 0);
@@ -1218,7 +1224,8 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
"testEvent2", 2L, SubscriptionBaseTransitionType.PHASE);
events.add(event2);
- final Invoice invoice2 = generator.generateInvoice(account, events, invoiceList, new LocalDate(effectiveDate2), Currency.USD, context);
+ final InvoiceWithMetadata invoiceWithMetadata2 = generator.generateInvoice(account, events, invoiceList, new LocalDate(effectiveDate2), Currency.USD, context);
+ final Invoice invoice2 = invoiceWithMetadata2.getInvoice();
assertNotNull(invoice2);
assertEquals(invoice2.getNumberOfItems(), 1);
assertEquals(invoice2.getBalance().compareTo(cheapAmount), 0);
@@ -1228,7 +1235,8 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
//invoiceUtil.createInvoice(invoice2, invoice2.getTargetDate().getDayOfMonth(), callcontext);
final DateTime effectiveDate3 = effectiveDate2.plusMonths(1);
- final Invoice invoice3 = generator.generateInvoice(account, events, invoiceList, new LocalDate(effectiveDate3), Currency.USD, context);
+ final InvoiceWithMetadata invoiceWithMetadata3 = generator.generateInvoice(account, events, invoiceList, new LocalDate(effectiveDate3), Currency.USD, context);
+ final Invoice invoice3 = invoiceWithMetadata3.getInvoice();
assertNotNull(invoice3);
assertEquals(invoice3.getNumberOfItems(), 1);
assertEquals(invoice3.getBalance().compareTo(cheapAmount), 0);
@@ -1239,7 +1247,8 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
@Test(groups = "slow")
public void testInvoiceForEmptyEventSet() throws InvoiceApiException {
final BillingEventSet events = new MockBillingEventSet();
- final Invoice invoice = generator.generateInvoice(account, events, null, new LocalDate(), Currency.USD, context);
+ final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, new LocalDate(), Currency.USD, context);
+ final Invoice invoice = invoiceWithMetadata.getInvoice();
assertNull(invoice);
}
@@ -1273,7 +1282,8 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
"testEvent2", 2L, SubscriptionBaseTransitionType.CHANGE);
events.add(event2);
- final Invoice invoice = generator.generateInvoice(account, events, null, new LocalDate(effectiveDate2), Currency.USD, context);
+ final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, new LocalDate(effectiveDate2), Currency.USD, context);
+ final Invoice invoice = invoiceWithMetadata.getInvoice();
assertNotNull(invoice);
assertEquals(invoice.getNumberOfItems(), 2);
assertEquals(invoice.getBalance().compareTo(cheapAmount), 0);
@@ -1344,7 +1354,8 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
BillingPeriod.MONTHLY, 31, BillingMode.IN_ADVANCE,
"new-event", 1L, SubscriptionBaseTransitionType.CREATE);
events.add(event1);
- final Invoice newInvoice = generator.generateInvoice(account, events, invoices, targetDate, Currency.USD, context);
+ final InvoiceWithMetadata newInvoiceWithMetadata = generator.generateInvoice(account, events, invoices, targetDate, Currency.USD, context);
+ final Invoice newInvoice = newInvoiceWithMetadata.getInvoice();
invoiceUtil.createInvoice(newInvoice, true, context);
// VERIFY THAT WE STILL HAVE ONLY 2 ITEMS, MEANING THERE WERE NO REPAIR AND NO CBA GENERATED
@@ -1379,7 +1390,8 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
"testEvent1", 1L, SubscriptionBaseTransitionType.CHANGE);
events.add(event1);
- Invoice invoice1 = generator.generateInvoice(account, events, invoices, new LocalDate(targetDate1), Currency.USD, context);
+ InvoiceWithMetadata invoiceWithMetadata1 = generator.generateInvoice(account, events, invoices, new LocalDate(targetDate1), Currency.USD, context);
+ Invoice invoice1 = invoiceWithMetadata1.getInvoice();
invoices.add(invoice1);
invoiceUtil.createInvoice(invoice1, true, context);
invoice1 = new DefaultInvoice(invoiceDao.getById(invoice1.getId(), context));
@@ -1390,7 +1402,8 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
BillingPeriod.MONTHLY, 31, BillingMode.IN_ADVANCE,
"testEvent2", 2L, SubscriptionBaseTransitionType.CHANGE);
events.add(event2);
- Invoice invoice2 = generator.generateInvoice(account, events, invoices, new LocalDate(targetDate2), Currency.USD, context);
+ InvoiceWithMetadata invoiceWithMetadata2 = generator.generateInvoice(account, events, invoices, new LocalDate(targetDate2), Currency.USD, context);
+ Invoice invoice2 = invoiceWithMetadata2.getInvoice();
invoiceUtil.createInvoice(invoice2, true, context);
invoice2 = new DefaultInvoice(invoiceDao.getById(invoice2.getId(), context));
assertNotNull(invoice2.getInvoiceNumber());
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
index 9094ddc..cadda9d 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
@@ -146,15 +146,15 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
@Test(groups = "fast")
public void testWithNullEventSetAndNullInvoiceSet() throws InvoiceApiException {
- final Invoice invoice = generator.generateInvoice(account, null, null, clock.getUTCToday(), Currency.USD, internalCallContext);
- assertNull(invoice);
+ final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, null, null, clock.getUTCToday(), Currency.USD, internalCallContext);
+ assertNull(invoiceWithMetadata.getInvoice());
}
@Test(groups = "fast")
public void testWithEmptyEventSet() throws InvoiceApiException {
final BillingEventSet events = new MockBillingEventSet();
- final Invoice invoice = generator.generateInvoice(account, events, null, clock.getUTCToday(), Currency.USD, internalCallContext);
- assertNull(invoice);
+ final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, clock.getUTCToday(), Currency.USD, internalCallContext);
+ assertNull(invoiceWithMetadata.getInvoice());
}
@Test(groups = "fast")
@@ -172,8 +172,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
events.add(event);
final LocalDate targetDate = invoiceUtil.buildDate(2011, 10, 3);
- final Invoice invoice = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
-
+ final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
+ final Invoice invoice = invoiceWithMetadata.getInvoice();
assertNotNull(invoice);
assertEquals(invoice.getNumberOfItems(), 2);
assertEquals(invoice.getBalance(), KillBillMoney.of(TWENTY, invoice.getCurrency()));
@@ -209,8 +209,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
// Target date is the next BCD, in local time
final LocalDate targetDate = invoiceUtil.buildDate(2012, 8, bcdLocal);
- final Invoice invoice = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
-
+ final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
+ final Invoice invoice = invoiceWithMetadata.getInvoice();
assertNotNull(invoice);
assertEquals(invoice.getNumberOfItems(), 2);
assertEquals(invoice.getInvoiceItems().get(0).getStartDate(), invoiceUtil.buildDate(2012, 7, 16));
@@ -233,8 +233,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
// Set a target date of today (start date)
final LocalDate targetDate = startDate;
- final Invoice invoice = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
-
+ final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
+ final Invoice invoice = invoiceWithMetadata.getInvoice();
assertNotNull(invoice);
assertEquals(invoice.getNumberOfItems(), 1);
assertEquals(invoice.getInvoiceItems().get(0).getStartDate(), invoiceUtil.buildDate(2012, 7, 16));
@@ -255,8 +255,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
events.add(event);
final LocalDate targetDate = invoiceUtil.buildDate(2011, 10, 3);
- final Invoice invoice = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
-
+ final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
+ final Invoice invoice = invoiceWithMetadata.getInvoice();
assertNotNull(invoice);
assertEquals(invoice.getNumberOfItems(), 2);
@@ -287,8 +287,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
events.add(event2);
final LocalDate targetDate = invoiceUtil.buildDate(2011, 10, 3);
- final Invoice invoice = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
-
+ final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
+ final Invoice invoice = invoiceWithMetadata.getInvoice();
assertNotNull(invoice);
assertEquals(invoice.getNumberOfItems(), 2);
assertEquals(invoice.getBalance(), KillBillMoney.of(rate1.add(rate2), invoice.getCurrency()));
@@ -313,8 +313,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
final LocalDate targetDate = invoiceUtil.buildDate(2011, 12, 3);
final UUID accountId = UUID.randomUUID();
- final Invoice invoice = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
-
+ final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
+ final Invoice invoice = invoiceWithMetadata.getInvoice();
assertNotNull(invoice);
assertEquals(invoice.getNumberOfItems(), 4);
@@ -354,8 +354,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
events.add(event3);
final LocalDate targetDate = invoiceUtil.buildDate(2011, 12, 3);
- final Invoice invoice = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
-
+ final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
+ final Invoice invoice = invoiceWithMetadata.getInvoice();
assertNotNull(invoice);
assertEquals(invoice.getNumberOfItems(), 4);
assertEquals(invoice.getBalance(), KillBillMoney.of(rate1.add(rate2).add(TWO.multiply(rate3)), invoice.getCurrency()));
@@ -376,13 +376,14 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
events.add(event1);
LocalDate targetDate = invoiceUtil.buildDate(2011, 12, 1);
- final Invoice invoice1 = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata1 = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
final List<Invoice> existingInvoices = new ArrayList<Invoice>();
+ final Invoice invoice1 = invoiceWithMetadata1.getInvoice();
existingInvoices.add(invoice1);
targetDate = invoiceUtil.buildDate(2011, 12, 3);
- final Invoice invoice2 = generator.generateInvoice(account, events, existingInvoices, targetDate, Currency.USD, internalCallContext);
-
+ final InvoiceWithMetadata invoiceWithMetadata2 = generator.generateInvoice(account, events, existingInvoices, targetDate, Currency.USD, internalCallContext);
+ final Invoice invoice2 = invoiceWithMetadata2.getInvoice();
assertNull(invoice2);
}
@@ -554,8 +555,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
final LocalDate targetDate = invoiceUtil.buildDate(2011, 1, 1);
events.add(createBillingEvent(UUID.randomUUID(), UUID.randomUUID(), targetDate, plan, planPhase, 1));
- final Invoice invoice = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
-
+ final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
+ final Invoice invoice = invoiceWithMetadata.getInvoice();
assertEquals(invoice.getNumberOfItems(), 1);
}
@@ -569,9 +570,11 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
events.add(createBillingEvent(UUID.randomUUID(), UUID.randomUUID(), startDate, plan, planPhase, startDate.getDayOfMonth()));
- final Invoice invoice = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
+ final Invoice invoice = invoiceWithMetadata.getInvoice();
final RecurringInvoiceItem item = (RecurringInvoiceItem) invoice.getInvoiceItems().get(0);
+
// end date of the invoice item should be equal to exactly one month later (rounded)
assertEquals(item.getEndDate(), startDate.plusMonths(1));
}
@@ -605,13 +608,15 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
events.add(event2);
events.add(event1);
- final Invoice invoice1 = generator.generateInvoice(account, events, null, new LocalDate("2012-02-01"), Currency.USD, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata1 = generator.generateInvoice(account, events, null, new LocalDate("2012-02-01"), Currency.USD, internalCallContext);
+ final Invoice invoice1 = invoiceWithMetadata1.getInvoice();
assertNotNull(invoice1);
assertEquals(invoice1.getNumberOfItems(), 1);
final List<Invoice> invoiceList = new ArrayList<Invoice>();
invoiceList.add(invoice1);
- final Invoice invoice2 = generator.generateInvoice(account, events, invoiceList, new LocalDate("2012-04-05"), Currency.USD, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata2 = generator.generateInvoice(account, events, invoiceList, new LocalDate("2012-04-05"), Currency.USD, internalCallContext);
+ final Invoice invoice2 = invoiceWithMetadata2.getInvoice();
assertNotNull(invoice2);
assertEquals(invoice2.getNumberOfItems(), 1);
final FixedPriceInvoiceItem item = (FixedPriceInvoiceItem) invoice2.getInvoiceItems().get(0);
@@ -635,7 +640,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
events.add(event1);
// ensure both components are invoiced
- final Invoice invoice1 = generator.generateInvoice(account, events, null, startDate, Currency.USD, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata1 = generator.generateInvoice(account, events, null, startDate, Currency.USD, internalCallContext);
+ final Invoice invoice1 = invoiceWithMetadata1.getInvoice();
assertNotNull(invoice1);
assertEquals(invoice1.getNumberOfItems(), 2);
assertEquals(invoice1.getBalance(), KillBillMoney.of(FIFTEEN, invoice1.getCurrency()));
@@ -647,7 +653,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
final LocalDate currentDate = startDate.plusMonths(1);
// ensure that only the recurring price is invoiced
- final Invoice invoice2 = generator.generateInvoice(account, events, invoiceList, currentDate, Currency.USD, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata2 = generator.generateInvoice(account, events, invoiceList, currentDate, Currency.USD, internalCallContext);
+ final Invoice invoice2 = invoiceWithMetadata2.getInvoice();
assertNotNull(invoice2);
assertEquals(invoice2.getNumberOfItems(), 1);
assertEquals(invoice2.getBalance(), KillBillMoney.of(FIVE, invoice2.getCurrency()));
@@ -672,8 +679,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
events.add(event1);
// ensure that a single invoice item is generated for the fixed cost
- final Invoice invoice1 = generator.generateInvoice(account, events, null, startDate, Currency.USD, internalCallContext);
- assertNotNull(invoice1);
+ final InvoiceWithMetadata invoiceWithMetadata1 = generator.generateInvoice(account, events, null, startDate, Currency.USD, internalCallContext);
+ final Invoice invoice1 = invoiceWithMetadata1.getInvoice();assertNotNull(invoice1);
assertEquals(invoice1.getNumberOfItems(), 1);
assertEquals(invoice1.getBalance(), KillBillMoney.of(fixedCost1, invoice1.getCurrency()));
@@ -686,8 +693,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
events.add(event2);
// ensure that a single invoice item is generated for the fixed cost
- final Invoice invoice2 = generator.generateInvoice(account, events, invoiceList, phaseChangeDate, Currency.USD, internalCallContext);
- assertNotNull(invoice2);
+ final InvoiceWithMetadata invoiceWithMetadata2 = generator.generateInvoice(account, events, invoiceList, phaseChangeDate, Currency.USD, internalCallContext);
+ final Invoice invoice2 = invoiceWithMetadata2.getInvoice();
assertEquals(invoice2.getNumberOfItems(), 1);
assertEquals(invoice2.getBalance(), KillBillMoney.of(fixedCost2, invoice2.getCurrency()));
}
@@ -718,7 +725,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
final LocalDate discountPhaseEndDate = trialPhaseEndDate.plusMonths(6);
events.add(createBillingEvent(subscriptionId, bundleId, discountPhaseEndDate, plan1, phase3, BILL_CYCLE_DAY));
- final Invoice invoice1 = generator.generateInvoice(account, events, null, creationDate, Currency.USD, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata1 = generator.generateInvoice(account, events, null, creationDate, Currency.USD, internalCallContext);
+ final Invoice invoice1 = invoiceWithMetadata1.getInvoice();
assertNotNull(invoice1);
assertEquals(invoice1.getNumberOfItems(), 1);
assertEquals(invoice1.getBalance().compareTo(ZERO), 0);
@@ -726,7 +734,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
final List<Invoice> invoiceList = new ArrayList<Invoice>();
invoiceList.add(invoice1);
- final Invoice invoice2 = generator.generateInvoice(account, events, invoiceList, trialPhaseEndDate, Currency.USD, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata2 = generator.generateInvoice(account, events, invoiceList, trialPhaseEndDate, Currency.USD, internalCallContext);
+ final Invoice invoice2 = invoiceWithMetadata2.getInvoice();
assertNotNull(invoice2);
assertEquals(invoice2.getNumberOfItems(), 1);
assertEquals(invoice2.getInvoiceItems().get(0).getStartDate(), trialPhaseEndDate);
@@ -734,7 +743,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
invoiceList.add(invoice2);
LocalDate targetDate = new LocalDate(trialPhaseEndDate.getYear(), trialPhaseEndDate.getMonthOfYear(), BILL_CYCLE_DAY);
- final Invoice invoice3 = generator.generateInvoice(account, events, invoiceList, targetDate, Currency.USD, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata3 = generator.generateInvoice(account, events, invoiceList, targetDate, Currency.USD, internalCallContext);
+ final Invoice invoice3 = invoiceWithMetadata3.getInvoice();
assertNotNull(invoice3);
assertEquals(invoice3.getNumberOfItems(), 1);
assertEquals(invoice3.getInvoiceItems().get(0).getStartDate(), targetDate);
@@ -742,7 +752,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
invoiceList.add(invoice3);
targetDate = targetDate.plusMonths(6);
- final Invoice invoice4 = generator.generateInvoice(account, events, invoiceList, targetDate, Currency.USD, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata4 = generator.generateInvoice(account, events, invoiceList, targetDate, Currency.USD, internalCallContext);
+ final Invoice invoice4 = invoiceWithMetadata4.getInvoice();
assertNotNull(invoice4);
assertEquals(invoice4.getNumberOfItems(), 7);
}
@@ -801,7 +812,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
final LocalDate targetDate, final int expectedNumberOfItems,
final BigDecimal expectedAmount) throws InvoiceApiException {
final Currency currency = Currency.USD;
- final Invoice invoice = generator.generateInvoice(account, events, existingInvoices, targetDate, currency, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, existingInvoices, targetDate, currency, internalCallContext);
+ final Invoice invoice = invoiceWithMetadata.getInvoice();
assertNotNull(invoice);
assertEquals(invoice.getNumberOfItems(), expectedNumberOfItems);
existingInvoices.add(invoice);
@@ -827,7 +839,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
events.add(createBillingEvent(baseSubscription.getId(), baseSubscription.getBundleId(), april25, basePlan, basePlanEvergreen, 25));
// generate invoice
- final Invoice invoice1 = generator.generateInvoice(account, events, null, april25, Currency.USD, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata1 = generator.generateInvoice(account, events, null, april25, Currency.USD, internalCallContext);
+ final Invoice invoice1 = invoiceWithMetadata1.getInvoice();
assertNotNull(invoice1);
assertEquals(invoice1.getNumberOfItems(), 1);
assertEquals(invoice1.getBalance().compareTo(TEN), 0);
@@ -848,7 +861,9 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
events.add(createBillingEvent(addOnSubscription2.getId(), baseSubscription.getBundleId(), april28, addOn2Plan, addOn2PlanPhaseEvergreen, 25));
// generate invoice
- final Invoice invoice2 = generator.generateInvoice(account, events, invoices, april28, Currency.USD, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata2 = generator.generateInvoice(account, events, invoices, april28, Currency.USD, internalCallContext);
+ final Invoice invoice2 = invoiceWithMetadata2.getInvoice();
+
invoices.add(invoice2);
assertNotNull(invoice2);
assertEquals(invoice2.getNumberOfItems(), 2);
@@ -865,7 +880,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
// generate invoice
final LocalDate may1 = new LocalDate(2012, 5, 1);
- final Invoice invoice3 = generator.generateInvoice(account, newEvents, invoices, may1, Currency.USD, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata3 = generator.generateInvoice(account, newEvents, invoices, may1, Currency.USD, internalCallContext);
+ final Invoice invoice3 = invoiceWithMetadata3.getInvoice();
assertNotNull(invoice3);
assertEquals(invoice3.getNumberOfItems(), 3);
// -4.50 -18 - 10 (to correct the previous 2 invoices) + 4.50 + 13
@@ -887,7 +903,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
final BillingEventSet events = new MockBillingEventSet();
events.add(createBillingEvent(originalSubscription.getId(), originalSubscription.getBundleId(), april25, originalPlan, originalPlanEvergreen, 25));
- final Invoice invoice1 = generator.generateInvoice(account, events, null, april25, Currency.USD, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata1 = generator.generateInvoice(account, events, null, april25, Currency.USD, internalCallContext);
+ final Invoice invoice1 = invoiceWithMetadata1.getInvoice();
printDetailInvoice(invoice1);
@@ -909,7 +926,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
events.add(createBillingEvent(newSubscription.getId(), originalSubscription.getBundleId(), april25, newPlan, newPlanEvergreen, 25));
// generate a new invoice
- final Invoice invoice2 = generator.generateInvoice(account, events, invoices, april25, Currency.USD, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata2 = generator.generateInvoice(account, events, invoices, april25, Currency.USD, internalCallContext);
+ final Invoice invoice2 = invoiceWithMetadata2.getInvoice();
printDetailInvoice(invoice2);
assertEquals(invoice2.getNumberOfItems(), 2);
@@ -973,7 +991,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
// Generate a new invoice
- final Invoice invoice = generator.generateInvoice(account, events, existingInvoices, targetDate, currency, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, existingInvoices, targetDate, currency, internalCallContext);
+ final Invoice invoice = invoiceWithMetadata.getInvoice();
assertEquals(invoice.getNumberOfItems(), 7);
assertEquals(invoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
assertEquals(invoice.getInvoiceItems().get(0).getStartDate(), new LocalDate(2013, 6, 15));
@@ -1007,7 +1026,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
existingInvoices.add(invoice);
// Generate next invoice (no-op)
- final Invoice newInvoice = generator.generateInvoice(account, events, existingInvoices, targetDate, currency, internalCallContext);
+ final InvoiceWithMetadata newInvoiceWithMetdata = generator.generateInvoice(account, events, existingInvoices, targetDate, currency, internalCallContext);
+ final Invoice newInvoice = newInvoiceWithMetdata.getInvoice();
assertNull(newInvoice);
}
@@ -1054,9 +1074,9 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
final LocalDate targetDate = invoiceUtil.buildDate(2011, 10, 3);
final UUID accountId = UUID.randomUUID();
- final Invoice invoice = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
- assertNull(invoice);
+ assertNull(invoiceWithMetadata.getInvoice());
}
public void testAutoInvoiceOffWithCredits() throws CatalogApiException, InvoiceApiException {
@@ -1082,7 +1102,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
eventSet.add(createBillingEvent(subscriptionId2, bundleId, startDate, plan2, plan2phase1, 1));
// generate the first invoice
- final Invoice invoice1 = generator.generateInvoice(account, eventSet, invoices, startDate, currency, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata1 = generator.generateInvoice(account, eventSet, invoices, startDate, currency, internalCallContext);
+ final Invoice invoice1 = invoiceWithMetadata1.getInvoice();
assertNotNull(invoice1);
assertTrue(invoice1.getBalance().compareTo(FIFTEEN.add(TWELVE)) == 0);
invoices.add(invoice1);
@@ -1093,7 +1114,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
eventSet.addSubscriptionWithAutoInvoiceOff(subscriptionId1);
final LocalDate targetDate2 = startDate.plusMonths(1);
- final Invoice invoice2 = generator.generateInvoice(account, eventSet, invoices, targetDate2, currency, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata2 = generator.generateInvoice(account, eventSet, invoices, targetDate2, currency, internalCallContext);
+ final Invoice invoice2 = invoiceWithMetadata2.getInvoice();
assertNotNull(invoice2);
assertTrue(invoice2.getBalance().compareTo(TWELVE) == 0);
invoices.add(invoice2);
@@ -1101,7 +1123,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
final LocalDate targetDate3 = targetDate2.plusMonths(1);
eventSet.clearSubscriptionsWithAutoInvoiceOff();
eventSet.add(subscription1creation);
- final Invoice invoice3 = generator.generateInvoice(account, eventSet, invoices, targetDate3, currency, internalCallContext);
+ final InvoiceWithMetadata invoiceWithMetadata3 = generator.generateInvoice(account, eventSet, invoices, targetDate3, currency, internalCallContext);
+ final Invoice invoice3 = invoiceWithMetadata3.getInvoice();
assertNotNull(invoice3);
assertTrue(invoice3.getBalance().compareTo(FIFTEEN.multiply(TWO).add(TWELVE)) == 0);
}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
index 5929b0b..2bdde60 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
@@ -19,12 +19,10 @@
package org.killbill.billing.invoice;
import java.math.BigDecimal;
-import java.util.Collections;
import java.util.List;
import java.util.UUID;
import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountApiException;
@@ -38,8 +36,6 @@ import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.catalog.api.PhaseType;
import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.PlanPhase;
-import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications;
-import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications.SubscriptionNotification;
import org.killbill.billing.invoice.TestInvoiceHelper.DryRunFutureDateArguments;
import org.killbill.billing.invoice.api.DryRunArguments;
import org.killbill.billing.invoice.api.Invoice;
@@ -47,14 +43,11 @@ import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.invoice.api.InvoiceNotifier;
-import org.killbill.billing.invoice.dao.InvoiceItemModelDao;
import org.killbill.billing.invoice.dao.InvoiceModelDao;
import org.killbill.billing.invoice.notification.NullInvoiceNotifier;
import org.killbill.billing.junction.BillingEventSet;
import org.killbill.billing.subscription.api.SubscriptionBase;
import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
-import org.killbill.billing.util.timezone.DateAndTimeZoneContext;
-import org.killbill.clock.ClockMock;
import org.mockito.Mockito;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
@@ -82,7 +75,7 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
final BillingEventSet events = new MockBillingEventSet();
final Plan plan = MockPlan.createBicycleNoTrialEvergreen1USD();
final PlanPhase planPhase = MockPlanPhase.create1USDMonthlyEvergreen();
- final DateTime effectiveDate = new DateTime().minusDays(1);
+ final DateTime effectiveDate = clock.getUTCNow().minusDays(1);
final Currency currency = Currency.USD;
final BigDecimal fixedPrice = null;
events.add(invoiceUtil.createMockBillingEvent(account, subscription, effectiveDate, plan, planPhase,
@@ -91,7 +84,7 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
Mockito.when(billingApi.getBillingEventsForAccountAndUpdateAccountBCD(Mockito.<UUID>any(), Mockito.<DryRunArguments>any(), Mockito.<InternalCallContext>any())).thenReturn(events);
- final DateTime target = new DateTime();
+ final DateTime target = effectiveDate;
final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
final InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountApi, billingApi, subscriptionApi, invoiceDao,
@@ -190,35 +183,4 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
Assert.assertEquals(item.getSubscriptionId(), subscription.getId());
}
}
-
- @Test(groups = "slow")
- public void testCreateNextFutureNotificationDate() throws Exception {
-
- final LocalDate startDate = new LocalDate("2012-10-26");
- final LocalDate endDate = new LocalDate("2012-11-26");
-
- ((ClockMock) clock).setTime(new DateTime(2012, 10, 13, 1, 12, 23, DateTimeZone.UTC));
-
- final DateAndTimeZoneContext dateAndTimeZoneContext = new DateAndTimeZoneContext(clock.getUTCNow(), DateTimeZone.forID("Pacific/Pitcairn"), clock);
-
- final InvoiceItemModelDao item = new InvoiceItemModelDao(UUID.randomUUID(), clock.getUTCNow(), InvoiceItemType.RECURRING, UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(),
- null, "planName", "phaseName", null, startDate, endDate, new BigDecimal("23.9"), new BigDecimal("23.9"), Currency.EUR, null);
-
- final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
- final InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountApi, billingApi, subscriptionApi, invoiceDao,
- internalCallContextFactory, invoiceNotifier, invoicePluginDispatcher, locker, busService.getBus(),
- null, invoiceConfig, clock);
-
- final FutureAccountNotifications futureAccountNotifications = dispatcher.createNextFutureNotificationDate(Collections.singletonList(item), null, dateAndTimeZoneContext, context);
-
- Assert.assertEquals(futureAccountNotifications.getNotifications().size(), 1);
-
- final List<SubscriptionNotification> receivedDates = futureAccountNotifications.getNotifications().get(item.getSubscriptionId());
- Assert.assertEquals(receivedDates.size(), 1);
-
- final LocalDate receivedTargetDate = new LocalDate(receivedDates.get(0).getEffectiveDate(), DateTimeZone.forID("Pacific/Pitcairn"));
- Assert.assertEquals(receivedTargetDate, endDate);
-
- Assert.assertTrue(receivedDates.get(0).getEffectiveDate().compareTo(new DateTime(2012, 11, 27, 1, 12, 23, DateTimeZone.UTC)) <= 0);
- }
}