killbill-aplcache
Changes
beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java 26(+13 -13)
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/notification/DefaultNextBillingDateNotifier.java 3(+0 -3)
invoice/src/main/java/org/killbill/billing/invoice/notification/ParentInvoiceCommitmentNotifier.java 8(+1 -7)
invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java 77(+39 -38)
invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java 34(+17 -17)
junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java 6(+4 -2)
junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java 53(+26 -27)
overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java 29(+13 -16)
overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueAsyncBusNotifier.java 4(+2 -2)
overdue/src/test/java/org/killbill/billing/overdue/applicator/TestOverdueStateApplicator.java 6(+3 -3)
overdue/src/test/java/org/killbill/billing/overdue/notification/TestOverdueCheckNotifier.java 2(+1 -1)
payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java 92(+68 -24)
pom.xml 2(+1 -1)
Details
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
index a01413c..ebf1e53 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
@@ -610,8 +610,8 @@ public class TestOverdueIntegration extends TestOverdueBase {
// Should still be in clear state
checkODState(OverdueWrapper.CLEAR_STATE_NAME);
- // 2012-07-05 => DAY 65 - 35 days after invoice
- addDaysAndCheckForCompletion(20, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT_ERROR);
+ // 2012-06-30 => DAY 65 - 30 days after invoice
+ addDaysAndCheckForCompletion(15, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT_ERROR);
invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 7, 31), callContext);
@@ -627,14 +627,14 @@ public class TestOverdueIntegration extends TestOverdueBase {
checkODState("OD1");
checkChangePlanWithOverdueState(baseEntitlement, true, true);
- // 2012-07-15 => DAY 75 - 45 days after invoice
+ // 2012-07-10 => DAY 75 - 40 days after invoice
addDaysAndCheckForCompletion(8, NextEvent.BLOCK, NextEvent.TAG);
// Should now be in OD2
checkODState("OD2");
checkChangePlanWithOverdueState(baseEntitlement, true, true);
- // 2012-07-25 => DAY 85 - 55 days after invoice
+ // 2012-07-20 => DAY 85 - 50 days after invoice
addDaysAndCheckForCompletion(10, NextEvent.BLOCK);
// Should now be in OD3
@@ -652,26 +652,26 @@ public class TestOverdueIntegration extends TestOverdueBase {
invoiceChecker.checkInvoice(account.getId(), 4, callContext,
// Item for the blocked period
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 15), new LocalDate(2012, 7, 25), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-80.63")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 25), InvoiceItemType.CBA_ADJ, new BigDecimal("80.63")));
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 10), new LocalDate(2012, 7, 20), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-80.63")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 20), new LocalDate(2012, 7, 20), InvoiceItemType.CBA_ADJ, new BigDecimal("80.63")));
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 7, 31), callContext);
- checkChangePlanWithOverdueState(baseEntitlement, false, false);
+ checkChangePlanWithOverdueState(baseEntitlement, false, true);
invoiceChecker.checkInvoice(account.getId(), 4, callContext,
// Item for the blocked period
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 15), new LocalDate(2012, 7, 25), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-80.63")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 25), InvoiceItemType.CBA_ADJ, new BigDecimal("80.63")));
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 10), new LocalDate(2012, 7, 20), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-80.63")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 20), new LocalDate(2012, 7, 20), InvoiceItemType.CBA_ADJ, new BigDecimal("80.63")));
invoiceChecker.checkInvoice(account.getId(), 5, callContext,
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("116.12")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-48.38")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 25), InvoiceItemType.CBA_ADJ, new BigDecimal("-67.74")));
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 20), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("212.89")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 20), new LocalDate(2012, 7, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-88.69")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 20), new LocalDate(2012, 7, 20), InvoiceItemType.CBA_ADJ, new BigDecimal("-80.63")));
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 7, 31), callContext);
- assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(new BigDecimal("-12.89")), 0);
+ assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
}
@Test(groups = "slow", description = "Test overdue for draft external charge")
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
index 3c3473a..a5e1c5c 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
@@ -88,7 +88,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
if (compared2 != 0) {
return compared2;
} else {
- // Default, stable, ordering
+ // Default stable ordering (in the sense that doing twice the same call will lead to same result)
return o1.getId().compareTo(o2.getId());
}
}
@@ -203,8 +203,8 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
final SubscriptionBundle bundle = getSubscriptionBundle(cur.getId(), context);
result.add(bundle);
}
-
- return result;
+ // Sorting by createdDate will likely place the active bundle last, but this is the same ordering we already use for getSubscriptionBundlesForAccount
+ return Ordering.from(SUBSCRIPTION_BUNDLE_COMPARATOR).sortedCopy(result);
}
@Override
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/notification/DefaultNextBillingDateNotifier.java b/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDateNotifier.java
index 3131548..644cec3 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDateNotifier.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDateNotifier.java
@@ -44,7 +44,6 @@ public class DefaultNextBillingDateNotifier implements NextBillingDateNotifier {
public static final String NEXT_BILLING_DATE_NOTIFIER_QUEUE = "next-billing-date-queue";
private final NotificationQueueService notificationQueueService;
- private final InvoiceConfig config;
private final SubscriptionBaseInternalApi subscriptionApi;
private final InvoiceListener listener;
private final InternalCallContextFactory callContextFactory;
@@ -53,12 +52,10 @@ public class DefaultNextBillingDateNotifier implements NextBillingDateNotifier {
@Inject
public DefaultNextBillingDateNotifier(final NotificationQueueService notificationQueueService,
- final InvoiceConfig config,
final SubscriptionBaseInternalApi subscriptionApi,
final InvoiceListener listener,
final InternalCallContextFactory callContextFactory) {
this.notificationQueueService = notificationQueueService;
- this.config = config;
this.subscriptionApi = subscriptionApi;
this.listener = listener;
this.callContextFactory = callContextFactory;
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/notification/ParentInvoiceCommitmentNotifier.java b/invoice/src/main/java/org/killbill/billing/invoice/notification/ParentInvoiceCommitmentNotifier.java
index 9760f46..a59e51b 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/notification/ParentInvoiceCommitmentNotifier.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/notification/ParentInvoiceCommitmentNotifier.java
@@ -42,21 +42,15 @@ public class ParentInvoiceCommitmentNotifier implements NextBillingDateNotifier
public static final String PARENT_INVOICE_COMMITMENT_NOTIFIER_QUEUE = "parent-invoice-commitment-queue";
private final NotificationQueueService notificationQueueService;
- private final InvoiceConfig config;
private final InvoiceListener listener;
- private final InternalCallContextFactory callContextFactory;
private NotificationQueue commitInvoiceQueue;
@Inject
public ParentInvoiceCommitmentNotifier(final NotificationQueueService notificationQueueService,
- final InvoiceConfig config,
- final InvoiceListener listener,
- final InternalCallContextFactory callContextFactory) {
+ final InvoiceListener listener) {
this.notificationQueueService = notificationQueueService;
- this.config = config;
this.listener = listener;
- this.callContextFactory = callContextFactory;
}
@Override
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/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index 3508ed9..2b2c0a4 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -1118,7 +1118,7 @@ public class AccountResource extends JaxRsResourceBase {
@ApiResponse(code = 404, message = "Account not found")})
public Response getTags(@PathParam(ID_PARAM_NAME) final String accountIdString,
@QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
- @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
+ @QueryParam(QUERY_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
@javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException {
final UUID accountId = UUID.fromString(accountIdString);
return super.getTags(accountId, accountId, auditMode, includedDeleted, context.createContext(request));
@@ -1134,7 +1134,7 @@ public class AccountResource extends JaxRsResourceBase {
public Response getAllTags(@PathParam(ID_PARAM_NAME) final String accountIdString,
@QueryParam(QUERY_OBJECT_TYPE) final ObjectType objectType,
@QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
- @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
+ @QueryParam(QUERY_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
@javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException {
final UUID accountId = UUID.fromString(accountIdString);
final TenantContext tenantContext = context.createContext(request);
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java
index f32c0f3..daffae5 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java
@@ -17,6 +17,7 @@
package org.killbill.billing.jaxrs.resources;
import java.net.URI;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -74,6 +75,7 @@ import org.killbill.clock.Clock;
import org.killbill.commons.metrics.TimedResource;
import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Inject;
import com.wordnik.swagger.annotations.Api;
@@ -132,14 +134,26 @@ public class BundleResource extends JaxRsResourceBase {
@ApiOperation(value = "Retrieve a bundle by external key", response = BundleJson.class)
@ApiResponses(value = {@ApiResponse(code = 404, message = "Bundle not found")})
public Response getBundleByKey(@QueryParam(QUERY_EXTERNAL_KEY) final String externalKey,
+ @QueryParam(QUERY_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
@javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, AccountApiException, CatalogApiException {
final TenantContext tenantContext = this.context.createContext(request);
- final SubscriptionBundle bundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey(externalKey, tenantContext);
- final Account account = accountUserApi.getAccountById(bundle.getAccountId(), tenantContext);
- final BundleJson json = new BundleJson(bundle, account.getCurrency(), null);
- return Response.status(Status.OK).entity(json).build();
+ final List<SubscriptionBundle> bundles;
+ if (includedDeleted) {
+ bundles = subscriptionApi.getSubscriptionBundlesForExternalKey(externalKey, tenantContext);
+ } else {
+ final SubscriptionBundle activeBundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey(externalKey, tenantContext);
+ bundles = ImmutableList.of(activeBundle);
+ }
+
+ final List<BundleJson> result = new ArrayList<BundleJson>(bundles.size());
+ for (final SubscriptionBundle bundle : bundles) {
+ final Account account = accountUserApi.getAccountById(bundle.getAccountId(), tenantContext);
+ final BundleJson json = new BundleJson(bundle, account.getCurrency(), null);
+ result.add(json);
+ }
+ return Response.status(Status.OK).entity(result).build();
}
@TimedResource
@@ -333,7 +347,7 @@ public class BundleResource extends JaxRsResourceBase {
@ApiResponse(code = 404, message = "Bundle not found")})
public Response getTags(@PathParam(ID_PARAM_NAME) final String bundleIdString,
@QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
- @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
+ @QueryParam(QUERY_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
@javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException, SubscriptionApiException {
final UUID bundleId = UUID.fromString(bundleIdString);
final TenantContext tenantContext = context.createContext(request);
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
index 74e1588..377dd1c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
@@ -294,7 +294,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
public Response getTags(@PathParam(ID_PARAM_NAME) final String paymentIdString,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
- @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
+ @QueryParam(QUERY_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
@javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException, PaymentApiException {
final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
final UUID paymentId = UUID.fromString(paymentIdString);
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
index 9d6b982..6811d99 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
@@ -925,7 +925,7 @@ public class InvoiceResource extends JaxRsResourceBase {
@ApiResponse(code = 404, message = "Invoice not found")})
public Response getTags(@PathParam(ID_PARAM_NAME) final String invoiceIdString,
@QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
- @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
+ @QueryParam(QUERY_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
@javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException, InvoiceApiException {
final UUID invoiceId = UUID.fromString(invoiceIdString);
final TenantContext tenantContext = context.createContext(request);
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index d66420d..db3dc47 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -104,7 +104,6 @@ public interface JaxrsResource {
public static final String QUERY_TAGS = "tagList";
- public static final String QUERY_TAGS_INCLUDED_DELETED = "includedDeleted";
public static final String QUERY_CUSTOM_FIELDS = "customFieldList";
public static final String QUERY_OBJECT_TYPE = "objectType";
@@ -263,4 +262,7 @@ public interface JaxrsResource {
public static final String BCD = "bcd";
public static final String TRANSFER_CREDIT = "transferCredit";
+ public static final String QUERY_INCLUDED_DELETED = "includedDeleted";
+
+
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
index e770d05..5fb1531 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
@@ -718,7 +718,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
@ApiResponse(code = 404, message = "Subscription not found")})
public Response getTags(@PathParam(ID_PARAM_NAME) final String subscriptionIdString,
@QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
- @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
+ @QueryParam(QUERY_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
@javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException, SubscriptionApiException {
final UUID subscriptionId = UUID.fromString(subscriptionIdString);
final TenantContext tenantContext = context.createContext(request);
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
index 37989fa..f78edf5 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
@@ -98,9 +98,9 @@ public class BlockingCalculator {
*
* @param billingEvents the original list of billing events to update (without overdue events)
*/
- public void insertBlockingEvents(final SortedSet<BillingEvent> billingEvents, final Set<UUID> skippedSubscriptions, final InternalTenantContext context) throws CatalogApiException {
+ public boolean insertBlockingEvents(final SortedSet<BillingEvent> billingEvents, final Set<UUID> skippedSubscriptions, final InternalTenantContext context) throws CatalogApiException {
if (billingEvents.size() <= 0) {
- return;
+ return false;
}
final Hashtable<UUID, List<SubscriptionBase>> bundleMap = createBundleSubscriptionMap(billingEvents);
@@ -146,6 +146,8 @@ public class BlockingCalculator {
for (final BillingEvent eventToRemove : billingEventsToRemove) {
billingEvents.remove(eventToRemove);
}
+
+ return !(billingEventsToAdd.isEmpty() && billingEventsToRemove.isEmpty());
}
final List<BlockingState> getAggregateBlockingEventsPerSubscription(final Iterable<BlockingState> subscriptionBlockingEvents, final Iterable<BlockingState> bundleBlockingEvents, final Iterable<BlockingState> accountBlockingEvents) {
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
index 70cf640..e556f31 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
@@ -70,66 +70,65 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
private static final Logger log = LoggerFactory.getLogger(DefaultInternalBillingApi.class);
private final AccountInternalApi accountApi;
- private final BillCycleDayCalculator bcdCalculator;
private final SubscriptionBaseInternalApi subscriptionApi;
private final CatalogService catalogService;
private final BlockingCalculator blockCalculator;
private final TagInternalApi tagApi;
- private final Clock clock;
@Inject
public DefaultInternalBillingApi(final AccountInternalApi accountApi,
- final BillCycleDayCalculator bcdCalculator,
final SubscriptionBaseInternalApi subscriptionApi,
final BlockingCalculator blockCalculator,
final CatalogService catalogService,
- final TagInternalApi tagApi,
- final Clock clock) {
+ final TagInternalApi tagApi) {
this.accountApi = accountApi;
- this.bcdCalculator = bcdCalculator;
this.subscriptionApi = subscriptionApi;
this.catalogService = catalogService;
this.blockCalculator = blockCalculator;
this.tagApi = tagApi;
- this.clock = clock;
}
@Override
public BillingEventSet getBillingEventsForAccountAndUpdateAccountBCD(final UUID accountId, final DryRunArguments dryRunArguments, final InternalCallContext context) throws CatalogApiException, AccountApiException {
- final List<SubscriptionBaseBundle> bundles = subscriptionApi.getBundlesForAccount(accountId, context);
final StaticCatalog currentCatalog = catalogService.getCurrentCatalog(context);
- final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, context);
- final DefaultBillingEventSet result = new DefaultBillingEventSet(false, currentCatalog.getRecurringBillingMode());
+ // Check to see if billing is off for the account
+ final List<Tag> accountTags = tagApi.getTags(accountId, ObjectType.ACCOUNT, context);
+ final boolean found_AUTO_INVOICING_OFF = is_AUTO_INVOICING_OFF(accountTags);
+ if (found_AUTO_INVOICING_OFF) {
+ return new DefaultBillingEventSet(true, currentCatalog.getRecurringBillingMode()); // billing is off, we are done
+ }
+ final List<SubscriptionBaseBundle> bundles = subscriptionApi.getBundlesForAccount(accountId, context);
+ final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, context);
+ final DefaultBillingEventSet result = new DefaultBillingEventSet(false, currentCatalog.getRecurringBillingMode());
final Set<UUID> skippedSubscriptions = new HashSet<UUID>();
try {
- // Check to see if billing is off for the account
- final List<Tag> accountTags = tagApi.getTags(accountId, ObjectType.ACCOUNT, context);
- final boolean found_AUTO_INVOICING_OFF = is_AUTO_INVOICING_OFF(accountTags);
- if (found_AUTO_INVOICING_OFF) {
- return new DefaultBillingEventSet(true, currentCatalog.getRecurringBillingMode()); // billing is off, we are done
- }
-
addBillingEventsForBundles(bundles, account, dryRunArguments, context, result, skippedSubscriptions);
- } catch (SubscriptionBaseApiException e) {
+ } catch (final SubscriptionBaseApiException e) {
log.warn("Failed while getting BillingEvent", e);
}
+ if (result.isEmpty()) {
+ log.info("No billing event for accountId='{}'", accountId);
+ return result;
+ }
+
// Pretty-print the events, before and after the blocking calculator does its magic
final StringBuilder logStringBuilder = new StringBuilder("Computed billing events for accountId='").append(accountId).append("'");
- eventsToString(logStringBuilder, result, "\nBilling Events Raw");
- blockCalculator.insertBlockingEvents(result, skippedSubscriptions, context);
- eventsToString(logStringBuilder, result, "\nBilling Events After Blocking");
+ eventsToString(logStringBuilder, result);
+ if (blockCalculator.insertBlockingEvents(result, skippedSubscriptions, context)) {
+ logStringBuilder.append("\nBilling Events After Blocking");
+ eventsToString(logStringBuilder, result);
+ }
log.info(logStringBuilder.toString());
return result;
}
- private void eventsToString(final StringBuilder stringBuilder, final SortedSet<BillingEvent> events, final String title) {
- stringBuilder.append(title);
+ private void eventsToString(final StringBuilder stringBuilder, final SortedSet<BillingEvent> events) {
for (final BillingEvent event : events) {
stringBuilder.append("\n").append(event.toString());
}
@@ -185,8 +184,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
// If dryRun is specified, we don't want to to update the account BCD value, so we initialize the flag updatedAccountBCD to true
boolean updatedAccountBCD = dryRunMode;
-
- int currentAccountBCD = accountApi.getBCD(account.getId(), context);
+ final int currentAccountBCD = accountApi.getBCD(account.getId(), context);
for (final SubscriptionBase subscription : subscriptions) {
// The subscription did not even start, so there is nothing to do yet, we can skip and avoid some NPE down the line when calculating the BCD
@@ -218,6 +216,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
calculateBcdForTransition(catalog, baseSubscription, subscription, account, currentAccountBCD, transition);
if (currentAccountBCD == 0 && !updatedAccountBCD) {
+ log.info("Setting account BCD='{}', accountId='{}'", bcdLocal, account.getId());
accountApi.updateBCD(account.getExternalKey(), bcdLocal, context);
updatedAccountBCD = true;
}
@@ -231,7 +230,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
private int calculateBcdForTransition(final Catalog catalog, final SubscriptionBase baseSubscription, final SubscriptionBase subscription, final ImmutableAccountData account, final int accountBillCycleDayLocal, final EffectiveSubscriptionInternalEvent transition)
throws CatalogApiException, AccountApiException, SubscriptionBaseApiException {
final BillingAlignment alignment = catalog.billingAlignment(getPlanPhaseSpecifierFromTransition(catalog, transition), transition.getEffectiveTransitionTime());
- return bcdCalculator.calculateBcdForAlignment(subscription, baseSubscription, alignment, account.getTimeZone(), accountBillCycleDayLocal);
+ return BillCycleDayCalculator.calculateBcdForAlignment(subscription, baseSubscription, alignment, account.getTimeZone(), accountBillCycleDayLocal);
}
private PlanPhaseSpecifier getPlanPhaseSpecifierFromTransition(final Catalog catalog, final EffectiveSubscriptionInternalEvent transition) throws CatalogApiException {
@@ -255,7 +254,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
phase.getPhaseType());
}
- private final boolean is_AUTO_INVOICING_OFF(final List<Tag> tags) {
+ private boolean is_AUTO_INVOICING_OFF(final List<Tag> tags) {
return ControlTagType.isAutoInvoicingOff(Collections2.transform(tags, new Function<Tag, UUID>() {
@Nullable
@Override
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java b/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
index 22d0865..cf2ea4f 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
@@ -86,7 +86,6 @@ public class OverdueStateApplicator {
private static final Logger log = LoggerFactory.getLogger(OverdueStateApplicator.class);
private final BlockingInternalApi blockingApi;
- private final Clock clock;
private final OverduePoster checkPoster;
private final PersistentBus bus;
private final AccountInternalApi accountApi;
@@ -102,7 +101,6 @@ public class OverdueStateApplicator {
final AccountInternalApi accountApi,
final EntitlementApi entitlementApi,
final EntitlementInternalApi entitlementInternalApi,
- final Clock clock,
@Named(DefaultOverdueModule.OVERDUE_NOTIFIER_CHECK_NAMED) final OverduePoster checkPoster,
final OverdueEmailGenerator overdueEmailGenerator,
final EmailConfig config,
@@ -114,7 +112,6 @@ public class OverdueStateApplicator {
this.accountApi = accountApi;
this.entitlementApi = entitlementApi;
this.entitlementInternalApi = entitlementInternalApi;
- this.clock = clock;
this.checkPoster = checkPoster;
this.overdueEmailGenerator = overdueEmailGenerator;
this.tagApi = tagApi;
@@ -123,7 +120,7 @@ public class OverdueStateApplicator {
this.bus = bus;
}
- public void apply(final OverdueStateSet overdueStateSet, final BillingState billingState,
+ public void apply(final DateTime effectiveDate, final OverdueStateSet overdueStateSet, final BillingState billingState,
final ImmutableAccountData account, final OverdueState previousOverdueState,
final OverdueState nextOverdueState, final InternalCallContext context) throws OverdueException, OverdueApiException {
try {
@@ -132,7 +129,7 @@ public class OverdueStateApplicator {
return;
}
- log.debug("OverdueStateApplicator: time={}, previousState={}, nextState={}, billingState={}", clock.getUTCNow(), previousOverdueState, nextOverdueState, billingState);
+ log.debug("OverdueStateApplicator: time={}, previousState={}, nextState={}, billingState={}", effectiveDate, previousOverdueState, nextOverdueState, billingState);
final OverdueState firstOverdueState = overdueStateSet.getFirstState();
final boolean conditionForNextNotfication = !nextOverdueState.isClearState() ||
@@ -145,8 +142,8 @@ public class OverdueStateApplicator {
if (reevaluationInterval == null) {
log.debug("OverdueStateApplicator <notificationQ>: missing InitialReevaluationInterval from config, NOT inserting notification for account {}", account.getId());
} else {
- log.debug("OverdueStateApplicator <notificationQ>: inserting notification for account={}, time={}", account.getId(), clock.getUTCNow().plus(reevaluationInterval));
- createFutureNotification(account, clock.getUTCNow().plus(reevaluationInterval), context);
+ log.debug("OverdueStateApplicator <notificationQ>: inserting notification for account={}, time={}", account.getId(), effectiveDate.plus(reevaluationInterval));
+ createFutureNotification(account, effectiveDate.plus(reevaluationInterval), context);
}
} else if (nextOverdueState.isClearState()) {
clearFutureNotification(account, context);
@@ -157,7 +154,7 @@ public class OverdueStateApplicator {
return;
}
- cancelSubscriptionsIfRequired(account, nextOverdueState, context);
+ cancelSubscriptionsIfRequired(effectiveDate, account, nextOverdueState, context);
sendEmailIfRequired(account.getId(), billingState, nextOverdueState, context);
@@ -166,7 +163,7 @@ public class OverdueStateApplicator {
// Make sure to store the new state last here: the entitlement DAO will send a BlockingTransitionInternalEvent
// on the bus to which invoice will react. We need the latest state (including AUTO_INVOICE_OFF tag for example)
// to be present in the database first.
- storeNewState(account, nextOverdueState, context);
+ storeNewState(effectiveDate, account, nextOverdueState, context);
} catch (final AccountApiException e) {
throw new OverdueException(e);
}
@@ -212,11 +209,11 @@ public class OverdueStateApplicator {
}
}
- public void clear(final ImmutableAccountData account, final OverdueState previousOverdueState, final OverdueState clearState, final InternalCallContext context) throws OverdueException {
+ public void clear(final DateTime effectiveDate, final ImmutableAccountData account, final OverdueState previousOverdueState, final OverdueState clearState, final InternalCallContext context) throws OverdueException {
- log.debug("OverdueStateApplicator:clear : time = " + clock.getUTCNow() + ", previousState = " + previousOverdueState.getName());
+ log.debug("OverdueStateApplicator:clear : time = " + effectiveDate + ", previousState = " + previousOverdueState.getName());
- storeNewState(account, clearState, context);
+ storeNewState(effectiveDate, account, clearState, context);
clearFutureNotification(account, context);
@@ -248,7 +245,7 @@ public class OverdueStateApplicator {
context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
}
- protected void storeNewState(final ImmutableAccountData blockable, final OverdueState nextOverdueState, final InternalCallContext context) throws OverdueException {
+ protected void storeNewState(final DateTime effectiveDate, final ImmutableAccountData blockable, final OverdueState nextOverdueState, final InternalCallContext context) throws OverdueException {
try {
blockingApi.setBlockingState(new DefaultBlockingState(blockable.getId(),
BlockingStateType.ACCOUNT,
@@ -257,7 +254,7 @@ public class OverdueStateApplicator {
blockChanges(nextOverdueState),
blockEntitlement(nextOverdueState),
blockBilling(nextOverdueState),
- clock.getUTCNow()),
+ effectiveDate),
context);
} catch (final Exception e) {
throw new OverdueException(e, ErrorCode.OVERDUE_CAT_ERROR_ENCOUNTERED, blockable.getId(), blockable.getClass().getName());
@@ -312,7 +309,7 @@ public class OverdueStateApplicator {
checkPoster.clearOverdueCheckNotifications(account.getId(), OverdueCheckNotifier.OVERDUE_CHECK_NOTIFIER_QUEUE, OverdueCheckNotificationKey.class, context);
}
- private void cancelSubscriptionsIfRequired(final ImmutableAccountData account, final OverdueState nextOverdueState, final InternalCallContext context) throws OverdueException {
+ private void cancelSubscriptionsIfRequired(final DateTime effectiveDate, final ImmutableAccountData account, final OverdueState nextOverdueState, final InternalCallContext context) throws OverdueException {
if (nextOverdueState.getOverdueCancellationPolicy() == OverdueCancellationPolicy.NONE) {
return;
}
@@ -334,7 +331,7 @@ public class OverdueStateApplicator {
computeEntitlementsToCancel(account, toBeCancelled, callContext);
try {
- entitlementInternalApi.cancel(toBeCancelled, clock.getToday(account.getTimeZone()), actionPolicy, ImmutableList.<PluginProperty>of(), context);
+ entitlementInternalApi.cancel(toBeCancelled, context.toLocalDate(effectiveDate), actionPolicy, ImmutableList.<PluginProperty>of(), context);
} catch (final EntitlementApiException e) {
throw new OverdueException(e);
}
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueDispatcher.java b/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueDispatcher.java
index c631b72..461539e 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueDispatcher.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueDispatcher.java
@@ -18,6 +18,7 @@ package org.killbill.billing.overdue.listener;
import java.util.UUID;
+import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,25 +39,25 @@ public class OverdueDispatcher {
this.factory = factory;
}
- public void processOverdueForAccount(final UUID accountId, final InternalCallContext context) {
- processOverdue(accountId, context);
+ public void processOverdueForAccount(final UUID accountId, final DateTime effectiveDate, final InternalCallContext context) {
+ processOverdue(accountId, effectiveDate, context);
}
- public void clearOverdueForAccount(final UUID accountId, final InternalCallContext context) {
- clearOverdue(accountId, context);
+ public void clearOverdueForAccount(final UUID accountId, final DateTime effectiveDate, final InternalCallContext context) {
+ clearOverdue(accountId, effectiveDate, context);
}
- private void processOverdue(final UUID accountId, final InternalCallContext context) {
+ private void processOverdue(final UUID accountId, final DateTime effectiveDate, final InternalCallContext context) {
try {
- factory.createOverdueWrapperFor(accountId, context).refresh(context);
+ factory.createOverdueWrapperFor(accountId, context).refresh(effectiveDate, context);
} catch (BillingExceptionBase e) {
log.warn("Error processing Overdue for accountId='{}'", accountId, e);
}
}
- private void clearOverdue(final UUID accountId, final InternalCallContext context) {
+ private void clearOverdue(final UUID accountId, final DateTime effectiveDate, final InternalCallContext context) {
try {
- factory.createOverdueWrapperFor(accountId, context).clear(context);
+ factory.createOverdueWrapperFor(accountId, context).clear(effectiveDate, context);
} catch (BillingExceptionBase e) {
log.warn("Error processing Overdue for accountId='{}'", accountId, e);
}
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueAsyncBusNotifier.java b/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueAsyncBusNotifier.java
index be98671..4d90ec1 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueAsyncBusNotifier.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueAsyncBusNotifier.java
@@ -61,10 +61,10 @@ public class OverdueAsyncBusNotifier extends DefaultOverdueNotifierBase implemen
final OverdueAsyncBusNotificationKey key = (OverdueAsyncBusNotificationKey) notificationKey;
switch (key.getAction()) {
case CLEAR:
- dispatcher.clearOverdueForAccount(key.getUuidKey(), createCallContext(userToken, accountRecordId, tenantRecordId));
+ dispatcher.clearOverdueForAccount(key.getUuidKey(), eventDate, createCallContext(userToken, accountRecordId, tenantRecordId));
break;
case REFRESH:
- dispatcher.processOverdueForAccount(key.getUuidKey(), createCallContext(userToken, accountRecordId, tenantRecordId));
+ dispatcher.processOverdueForAccount(key.getUuidKey(), eventDate, createCallContext(userToken, accountRecordId, tenantRecordId));
break;
default:
throw new RuntimeException("Unexpected action " + key.getAction() + " for account " + key.getUuidKey());
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueCheckNotifier.java b/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueCheckNotifier.java
index bd644dd..6fdb77d 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueCheckNotifier.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueCheckNotifier.java
@@ -59,7 +59,7 @@ public class OverdueCheckNotifier extends DefaultOverdueNotifierBase implements
}
final OverdueCheckNotificationKey key = (OverdueCheckNotificationKey) notificationKey;
- dispatcher.processOverdueForAccount(key.getUuidKey(), createCallContext(userToken, accountRecordId, tenantRecordId));
+ dispatcher.processOverdueForAccount(key.getUuidKey(), eventDate, createCallContext(userToken, accountRecordId, tenantRecordId));
} catch (IllegalArgumentException e) {
log.error("The key returned from the NextBillingNotificationQueue is not a valid UUID", e);
}
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java b/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java
index 46ed578..48ddfbb 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java
@@ -20,6 +20,7 @@ package org.killbill.billing.overdue.wrapper;
import java.util.List;
+import org.joda.time.DateTime;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.account.api.ImmutableAccountData;
@@ -85,7 +86,7 @@ public class OverdueWrapper {
this.accountApi = accountApi;
}
- public OverdueState refresh(final InternalCallContext context) throws OverdueException, OverdueApiException {
+ public OverdueState refresh(final DateTime effectiveDate, final InternalCallContext context) throws OverdueException, OverdueApiException {
if (overdueStateSet.size() < 1) { // No configuration available
return overdueStateSet.getClearState();
}
@@ -94,7 +95,7 @@ public class OverdueWrapper {
try {
lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), overdueable.getId().toString(), MAX_LOCK_RETRIES);
- return refreshWithLock(context);
+ return refreshWithLock(effectiveDate, context);
} catch (final LockFailedException e) {
log.warn("Failed to process overdue for accountId='{}'", overdueable.getId(), e);
} finally {
@@ -105,14 +106,14 @@ public class OverdueWrapper {
return null;
}
- private OverdueState refreshWithLock(final InternalCallContext context) throws OverdueException, OverdueApiException {
+ private OverdueState refreshWithLock(final DateTime effectiveDate, final InternalCallContext context) throws OverdueException, OverdueApiException {
final BillingState billingState = billingState(context);
final BlockingState blockingStateForService = api.getBlockingStateForService(overdueable.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, context);
final String previousOverdueStateName = blockingStateForService != null ? blockingStateForService.getStateName() : OverdueWrapper.CLEAR_STATE_NAME;
final OverdueState currentOverdueState = overdueStateSet.findState(previousOverdueStateName);
final OverdueState nextOverdueState = overdueStateSet.calculateOverdueState(billingState, clock.getToday(billingState.getAccountTimeZone()));
- overdueStateApplicator.apply(overdueStateSet, billingState, overdueable, currentOverdueState, nextOverdueState, context);
+ overdueStateApplicator.apply(effectiveDate, overdueStateSet, billingState, overdueable, currentOverdueState, nextOverdueState, context);
try {
final List<Account> childrenAccounts = accountApi.getChildrenAccounts(overdueable.getId(), context);
@@ -133,7 +134,7 @@ public class OverdueWrapper {
billingState.getIdOfEarliestUnpaidInvoice(),
billingState.getResponseForLastFailedPayment(),
billingState.getTags());
- overdueStateApplicator.apply(overdueStateSet, childBillingState, accountData, currentOverdueState, nextOverdueState, accountContext);
+ overdueStateApplicator.apply(effectiveDate, overdueStateSet, childBillingState, accountData, currentOverdueState, nextOverdueState, accountContext);
}
}
@@ -144,12 +145,12 @@ public class OverdueWrapper {
return nextOverdueState;
}
- public void clear(final InternalCallContext context) throws OverdueException, OverdueApiException {
+ public void clear(final DateTime effectiveDate, final InternalCallContext context) throws OverdueException, OverdueApiException {
GlobalLock lock = null;
try {
lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), overdueable.getId().toString(), MAX_LOCK_RETRIES);
- clearWithLock(context);
+ clearWithLock(effectiveDate, context);
} catch (final LockFailedException e) {
log.warn("Failed to clear overdue for accountId='{}'", overdueable.getId(), e);
} finally {
@@ -159,13 +160,13 @@ public class OverdueWrapper {
}
}
- private void clearWithLock(final InternalCallContext context) throws OverdueException, OverdueApiException {
+ private void clearWithLock(final DateTime effectiveDate, final InternalCallContext context) throws OverdueException, OverdueApiException {
final BlockingState blockingStateForService = api.getBlockingStateForService(overdueable.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, context);
final String previousOverdueStateName = blockingStateForService != null ? blockingStateForService.getStateName() : OverdueWrapper.CLEAR_STATE_NAME;
final OverdueState previousOverdueState = overdueStateSet.findState(previousOverdueStateName);
- overdueStateApplicator.clear(overdueable, previousOverdueState, overdueStateSet.getClearState(), context);
// TODO maguero: should we do the same as "refreshWithLock"?
+ overdueStateApplicator.clear(effectiveDate, overdueable, previousOverdueState, overdueStateSet.getClearState(), context);
}
public BillingState billingState(final InternalTenantContext context) throws OverdueException {
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/applicator/TestOverdueStateApplicator.java b/overdue/src/test/java/org/killbill/billing/overdue/applicator/TestOverdueStateApplicator.java
index 2222377..42e70ce 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/applicator/TestOverdueStateApplicator.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/applicator/TestOverdueStateApplicator.java
@@ -54,17 +54,17 @@ public class TestOverdueStateApplicator extends OverdueTestSuiteWithEmbeddedDB {
OverdueState state;
state = config.getOverdueStatesAccount().findState("OD1");
- applicator.apply(overdueStateSet, null, account, clearState, state, internalCallContext);
+ applicator.apply(clock.getUTCNow(), overdueStateSet, null, account, clearState, state, internalCallContext);
testOverdueHelper.checkStateApplied(state);
checkBussEvent("OD1");
state = config.getOverdueStatesAccount().findState("OD2");
- applicator.apply(overdueStateSet, null, account, clearState, state, internalCallContext);
+ applicator.apply(clock.getUTCNow(), overdueStateSet, null, account, clearState, state, internalCallContext);
testOverdueHelper.checkStateApplied(state);
checkBussEvent("OD2");
state = config.getOverdueStatesAccount().findState("OD3");
- applicator.apply(overdueStateSet, null, account, clearState, state, internalCallContext);
+ applicator.apply(clock.getUTCNow(), overdueStateSet, null, account, clearState, state, internalCallContext);
testOverdueHelper.checkStateApplied(state);
checkBussEvent("OD3");
}
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/notification/TestOverdueCheckNotifier.java b/overdue/src/test/java/org/killbill/billing/overdue/notification/TestOverdueCheckNotifier.java
index b9da2b6..ee470c5 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/notification/TestOverdueCheckNotifier.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/notification/TestOverdueCheckNotifier.java
@@ -53,7 +53,7 @@ public class TestOverdueCheckNotifier extends OverdueTestSuiteWithEmbeddedDB {
}
@Override
- public void processOverdueForAccount(final UUID accountId, final InternalCallContext context) {
+ public void processOverdueForAccount(final UUID accountId, final DateTime effectiveDate, final InternalCallContext context) {
eventCount++;
latestAccountId = accountId;
}
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/wrapper/TestOverdueWrapper.java b/overdue/src/test/java/org/killbill/billing/overdue/wrapper/TestOverdueWrapper.java
index 2c22056..f495016 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/wrapper/TestOverdueWrapper.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/wrapper/TestOverdueWrapper.java
@@ -51,19 +51,19 @@ public class TestOverdueWrapper extends OverdueTestSuiteWithEmbeddedDB {
state = config.getOverdueStatesAccount().findState("OD1");
account = testOverdueHelper.createImmutableAccountData(clock.getUTCToday().minusDays(31));
wrapper = overdueWrapperFactory.createOverdueWrapperFor(account, internalCallContext);
- wrapper.refresh(internalCallContext);
+ wrapper.refresh(clock.getUTCNow(), internalCallContext);
testOverdueHelper.checkStateApplied(state);
state = config.getOverdueStatesAccount().findState("OD2");
account = testOverdueHelper.createImmutableAccountData(clock.getUTCToday().minusDays(41));
wrapper = overdueWrapperFactory.createOverdueWrapperFor(account, internalCallContext);
- wrapper.refresh(internalCallContext);
+ wrapper.refresh(clock.getUTCNow(), internalCallContext);
testOverdueHelper.checkStateApplied(state);
state = config.getOverdueStatesAccount().findState("OD3");
account = testOverdueHelper.createImmutableAccountData(clock.getUTCToday().minusDays(51));
wrapper = overdueWrapperFactory.createOverdueWrapperFor(account, internalCallContext);
- wrapper.refresh(internalCallContext);
+ wrapper.refresh(clock.getUTCNow(), internalCallContext);
testOverdueHelper.checkStateApplied(state);
}
@@ -79,7 +79,7 @@ public class TestOverdueWrapper extends OverdueTestSuiteWithEmbeddedDB {
state = config.getOverdueStatesAccount().findState(OverdueWrapper.CLEAR_STATE_NAME);
account = testOverdueHelper.createImmutableAccountData(clock.getUTCToday().minusDays(31));
wrapper = overdueWrapperFactory.createOverdueWrapperFor(account, internalCallContext);
- final OverdueState result = wrapper.refresh(internalCallContext);
+ final OverdueState result = wrapper.refresh(clock.getUTCNow(), internalCallContext);
Assert.assertEquals(result.getName(), state.getName());
Assert.assertEquals(result.isBlockChanges(), state.isBlockChanges());
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
index 5bfd18d..22f3a2a 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
@@ -47,9 +47,10 @@ import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
-public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
+import static org.killbill.billing.payment.logging.PaymentLoggingHelper.logEnterAPICall;
+import static org.killbill.billing.payment.logging.PaymentLoggingHelper.logExitAPICall;
- private static final Joiner JOINER = Joiner.on(",");
+public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
private static final boolean SHOULD_LOCK_ACCOUNT = true;
private static final boolean IS_API_PAYMENT = true;
@@ -84,7 +85,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
Payment payment = null;
PaymentTransaction paymentTransaction = null;
try {
- logEnterAPICall(transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, null);
+ logEnterAPICall(log, transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, null);
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = paymentProcessor.createAuthorization(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
@@ -94,7 +95,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
return payment;
} finally {
- logExitAPICall(transactionType,
+ logExitAPICall(log,
+ transactionType,
account,
payment != null ? payment.getPaymentMethodId() : null,
payment != null ? payment.getId() : null,
@@ -130,7 +132,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
Payment payment = null;
PaymentTransaction paymentTransaction = null;
try {
- logEnterAPICall(transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, paymentControlPluginNames);
+ logEnterAPICall(log, transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, paymentControlPluginNames);
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = pluginControlPaymentProcessor.createAuthorization(IS_API_PAYMENT, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
@@ -140,7 +142,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
return payment;
} finally {
- logExitAPICall(transactionType,
+ logExitAPICall(log,
+ transactionType,
account,
payment != null ? payment.getPaymentMethodId() : null,
payment != null ? payment.getId() : null,
@@ -168,7 +171,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
Payment payment = null;
PaymentTransaction paymentTransaction = null;
try {
- logEnterAPICall(transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, null);
+ logEnterAPICall(log, transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, null);
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = paymentProcessor.createCapture(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, amount, currency, paymentTransactionExternalKey,
@@ -178,7 +181,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
return payment;
} finally {
- logExitAPICall(transactionType,
+ logExitAPICall(log,
+ transactionType,
account,
payment != null ? payment.getPaymentMethodId() : null,
payment != null ? payment.getId() : null,
@@ -210,7 +214,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
Payment payment = null;
PaymentTransaction paymentTransaction = null;
try {
- logEnterAPICall(transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
+ logEnterAPICall(log, transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = pluginControlPaymentProcessor.createCapture(IS_API_PAYMENT, account, paymentId, amount, currency, paymentTransactionExternalKey,
@@ -220,7 +224,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
return payment;
} finally {
- logExitAPICall(transactionType,
+ logExitAPICall(log,
+ transactionType,
account,
payment != null ? payment.getPaymentMethodId() : null,
payment != null ? payment.getId() : null,
@@ -250,7 +255,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
Payment payment = null;
PaymentTransaction paymentTransaction = null;
try {
- logEnterAPICall(transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, null);
+ logEnterAPICall(log, transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, null);
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = paymentProcessor.createPurchase(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
@@ -260,7 +265,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
return payment;
} finally {
- logExitAPICall(transactionType,
+ logExitAPICall(log,
+ transactionType,
account,
payment != null ? payment.getPaymentMethodId() : null,
payment != null ? payment.getId() : null,
@@ -303,7 +309,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
PaymentTransaction paymentTransaction = null;
try {
- logEnterAPICall(transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, paymentControlPluginNames);
+ logEnterAPICall(log, transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, paymentControlPluginNames);
payment = pluginControlPaymentProcessor.createPurchase(IS_API_PAYMENT, account, nonNulPaymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
properties, paymentControlPluginNames, callContext, internalCallContext);
@@ -312,7 +318,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
return payment;
} finally {
- logExitAPICall(transactionType,
+ logExitAPICall(log,
+ transactionType,
account,
payment != null ? payment.getPaymentMethodId() : null,
payment != null ? payment.getId() : null,
@@ -338,7 +345,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
Payment payment = null;
PaymentTransaction paymentTransaction = null;
try {
- logEnterAPICall(transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, null);
+ logEnterAPICall(log, transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, null);
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = paymentProcessor.createVoid(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey,
@@ -348,7 +355,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
return payment;
} finally {
- logExitAPICall(transactionType,
+ logExitAPICall(log,
+ transactionType,
account,
payment != null ? payment.getPaymentMethodId() : null,
payment != null ? payment.getId() : null,
@@ -378,7 +386,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
Payment payment = null;
PaymentTransaction paymentTransaction = null;
try {
- logEnterAPICall(transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
+ logEnterAPICall(log, transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = pluginControlPaymentProcessor.createVoid(IS_API_PAYMENT, account, paymentId, paymentTransactionExternalKey,
@@ -388,7 +396,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
return payment;
} finally {
- logExitAPICall(transactionType,
+ logExitAPICall(log,
+ transactionType,
account,
payment != null ? payment.getPaymentMethodId() : null,
payment != null ? payment.getId() : null,
@@ -416,7 +425,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
Payment payment = null;
PaymentTransaction paymentTransaction = null;
try {
- logEnterAPICall(transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, null);
+ logEnterAPICall(log, transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, null);
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = paymentProcessor.createRefund(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, amount, currency, paymentTransactionExternalKey,
@@ -426,7 +435,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
return payment;
} finally {
- logExitAPICall(transactionType,
+ logExitAPICall(log,
+ transactionType,
account,
payment != null ? payment.getPaymentMethodId() : null,
payment != null ? payment.getId() : null,
@@ -460,7 +470,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
Payment payment = null;
PaymentTransaction paymentTransaction = null;
try {
- logEnterAPICall(transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
+ logEnterAPICall(log, transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = pluginControlPaymentProcessor.createRefund(IS_API_PAYMENT, account, paymentId, amount, currency, paymentTransactionExternalKey,
@@ -470,7 +480,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
return payment;
} finally {
- logExitAPICall(transactionType,
+ logExitAPICall(log,
+ transactionType,
account,
payment != null ? payment.getPaymentMethodId() : null,
payment != null ? payment.getId() : null,
@@ -500,7 +511,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
Payment payment = null;
PaymentTransaction paymentTransaction = null;
try {
- logEnterAPICall(transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, null);
+ logEnterAPICall(log, transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, null);
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = paymentProcessor.createCredit(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
@@ -510,7 +521,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
return payment;
} finally {
- logExitAPICall(transactionType,
+ logExitAPICall(log,
+ transactionType,
account,
payment != null ? payment.getPaymentMethodId() : null,
payment != null ? payment.getId() : null,
@@ -545,7 +557,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
Payment payment = null;
PaymentTransaction paymentTransaction = null;
try {
- logEnterAPICall(transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, paymentControlPluginNames);
+ logEnterAPICall(log, transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, paymentControlPluginNames);
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = pluginControlPaymentProcessor.createCredit(IS_API_PAYMENT, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
@@ -555,7 +567,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
return payment;
} finally {
- logExitAPICall(transactionType,
+ logExitAPICall(log,
+ transactionType,
account,
payment != null ? payment.getPaymentMethodId() : null,
payment != null ? payment.getId() : null,
@@ -578,7 +591,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
Payment payment = null;
PaymentTransaction paymentTransaction = null;
try {
- logEnterAPICall(transactionType, account, null, null, paymentTransactionId, null, null, null, null, null, null);
+ logEnterAPICall(log, transactionType, account, null, null, paymentTransactionId, null, null, null, null, null, null);
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = paymentProcessor.notifyPendingPaymentOfStateChanged(account, paymentTransactionId, isSuccess, callContext, internalCallContext);
@@ -592,7 +605,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
}).orNull();
return payment;
} finally {
- logExitAPICall(transactionType,
+ logExitAPICall(log,
+ transactionType,
account,
payment != null ? payment.getPaymentMethodId() : null,
payment != null ? payment.getId() : null,
@@ -622,7 +636,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
Payment payment = null;
PaymentTransaction paymentTransaction = null;
try {
- logEnterAPICall(transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, null);
+ logEnterAPICall(log, transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, null);
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = paymentProcessor.createChargeback(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey, amount, currency, true,
@@ -632,7 +646,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
return payment;
} finally {
- logExitAPICall(transactionType,
+ logExitAPICall(log,
+ transactionType,
account,
payment != null ? payment.getPaymentMethodId() : null,
payment != null ? payment.getId() : null,
@@ -662,7 +677,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
Payment payment = null;
PaymentTransaction paymentTransaction = null;
try {
- logEnterAPICall(transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
+ logEnterAPICall(log, transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = pluginControlPaymentProcessor.createChargeback(IS_API_PAYMENT, account, paymentId, paymentTransactionExternalKey, amount, currency,
@@ -672,7 +687,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
return payment;
} finally {
- logExitAPICall(transactionType,
+ logExitAPICall(log,
+ transactionType,
account,
payment != null ? payment.getPaymentMethodId() : null,
payment != null ? payment.getId() : null,
@@ -696,7 +712,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
Payment payment = null;
PaymentTransaction paymentTransaction = null;
try {
- logEnterAPICall(transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, null);
+ logEnterAPICall(log, transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, null);
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = paymentProcessor.createChargebackReversal(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey, null, null, true, callContext, internalCallContext);
@@ -705,7 +721,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
return payment;
} finally {
- logExitAPICall(transactionType,
+ logExitAPICall(log,
+ transactionType,
account,
payment != null ? payment.getPaymentMethodId() : null,
payment != null ? payment.getId() : null,
@@ -734,7 +751,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
Payment payment = null;
PaymentTransaction paymentTransaction = null;
try {
- logEnterAPICall(transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
+ logEnterAPICall(log, transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = pluginControlPaymentProcessor.createChargebackReversal(IS_API_PAYMENT, account, paymentId, paymentTransactionExternalKey, paymentControlPluginNames, callContext, internalCallContext);
@@ -750,7 +767,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
return payment;
} finally {
- logExitAPICall(transactionType,
+ logExitAPICall(log,
+ transactionType,
account,
payment != null ? payment.getPaymentMethodId() : null,
payment != null ? payment.getId() : null,
@@ -900,122 +918,4 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
});
}
}
-
- private void logEnterAPICall(final String transactionType,
- final Account account,
- @Nullable final UUID paymentMethodId,
- @Nullable final UUID paymentId,
- @Nullable final UUID transactionId,
- @Nullable final BigDecimal amount,
- @Nullable final Currency currency,
- @Nullable final String paymentExternalKey,
- @Nullable final String paymentTransactionExternalKey,
- @Nullable final TransactionStatus transactionStatus,
- @Nullable final List<String> paymentControlPluginNames) {
- logAPICallInternal("ENTERING ",
- transactionType,
- account,
- paymentMethodId,
- paymentId,
- transactionId,
- amount,
- currency,
- paymentExternalKey,
- paymentTransactionExternalKey,
- transactionStatus,
- paymentControlPluginNames);
- }
-
- private void logExitAPICall(final String transactionType,
- final Account account,
- @Nullable final UUID paymentMethodId,
- @Nullable final UUID paymentId,
- @Nullable final UUID transactionId,
- @Nullable final BigDecimal amount,
- @Nullable final Currency currency,
- @Nullable final String paymentExternalKey,
- @Nullable final String paymentTransactionExternalKey,
- @Nullable final TransactionStatus transactionStatus,
- @Nullable final List<String> paymentControlPluginNames) {
- logAPICallInternal("EXITING ",
- transactionType,
- account,
- paymentMethodId,
- paymentId,
- transactionId,
- amount,
- currency,
- paymentExternalKey,
- paymentTransactionExternalKey,
- transactionStatus,
- paymentControlPluginNames);
- }
-
- private void logAPICallInternal(final String prefixMsg,
- final String transactionType,
- final Account account,
- final UUID paymentMethodId,
- @Nullable final UUID paymentId,
- @Nullable final UUID transactionId,
- @Nullable final BigDecimal amount,
- @Nullable final Currency currency,
- @Nullable final String paymentExternalKey,
- @Nullable final String paymentTransactionExternalKey,
- @Nullable final TransactionStatus transactionStatus,
- @Nullable final List<String> paymentControlPluginNames) {
- if (log.isInfoEnabled()) {
- final StringBuilder logLine = new StringBuilder(prefixMsg);
- logLine.append("PaymentApi: transactionType='")
- .append(transactionType)
- .append("', accountId='")
- .append(account.getId())
- .append("'");
- if (paymentMethodId != null) {
- logLine.append(", paymentMethodId='")
- .append(paymentMethodId)
- .append("'");
- }
- if (paymentExternalKey != null) {
- logLine.append(", paymentExternalKey='")
- .append(paymentExternalKey)
- .append("'");
- }
- if (paymentTransactionExternalKey != null) {
- logLine.append(", paymentTransactionExternalKey='")
- .append(paymentTransactionExternalKey)
- .append("'");
- }
- if (paymentId != null) {
- logLine.append(", paymentId='")
- .append(paymentId)
- .append("'");
- }
- if (transactionId != null) {
- logLine.append(", transactionId='")
- .append(transactionId)
- .append("'");
- }
- if (amount != null) {
- logLine.append(", amount='")
- .append(amount)
- .append("'");
- }
- if (currency != null) {
- logLine.append(", currency='")
- .append(currency)
- .append("'");
- }
- if (transactionStatus != null) {
- logLine.append(", transactionStatus='")
- .append(transactionStatus)
- .append("'");
- }
- if (paymentControlPluginNames != null) {
- logLine.append(", paymentControlPluginNames='")
- .append(JOINER.join(paymentControlPluginNames))
- .append("'");
- }
- log.info(logLine.toString());
- }
- }
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java b/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java
index 145507a..fd9cebe 100644
--- a/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java
+++ b/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * 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
@@ -20,6 +20,7 @@ package org.killbill.billing.payment.bus;
import java.math.BigDecimal;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
@@ -30,8 +31,11 @@ import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.events.InvoiceCreationInternalEvent;
import org.killbill.billing.events.PaymentInternalEvent;
+import org.killbill.billing.payment.api.Payment;
import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.api.PaymentTransaction;
import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.payment.api.TransactionType;
import org.killbill.billing.payment.core.PluginControlPaymentProcessor;
import org.killbill.billing.payment.core.janitor.Janitor;
import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
@@ -44,10 +48,16 @@ import org.killbill.billing.util.config.definition.PaymentConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject;
+import static org.killbill.billing.payment.logging.PaymentLoggingHelper.logEnterAPICall;
+import static org.killbill.billing.payment.logging.PaymentLoggingHelper.logExitAPICall;
+
public class PaymentBusEventHandler {
private static final Logger log = LoggerFactory.getLogger(PaymentBusEventHandler.class);
@@ -82,22 +92,49 @@ public class PaymentBusEventHandler {
public void processInvoiceEvent(final InvoiceCreationInternalEvent event) {
log.info("Received invoice creation notification for accountId='{}', invoiceId='{}'", event.getAccountId(), event.getInvoiceId());
- final Account account;
- try {
- final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "PaymentRequestProcessor", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
- account = accountApi.getAccountById(event.getAccountId(), internalContext);
+ final Collection<PluginProperty> properties = new ArrayList<PluginProperty>();
+ final PluginProperty propertyInvoiceId = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, event.getInvoiceId().toString(), false);
+ properties.add(propertyInvoiceId);
+
+ final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "PaymentRequestProcessor", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
+ final CallContext callContext = internalCallContextFactory.createCallContext(internalContext);
- final List<PluginProperty> properties = new ArrayList<PluginProperty>();
- final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, event.getInvoiceId().toString(), false);
- properties.add(prop1);
+ final BigDecimal amountToBePaid = null; // We let the plugin compute how much should be paid
+ final List<String> paymentControlPluginNames = paymentConfig.getPaymentControlPluginNames(internalContext) != null ? new LinkedList<String>(paymentConfig.getPaymentControlPluginNames(internalContext)) : new LinkedList<String>();
+ paymentControlPluginNames.add(InvoicePaymentControlPluginApi.PLUGIN_NAME);
- final CallContext callContext = internalCallContextFactory.createCallContext(internalContext);
+ final String paymentExternalKey = UUIDs.randomUUID().toString();
+ final String paymentTransactionExternalKey = UUIDs.randomUUID().toString();
+
+ final String transactionType = TransactionType.PURCHASE.name();
+ Account account = null;
+ Payment payment = null;
+ PaymentTransaction paymentTransaction = null;
+ try {
+ account = accountApi.getAccountById(event.getAccountId(), internalContext);
- final BigDecimal amountToBePaid = null; // We let the plugin compute how much should be paid
- final List<String> paymentControlPluginNames = paymentConfig.getPaymentControlPluginNames(internalContext) != null ? new LinkedList<String>(paymentConfig.getPaymentControlPluginNames(internalContext)) : new LinkedList<String>();
- paymentControlPluginNames.add(InvoicePaymentControlPluginApi.PLUGIN_NAME);
- pluginControlPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amountToBePaid, account.getCurrency(), UUIDs.randomUUID().toString(), UUIDs.randomUUID().toString(),
- properties, paymentControlPluginNames, callContext, internalContext);
+ logEnterAPICall(log,
+ transactionType,
+ account,
+ account.getPaymentMethodId(),
+ null,
+ null,
+ amountToBePaid,
+ account.getCurrency(),
+ paymentExternalKey,
+ paymentTransactionExternalKey,
+ null,
+ paymentControlPluginNames);
+
+ payment = pluginControlPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amountToBePaid, account.getCurrency(), paymentExternalKey, paymentTransactionExternalKey, properties, paymentControlPluginNames, callContext, internalContext);
+
+ paymentTransaction = Iterables.<PaymentTransaction>find(Lists.<PaymentTransaction>reverse(payment.getTransactions()),
+ new Predicate<PaymentTransaction>() {
+ @Override
+ public boolean apply(final PaymentTransaction input) {
+ return paymentTransactionExternalKey.equals(input.getExternalKey());
+ }
+ });
} catch (final AccountApiException e) {
log.warn("Failed to process invoice payment", e);
} catch (final PaymentApiException e) {
@@ -105,6 +142,19 @@ public class PaymentBusEventHandler {
if (e.getCode() != ErrorCode.PAYMENT_PLUGIN_API_ABORTED.getCode()) {
log.warn("Failed to process invoice payment {}", e.toString());
}
+ } finally {
+ logExitAPICall(log,
+ transactionType,
+ account,
+ payment != null ? payment.getPaymentMethodId() : null,
+ payment != null ? payment.getId() : null,
+ paymentTransaction != null ? paymentTransaction.getId() : null,
+ paymentTransaction != null ? paymentTransaction.getProcessedAmount() : null,
+ paymentTransaction != null ? paymentTransaction.getProcessedCurrency() : null,
+ payment != null ? payment.getExternalKey() : null,
+ paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
+ paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
+ paymentControlPluginNames);
}
}
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
index cdb152d..30456c4 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
@@ -108,25 +108,6 @@ public class Janitor {
}
- /*
- public IncompletePaymentAttemptTask(final InternalCallContextFactory internalCallContextFactory,
- final PaymentConfig paymentConfig,
- final PaymentDao paymentDao,
- final Clock clock,
- final PaymentStateMachineHelper paymentStateMachineHelper,
- final PaymentControlStateMachineHelper retrySMHelper,
- final AccountInternalApi accountInternalApi,
- final PluginControlPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner,
- final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
- final GlobalLocker locker) {
-
-
- public IncompletePaymentTransactionTask(final InternalCallContextFactory internalCallContextFactory, final PaymentConfig paymentConfig,
- final PaymentDao paymentDao, final Clock clock,
- final PaymentStateMachineHelper paymentStateMachineHelper, final PaymentControlStateMachineHelper retrySMHelper, final AccountInternalApi accountInternalApi,
- final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry, final GlobalLocker locker) {
-
- */
public void initialize() throws NotificationQueueAlreadyExists {
janitorQueue = notificationQueueService.createNotificationQueue(DefaultPaymentService.SERVICE_NAME,
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java
index 9ace343..b1dffa6 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java
@@ -36,6 +36,7 @@ import org.killbill.billing.invoice.api.InvoiceInternalApi;
import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.Payment;
import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.api.PaymentTransaction;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.payment.api.TransactionType;
import org.killbill.billing.payment.core.sm.PaymentControlStateMachineHelper;
@@ -56,7 +57,13 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import static org.killbill.billing.payment.logging.PaymentLoggingHelper.logEnterAPICall;
+import static org.killbill.billing.payment.logging.PaymentLoggingHelper.logExitAPICall;
public class PluginControlPaymentProcessor extends ProcessorBase {
@@ -225,36 +232,60 @@ public class PluginControlPaymentProcessor extends ProcessorBase {
}
public void retryPaymentTransaction(final UUID attemptId, final List<String> paymentControlPluginNames, final InternalCallContext internalCallContext) {
- try {
- final PaymentAttemptModelDao attempt = paymentDao.getPaymentAttempt(attemptId, internalCallContext);
- final PaymentModelDao payment = paymentDao.getPaymentByExternalKey(attempt.getPaymentExternalKey(), internalCallContext);
- final UUID paymentId = payment != null ? payment.getId() : null;
+ final PaymentAttemptModelDao attempt = paymentDao.getPaymentAttempt(attemptId, internalCallContext);
+ log.info("Retrying attemptId='{}', paymentExternalKey='{}', transactionExternalKey='{}'. paymentControlPluginNames='{}'",
+ attemptId, attempt.getPaymentExternalKey(), attempt.getTransactionExternalKey(), paymentControlPluginNames);
- final Iterable<PluginProperty> pluginProperties = PluginPropertySerializer.deserialize(attempt.getPluginProperties());
- final Account account = accountInternalApi.getAccountById(attempt.getAccountId(), internalCallContext);
- final CallContext callContext = buildCallContext(internalCallContext);
+ final PaymentModelDao paymentModelDao = paymentDao.getPaymentByExternalKey(attempt.getPaymentExternalKey(), internalCallContext);
+ final UUID paymentId = paymentModelDao != null ? paymentModelDao.getId() : null;
+ final CallContext callContext = buildCallContext(internalCallContext);
+
+ final String transactionType = TransactionType.PURCHASE.name();
+ Account account = null;
+ Payment payment = null;
+ PaymentTransaction paymentTransaction = null;
+ try {
+ account = accountInternalApi.getAccountById(attempt.getAccountId(), internalCallContext);
final State state = paymentControlStateMachineHelper.getState(attempt.getStateName());
+ final Iterable<PluginProperty> pluginProperties = PluginPropertySerializer.deserialize(attempt.getPluginProperties());
- log.debug("Retrying attemptId={}, paymentExternalKey={}, transactionExternalKey={}. paymentControlPluginNames={}, now={}",
- attemptId, attempt.getPaymentExternalKey(), attempt.getTransactionExternalKey(), paymentControlPluginNames, clock.getUTCNow());
+ logEnterAPICall(log,
+ transactionType,
+ account,
+ attempt.getPaymentMethodId(),
+ paymentId,
+ null,
+ attempt.getAmount(),
+ attempt.getCurrency(),
+ attempt.getPaymentExternalKey(),
+ attempt.getTransactionExternalKey(),
+ null,
+ paymentControlPluginNames);
- pluginControlledPaymentAutomatonRunner.run(state,
- false,
- attempt.getTransactionType(),
- ControlOperation.valueOf(attempt.getTransactionType().toString()),
- account,
- attempt.getPaymentMethodId(),
- paymentId,
- attempt.getPaymentExternalKey(),
- attempt.getTransactionExternalKey(),
- attempt.getAmount(),
- attempt.getCurrency(),
- pluginProperties,
- paymentControlPluginNames,
- callContext,
- internalCallContext);
+ payment = pluginControlledPaymentAutomatonRunner.run(state,
+ false,
+ attempt.getTransactionType(),
+ ControlOperation.valueOf(attempt.getTransactionType().toString()),
+ account,
+ attempt.getPaymentMethodId(),
+ paymentId,
+ attempt.getPaymentExternalKey(),
+ attempt.getTransactionExternalKey(),
+ attempt.getAmount(),
+ attempt.getCurrency(),
+ pluginProperties,
+ paymentControlPluginNames,
+ callContext,
+ internalCallContext);
+ paymentTransaction = Iterables.<PaymentTransaction>find(Lists.<PaymentTransaction>reverse(payment.getTransactions()),
+ new Predicate<PaymentTransaction>() {
+ @Override
+ public boolean apply(final PaymentTransaction input) {
+ return attempt.getTransactionExternalKey().equals(input.getExternalKey());
+ }
+ });
} catch (final AccountApiException e) {
log.warn("Failed to retry attemptId='{}', paymentControlPlugins='{}'", attemptId, toPluginNamesOnError(paymentControlPluginNames), e);
} catch (final PaymentApiException e) {
@@ -263,6 +294,19 @@ public class PluginControlPaymentProcessor extends ProcessorBase {
log.warn("Failed to retry attemptId='{}', paymentControlPlugins='{}'", attemptId, toPluginNamesOnError(paymentControlPluginNames), e);
} catch (final MissingEntryException e) {
log.warn("Failed to retry attemptId='{}', paymentControlPlugins='{}'", attemptId, toPluginNamesOnError(paymentControlPluginNames), e);
+ } finally {
+ logExitAPICall(log,
+ transactionType,
+ account,
+ payment != null ? payment.getPaymentMethodId() : null,
+ payment != null ? payment.getId() : null,
+ paymentTransaction != null ? paymentTransaction.getId() : null,
+ paymentTransaction != null ? paymentTransaction.getProcessedAmount() : null,
+ paymentTransaction != null ? paymentTransaction.getProcessedCurrency() : null,
+ payment != null ? payment.getExternalKey() : null,
+ paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
+ paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
+ paymentControlPluginNames);
}
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/logging/PaymentLoggingHelper.java b/payment/src/main/java/org/killbill/billing/payment/logging/PaymentLoggingHelper.java
new file mode 100644
index 0000000..c3ac9c2
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/logging/PaymentLoggingHelper.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2016 Groupon, Inc
+ * Copyright 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
+ * 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.payment.logging;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.payment.api.TransactionStatus;
+import org.slf4j.Logger;
+
+import com.google.common.base.Joiner;
+
+public abstract class PaymentLoggingHelper {
+
+ private static final Joiner JOINER = Joiner.on(",");
+
+ public static void logEnterAPICall(final Logger log,
+ final String transactionType,
+ final Account account,
+ @Nullable final UUID paymentMethodId,
+ @Nullable final UUID paymentId,
+ @Nullable final UUID transactionId,
+ @Nullable final BigDecimal amount,
+ @Nullable final Currency currency,
+ @Nullable final String paymentExternalKey,
+ @Nullable final String paymentTransactionExternalKey,
+ @Nullable final TransactionStatus transactionStatus,
+ @Nullable final List<String> paymentControlPluginNames) {
+ logAPICallInternal(log,
+ "ENTERING ",
+ transactionType,
+ account,
+ paymentMethodId,
+ paymentId,
+ transactionId,
+ amount,
+ currency,
+ paymentExternalKey,
+ paymentTransactionExternalKey,
+ transactionStatus,
+ paymentControlPluginNames);
+ }
+
+ public static void logExitAPICall(final Logger log,
+ final String transactionType,
+ final Account account,
+ @Nullable final UUID paymentMethodId,
+ @Nullable final UUID paymentId,
+ @Nullable final UUID transactionId,
+ @Nullable final BigDecimal amount,
+ @Nullable final Currency currency,
+ @Nullable final String paymentExternalKey,
+ @Nullable final String paymentTransactionExternalKey,
+ @Nullable final TransactionStatus transactionStatus,
+ @Nullable final List<String> paymentControlPluginNames) {
+ logAPICallInternal(log,
+ "EXITING ",
+ transactionType,
+ account,
+ paymentMethodId,
+ paymentId,
+ transactionId,
+ amount,
+ currency,
+ paymentExternalKey,
+ paymentTransactionExternalKey,
+ transactionStatus,
+ paymentControlPluginNames);
+ }
+
+ public static void logAPICallInternal(final Logger log,
+ final String prefixMsg,
+ final String transactionType,
+ final Account account,
+ final UUID paymentMethodId,
+ @Nullable final UUID paymentId,
+ @Nullable final UUID transactionId,
+ @Nullable final BigDecimal amount,
+ @Nullable final Currency currency,
+ @Nullable final String paymentExternalKey,
+ @Nullable final String paymentTransactionExternalKey,
+ @Nullable final TransactionStatus transactionStatus,
+ @Nullable final List<String> paymentControlPluginNames) {
+ if (log.isInfoEnabled()) {
+ final StringBuilder logLine = new StringBuilder(prefixMsg);
+ logLine.append("PaymentApi: transactionType='")
+ .append(transactionType)
+ .append("', accountId='")
+ .append(account.getId())
+ .append("'");
+ if (paymentMethodId != null) {
+ logLine.append(", paymentMethodId='")
+ .append(paymentMethodId)
+ .append("'");
+ }
+ if (paymentExternalKey != null) {
+ logLine.append(", paymentExternalKey='")
+ .append(paymentExternalKey)
+ .append("'");
+ }
+ if (paymentTransactionExternalKey != null) {
+ logLine.append(", paymentTransactionExternalKey='")
+ .append(paymentTransactionExternalKey)
+ .append("'");
+ }
+ if (paymentId != null) {
+ logLine.append(", paymentId='")
+ .append(paymentId)
+ .append("'");
+ }
+ if (transactionId != null) {
+ logLine.append(", transactionId='")
+ .append(transactionId)
+ .append("'");
+ }
+ if (amount != null) {
+ logLine.append(", amount='")
+ .append(amount)
+ .append("'");
+ }
+ if (currency != null) {
+ logLine.append(", currency='")
+ .append(currency)
+ .append("'");
+ }
+ if (transactionStatus != null) {
+ logLine.append(", transactionStatus='")
+ .append(transactionStatus)
+ .append("'");
+ }
+ if (paymentControlPluginNames != null) {
+ logLine.append(", paymentControlPluginNames='")
+ .append(JOINER.join(paymentControlPluginNames))
+ .append("'");
+ }
+ log.info(logLine.toString());
+ }
+ }
+}
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java b/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java
index 0c47d84..772235b 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java
@@ -122,7 +122,8 @@ public class TestPaymentHelper {
final InvoiceCreationInternalEvent event = new MockInvoiceCreationEvent(invoice.getId(), invoice.getAccountId(),
invoice.getBalance(), invoice.getCurrency(),
- invoice.getInvoiceDate(), 1L, 2L, null);
+ invoice.getInvoiceDate(), internalCallContext.getAccountRecordId(),
+ internalCallContext.getTenantRecordId(), null);
eventBus.post(event);
return invoice;
pom.xml 2(+1 -1)
diff --git a/pom.xml b/pom.xml
index ca27368..d1cf442 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>killbill-oss-parent</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.109</version>
+ <version>0.111</version>
</parent>
<artifactId>killbill</artifactId>
<version>0.17.1-SNAPSHOT</version>
diff --git a/profiles/killbill/src/main/resources/logback.xml b/profiles/killbill/src/main/resources/logback.xml
index f2cdef8..c2c710a 100644
--- a/profiles/killbill/src/main/resources/logback.xml
+++ b/profiles/killbill/src/main/resources/logback.xml
@@ -43,7 +43,7 @@
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
- <pattern>%date [%thread] %maskedMsg%n</pattern>
+ <pattern>%date{"yyyy-MM-dd'T'HH:mm:ss,SSSZ", UTC} lvl='%level', log='%logger{0}', th='%thread', xff='%X{req.xForwardedFor}', rId='%X{req.requestId}', aRId='%X{kb.accountRecordId}', tRId='%X{kb.tenantRecordId}', %maskedMsg%n</pattern>
</encoder>
</appender>
</sift>
@@ -64,7 +64,7 @@
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
- <pattern>%date [%thread] %maskedMsg%n</pattern>
+ <pattern>%date{"yyyy-MM-dd'T'HH:mm:ss,SSSZ", UTC} lvl='%level', log='%logger{0}', th='%thread', xff='%X{req.xForwardedFor}', rId='%X{req.requestId}', aRId='%X{kb.accountRecordId}', tRId='%X{kb.tenantRecordId}', %maskedMsg%n</pattern>
</encoder>
</appender>
</sift>
@@ -85,7 +85,7 @@
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
- <pattern>%date [%thread] %maskedMsg%n</pattern>
+ <pattern>%date{"yyyy-MM-dd'T'HH:mm:ss,SSSZ", UTC} lvl='%level', log='%logger{0}', th='%thread', xff='%X{req.xForwardedFor}', rId='%X{req.requestId}', aRId='%X{kb.accountRecordId}', tRId='%X{kb.tenantRecordId}', %maskedMsg%n</pattern>
</encoder>
</appender>
</sift>
@@ -106,7 +106,7 @@
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
- <pattern>%date [%thread] %maskedMsg%n</pattern>
+ <pattern>%date{"yyyy-MM-dd'T'HH:mm:ss,SSSZ", UTC} lvl='%level', log='%logger{0}', th='%thread', xff='%X{req.xForwardedFor}', rId='%X{req.requestId}', aRId='%X{kb.accountRecordId}', tRId='%X{kb.tenantRecordId}', %maskedMsg%n</pattern>
</encoder>
</appender>
</sift>
@@ -127,7 +127,7 @@
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
- <pattern>%date [%thread] %maskedMsg%n</pattern>
+ <pattern>%date{"yyyy-MM-dd'T'HH:mm:ss,SSSZ", UTC} lvl='%level', log='%logger{0}', th='%thread', xff='%X{req.xForwardedFor}', rId='%X{req.requestId}', aRId='%X{kb.accountRecordId}', tRId='%X{kb.tenantRecordId}', %maskedMsg%n</pattern>
</encoder>
</appender>
</sift>
@@ -148,7 +148,7 @@
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
- <pattern>%date [%thread] %maskedMsg%n</pattern>
+ <pattern>%date{"yyyy-MM-dd'T'HH:mm:ss,SSSZ", UTC} lvl='%level', log='%logger{0}', th='%thread', xff='%X{req.xForwardedFor}', rId='%X{req.requestId}', aRId='%X{kb.accountRecordId}', tRId='%X{kb.tenantRecordId}', %maskedMsg%n</pattern>
</encoder>
</appender>
</sift>
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBundle.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBundle.java
index 33e7df6..a33bea7 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBundle.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBundle.java
@@ -78,9 +78,17 @@ public class TestBundle extends TestJaxrsBase {
public void testBundleNonExistent() throws Exception {
final Account accountJson = createAccount();
- Assert.assertNull(killBillClient.getBundle(UUID.randomUUID()));
- Assert.assertTrue(killBillClient.getAccountBundles(accountJson.getAccountId(), "98374982743892").isEmpty());
- Assert.assertTrue(killBillClient.getAccountBundles(accountJson.getAccountId()).isEmpty());
+ // ID
+ Assert.assertNull(killBillClient.getBundle(UUID.randomUUID(), requestOptions));
+
+ // External Key
+ Assert.assertNull(killBillClient.getBundle(UUID.randomUUID().toString(), requestOptions));
+ Assert.assertTrue(killBillClient.getAllBundlesForExternalKey(UUID.randomUUID().toString(), requestOptions).isEmpty());
+
+ // Account Id
+ Assert.assertTrue(killBillClient.getAccountBundles(accountJson.getAccountId(), "98374982743892", requestOptions).isEmpty());
+ Assert.assertTrue(killBillClient.getAccountBundles(accountJson.getAccountId(), requestOptions).isEmpty());
+
}
@Test(groups = "slow", description = "Can handle non existent account")
@@ -102,7 +110,7 @@ public class TestBundle extends TestJaxrsBase {
final Subscription entitlementJsonNoEvents = createEntitlement(accountJson.getAccountId(), bundleExternalKey, productName,
ProductCategory.BASE, term, true);
- final Bundle originalBundle = killBillClient.getBundle(bundleExternalKey);
+ final Bundle originalBundle = killBillClient.getBundle(bundleExternalKey, requestOptions);
assertEquals(originalBundle.getAccountId(), accountJson.getAccountId());
assertEquals(originalBundle.getExternalKey(), bundleExternalKey);
@@ -113,10 +121,18 @@ public class TestBundle extends TestJaxrsBase {
bundle.setBundleId(entitlementJsonNoEvents.getBundleId());
assertEquals(killBillClient.transferBundle(bundle, createdBy, reason, comment).getAccountId(), newAccount.getAccountId());
- final Bundle newBundle = killBillClient.getBundle(bundleExternalKey);
+ final Bundle newBundle = killBillClient.getBundle(bundleExternalKey, requestOptions);
assertNotEquals(newBundle.getBundleId(), originalBundle.getBundleId());
assertEquals(newBundle.getExternalKey(), originalBundle.getExternalKey());
assertEquals(newBundle.getAccountId(), newAccount.getAccountId());
+
+
+ final Bundles bundles = killBillClient.getAllBundlesForExternalKey(bundleExternalKey, requestOptions);
+ assertEquals(bundles.size(), 2);
+ assertEquals(bundles.get(0).getBundleId(), originalBundle.getBundleId());
+ assertEquals(bundles.get(0).getSubscriptions().get(0).getState(), EntitlementState.CANCELLED);
+ assertEquals(bundles.get(1).getBundleId(), newBundle.getBundleId());
+ assertEquals(bundles.get(1).getSubscriptions().get(0).getState(), EntitlementState.ACTIVE);
}
@Test(groups = "slow", description = "Block a bundle")
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPerTenantConfig.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPerTenantConfig.java
index 9736a04..a180f70 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPerTenantConfig.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPerTenantConfig.java
@@ -18,6 +18,8 @@
package org.killbill.billing.jaxrs;
import java.util.HashMap;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
import org.killbill.billing.client.model.Account;
import org.killbill.billing.client.model.Payments;
@@ -34,6 +36,8 @@ import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.google.inject.Inject;
+import com.jayway.awaitility.Awaitility;
+import com.jayway.awaitility.Duration;
public class TestPerTenantConfig extends TestJaxrsBase {
@@ -55,7 +59,6 @@ public class TestPerTenantConfig extends TestJaxrsBase {
@Test(groups = "slow")
public void testFailedPaymentWithPerTenantRetryConfig() throws Exception {
-
// Create the tenant
final String apiKeyTenant1 = "tenantSuperTuned";
final String apiSecretTenant1 = "2367$$ffr79";
@@ -66,7 +69,7 @@ public class TestPerTenantConfig extends TestJaxrsBase {
killBillClient.createTenant(tenant1, createdBy, reason, comment);
// Configure our plugin to fail
- mockPaymentProviderPlugin.makeNextPaymentFailWithError();
+ mockPaymentProviderPlugin.makeAllInvoicesFailWithError(true);
// Upload the config
final ObjectMapper mapper = new ObjectMapper();
@@ -86,10 +89,6 @@ public class TestPerTenantConfig extends TestJaxrsBase {
// Because we have specified a retry interval of one day we should see the new attempt after moving clock 1 day (and not 8 days which is default)
//
- // Configure our plugin to fail AGAIN
- mockPaymentProviderPlugin.makeNextPaymentFailWithError();
-
-
//
// Now unregister special per tenant config and we the first retry occurs one day after (and still fails), it now sets a retry date of 8 days
//
@@ -97,17 +96,24 @@ public class TestPerTenantConfig extends TestJaxrsBase {
// org.killbill.tenant.broadcast.rate has been set to 1s
crappyWaitForLackOfProperSynchonization(2000);
-
clock.addDays(1);
- crappyWaitForLackOfProperSynchonization(3000);
+ Awaitility.await()
+ .atMost(4, TimeUnit.SECONDS)
+ .pollInterval(Duration.ONE_SECOND)
+ .until(new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+
+ return killBillClient.getPaymentsForAccount(accountJson.getAccountId()).get(0).getTransactions().size() == 2;
+ }
+ });
final Payments payments2 = killBillClient.getPaymentsForAccount(accountJson.getAccountId());
Assert.assertEquals(payments2.size(), 1);
Assert.assertEquals(payments2.get(0).getTransactions().size(), 2);
Assert.assertEquals(payments2.get(0).getTransactions().get(0).getStatus(), TransactionStatus.PAYMENT_FAILURE.name());
Assert.assertEquals(payments2.get(0).getTransactions().get(1).getStatus(), TransactionStatus.PAYMENT_FAILURE.name());
-
clock.addDays(1);
crappyWaitForLackOfProperSynchonization(3000);
@@ -116,9 +122,18 @@ public class TestPerTenantConfig extends TestJaxrsBase {
Assert.assertEquals(payments3.size(), 1);
Assert.assertEquals(payments3.get(0).getTransactions().size(), 2);
+ mockPaymentProviderPlugin.makeAllInvoicesFailWithError(false);
clock.addDays(7);
- crappyWaitForLackOfProperSynchonization(3000);
+ Awaitility.await()
+ .atMost(4, TimeUnit.SECONDS)
+ .pollInterval(Duration.ONE_SECOND)
+ .until(new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ return killBillClient.getPaymentsForAccount(accountJson.getAccountId()).get(0).getTransactions().size() == 3;
+ }
+ });
final Payments payments4 = killBillClient.getPaymentsForAccount(accountJson.getAccountId());
Assert.assertEquals(payments4.size(), 1);
Assert.assertEquals(payments4.get(0).getTransactions().size(), 3);
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java
index 6b157c2..820c07f 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java
@@ -182,8 +182,7 @@ public class DefaultSubscriptionBaseService implements EventListener, Subscripti
private boolean onPhaseEvent(final DefaultSubscriptionBase subscription, final SubscriptionBaseEvent readyPhaseEvent, final InternalCallContext context) {
try {
- final DateTime now = clock.getUTCNow();
- final TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, now, context);
+ final TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, readyPhaseEvent.getEffectiveDate(), context);
final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
PhaseEventData.createNextPhaseEvent(subscription.getId(),
nextTimedPhase.getPhase().getName(), nextTimedPhase.getStartPhase()) :
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();
+ }
}
diff --git a/util/src/main/java/org/killbill/billing/util/bcd/BillCycleDayCalculator.java b/util/src/main/java/org/killbill/billing/util/bcd/BillCycleDayCalculator.java
index a451153..3ab047c 100644
--- a/util/src/main/java/org/killbill/billing/util/bcd/BillCycleDayCalculator.java
+++ b/util/src/main/java/org/killbill/billing/util/bcd/BillCycleDayCalculator.java
@@ -25,15 +25,10 @@ import org.killbill.clock.ClockUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.annotations.VisibleForTesting;
-
-public class BillCycleDayCalculator {
+public abstract class BillCycleDayCalculator {
private static final Logger log = LoggerFactory.getLogger(BillCycleDayCalculator.class);
- public BillCycleDayCalculator() {
- }
-
public static int calculateBcdForAlignment(final SubscriptionBase subscription, final SubscriptionBase baseSubscription, final BillingAlignment alignment, final DateTimeZone accountTimeZone, final int accountBillCycleDayLocal) {
int result = 0;
switch (alignment) {
@@ -50,12 +45,11 @@ public class BillCycleDayCalculator {
return result;
}
- @VisibleForTesting
- static int calculateBcdFromSubscription(final SubscriptionBase subscription, final DateTimeZone accountTimeZone) {
+ private static int calculateBcdFromSubscription(final SubscriptionBase subscription, final DateTimeZone accountTimeZone) {
final DateTime date = subscription.getDateOfFirstRecurringNonZeroCharge();
final int bcdLocal = ClockUtil.toDateTime(date, accountTimeZone).getDayOfMonth();
- log.info("Calculated BCD: subscriptionId='{}', subscriptionStartDate='{}', accountTimeZone='{}', bcd='{}'",
- subscription.getId(), date.toDateTimeISO(), accountTimeZone, bcdLocal);
+ log.debug("Calculated BCD: subscriptionId='{}', subscriptionStartDate='{}', accountTimeZone='{}', bcd='{}'",
+ subscription.getId(), date.toDateTimeISO(), accountTimeZone, bcdLocal);
return bcdLocal;
}
}