killbill-aplcache
Changes
account/pom.xml 2(+1 -1)
api/pom.xml 2(+1 -1)
beatrix/pom.xml 2(+1 -1)
catalog/pom.xml 2(+1 -1)
currency/pom.xml 2(+1 -1)
entitlement/pom.xml 2(+1 -1)
invoice/pom.xml 2(+1 -1)
invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java 83(+77 -6)
jaxrs/pom.xml 2(+1 -1)
junction/pom.xml 2(+1 -1)
NEWS 3(+3 -0)
overdue/pom.xml 2(+1 -1)
payment/pom.xml 2(+1 -1)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.java 10(+8 -2)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.java 8(+4 -4)
pom.xml 2(+1 -1)
profiles/killbill/pom.xml 2(+1 -1)
profiles/killpay/pom.xml 2(+1 -1)
profiles/pom.xml 2(+1 -1)
subscription/pom.xml 2(+1 -1)
tenant/pom.xml 2(+1 -1)
usage/pom.xml 2(+1 -1)
util/pom.xml 2(+1 -1)
Details
account/pom.xml 2(+1 -1)
diff --git a/account/pom.xml b/account/pom.xml
index b876bcd..a90876a 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-account</artifactId>
api/pom.xml 2(+1 -1)
diff --git a/api/pom.xml b/api/pom.xml
index ef10494..3dc5a1e 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-internal-api</artifactId>
beatrix/pom.xml 2(+1 -1)
diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 0a1f312..3b3064f 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-beatrix</artifactId>
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
index b79776b..37fa59a 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
@@ -663,4 +663,73 @@ public class TestIntegration extends TestIntegrationBase {
checkNoMoreInvoiceToGenerate(account);
}
+
+ @Test(groups = "slow")
+ public void testFixedTermPlan() throws Exception {
+ // We take april as it has 30 days (easier to play with BCD)
+ // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+ clock.setDay(new LocalDate(2015, 4, 1));
+
+ final AccountData accountData = getAccountData(1);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+
+ // First invoice
+ final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, /* NextEvent.BLOCK, */ NextEvent.INVOICE);
+
+
+ // Second invoice -> first recurring for Refurbish-Maintenance
+ addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Refurbish-Maintenance", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, /* NextEvent.BLOCK, */ NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+ final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("599.95")));
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 4, 1), new LocalDate(2015, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("199.95")));
+ invoiceChecker.checkInvoice(account.getId(), 2, callContext, expectedInvoices);
+ expectedInvoices.clear();
+
+
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 5, 1), new LocalDate(2015, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("199.95")));
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 5, 1), new LocalDate(2015, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+
+ // 2015-5-1
+ // Third invoice -> second recurring for Refurbish-Maintenance
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ clock.addDays(30); // Also = 1 month because or initial date 2015, 4, 1
+ assertListenerStatus();
+
+ invoiceChecker.checkInvoice(account.getId(), 3, callContext, expectedInvoices);
+ expectedInvoices.clear();
+
+
+ // Next 10 invoices -> third to twelve **and last** recurring for Refurbish-Maintenance
+ LocalDate startDate = new LocalDate(2015, 6, 1);
+ for (int i = 0; i < 10; i++) {
+
+ final LocalDate endDate = startDate.plusMonths(1);
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(startDate, endDate, InvoiceItemType.RECURRING, new BigDecimal("199.95")));
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(startDate, endDate, InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+
+ busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ invoiceChecker.checkInvoice(account.getId(), 4 + i, callContext, expectedInvoices);
+ expectedInvoices.clear();
+
+ startDate = endDate;
+ }
+
+ // We check there is no more recurring for Refurbish-Maintenance
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 4, 1), new LocalDate(2016, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+
+ busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+
+ invoiceChecker.checkInvoice(account.getId(), 14, callContext, expectedInvoices);
+ expectedInvoices.clear();
+ }
}
catalog/pom.xml 2(+1 -1)
diff --git a/catalog/pom.xml b/catalog/pom.xml
index e75310f..92e9692 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-catalog</artifactId>
diff --git a/catalog/src/test/resources/catalogTest.xml b/catalog/src/test/resources/catalogTest.xml
index 56576c4..4b1ee25 100644
--- a/catalog/src/test/resources/catalogTest.xml
+++ b/catalog/src/test/resources/catalogTest.xml
@@ -50,6 +50,7 @@
<available>
<addonProduct>Cleaning</addonProduct>
<addonProduct>Bullets</addonProduct>
+ <addonProduct>Refurbish-Maintenance</addonProduct>
</available>
</product>
<product name="Shotgun">
currency/pom.xml 2(+1 -1)
diff --git a/currency/pom.xml b/currency/pom.xml
index 049a773..748b65e 100644
--- a/currency/pom.xml
+++ b/currency/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-currency</artifactId>
entitlement/pom.xml 2(+1 -1)
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index 83f81aa..0537b95 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-entitlement</artifactId>
invoice/pom.xml 2(+1 -1)
diff --git a/invoice/pom.xml b/invoice/pom.xml
index 4614397..cd544ac 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-invoice</artifactId>
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 e48bd18..1ac8d6a 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
@@ -33,9 +33,12 @@ import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.catalog.api.BillingMode;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.Duration;
+import org.killbill.billing.catalog.api.PhaseType;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.invoice.generator.InvoiceWithMetadata.SubscriptionFutureNotificationDates;
import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
import org.killbill.billing.invoice.model.InvalidDateSequenceException;
@@ -50,6 +53,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
import com.google.inject.Inject;
import static org.killbill.billing.invoice.generator.InvoiceDateUtils.calculateNumberOfWholeBillingPeriods;
@@ -83,9 +88,8 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
// Generate list of proposed invoice items based on billing events from junction-- proposed items are ALL items since beginning of time
final List<InvoiceItem> proposedItems = new ArrayList<InvoiceItem>();
- processRecurringBillingEvents(invoiceId, account.getId(), eventSet, targetDate, targetCurrency, proposedItems, perSubscriptionFutureNotificationDate, internalCallContext);
+ processRecurringBillingEvents(invoiceId, account.getId(), eventSet, targetDate, targetCurrency, proposedItems, perSubscriptionFutureNotificationDate, existingInvoices, internalCallContext);
processFixedBillingEvents(invoiceId, account.getId(), eventSet, targetDate, targetCurrency, proposedItems, internalCallContext);
-
accountItemTree.mergeWithProposedItems(proposedItems);
return accountItemTree.getResultingItemList();
}
@@ -93,6 +97,7 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
private void processRecurringBillingEvents(final UUID invoiceId, final UUID accountId, final BillingEventSet events,
final LocalDate targetDate, final Currency currency, final List<InvoiceItem> proposedItems,
final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate,
+ @Nullable final List<Invoice> existingInvoices,
final InternalCallContext internalCallContext) throws InvoiceApiException {
if (events.size() == 0) {
return;
@@ -112,11 +117,12 @@ 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, logStringBuilder, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, existingInvoices, 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, logStringBuilder, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, existingInvoices, internalCallContext);
+
proposedItems.addAll(newProposedItems);
log.info(logStringBuilder.toString());
@@ -162,9 +168,15 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
final LocalDate targetDate, final Currency currency,
final StringBuilder logStringBuilder, final BillingMode billingMode,
final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate,
+ @Nullable final List<Invoice> existingInvoices,
final InternalCallContext internalCallContext) throws InvoiceApiException {
final List<InvoiceItem> items = new ArrayList<InvoiceItem>();
+ // For FIXEDTERM phases we need to stop when the specified duration has been reached
+ final LocalDate maxEndDate = thisEvent.getPlanPhase().getPhaseType() == PhaseType.FIXEDTERM ?
+ computeMaxEndDateForFixedTermPlanPhase(thisEvent, existingInvoices) :
+ null;
+
// Handle recurring items
final BillingPeriod billingPeriod = thisEvent.getBillingPeriod();
if (billingPeriod != BillingPeriod.NO_BILLING_PERIOD) {
@@ -183,8 +195,12 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
}
for (final RecurringInvoiceItemData itemDatum : itemDataWithNextBillingCycleDate.getItemData()) {
- final BigDecimal rate = thisEvent.getRecurringPrice();
+ // Stop if there a maxEndDate and we have reached it
+ if (maxEndDate != null && maxEndDate.compareTo(itemDatum.getEndDate()) < 0) {
+ break;
+ }
+ final BigDecimal rate = thisEvent.getRecurringPrice();
if (rate != null) {
final BigDecimal amount = KillBillMoney.of(itemDatum.getNumberOfCycles().multiply(rate), currency);
final RecurringInvoiceItem recurringItem = new RecurringInvoiceItem(invoiceId,
@@ -193,7 +209,8 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
thisEvent.getSubscription().getId(),
thisEvent.getPlan().getName(),
thisEvent.getPlanPhase().getName(),
- itemDatum.getStartDate(), itemDatum.getEndDate(),
+ itemDatum.getStartDate(),
+ itemDatum.getEndDate(),
amount, rate, currency);
items.add(recurringItem);
}
@@ -212,6 +229,38 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
return items;
}
+ //
+ // Go through invoices in reverse order and for each invoice extract a possible item for that subscription and the phaseName associated with this billing event
+ // Computes the startDate for that first one seen in that uninterrupted sequence and then add the FIXEDTERM duration to compute the max endDate
+ //
+ private LocalDate computeMaxEndDateForFixedTermPlanPhase(final BillingEvent thisEvent, @Nullable final List<Invoice> existingInvoices) {
+ if (existingInvoices == null || existingInvoices.isEmpty()) {
+ return null;
+ }
+
+ LocalDate firstStartDate = null;
+ for (int i = existingInvoices.size() - 1; i >= 0; i--) {
+ final Invoice cur = existingInvoices.get(i);
+
+ final InvoiceItem matchItem = Iterables.tryFind(cur.getInvoiceItems(), new Predicate<InvoiceItem>() {
+ @Override
+ public boolean apply(final InvoiceItem input) {
+ return input.getInvoiceItemType() == InvoiceItemType.RECURRING &&
+ thisEvent.getSubscription().getId().equals(input.getSubscriptionId()) &&
+ thisEvent.getPlanPhase().getName().equals(input.getPhaseName());
+ }
+ }).orNull();
+
+ if (matchItem == null) {
+ break;
+ }
+ firstStartDate = matchItem.getStartDate();
+ }
+
+ final LocalDate result = addDurationToLocalDate(firstStartDate, thisEvent.getPlanPhase().getDuration());
+ return result;
+ }
+
private void updatePerSubscriptionNextNotificationDate(final UUID subscriptionId, final LocalDate nextBillingCycleDate, final List<InvoiceItem> newProposedItems, final BillingMode billingMode, final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates) {
LocalDate nextNotificationDate = null;
@@ -355,4 +404,26 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
}
}
}
+
+ // That code should belong to Duration/DefaultDuration but requires a change api (not possible for 0.16.3, but will be moreved in 0.17.0)
+ private LocalDate addDurationToLocalDate(@Nullable final LocalDate inputDate, final Duration duration) {
+
+ if (inputDate == null) {
+ return inputDate;
+ }
+
+ switch (duration.getUnit()) {
+ case DAYS:
+ return inputDate.plusDays(duration.getNumber());
+ case MONTHS:
+ return inputDate.plusMonths(duration.getNumber());
+ case YEARS:
+ return inputDate.plusYears(duration.getNumber());
+ case UNLIMITED:
+ return inputDate.plusYears(100);
+ default:
+ throw new IllegalStateException("Unknwon duration " + duration.getUnit());
+ }
+ }
+
}
jaxrs/pom.xml 2(+1 -1)
diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index d0f56d5..1b7d890 100644
--- a/jaxrs/pom.xml
+++ b/jaxrs/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-jaxrs</artifactId>
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
index c11ff57..b55db89 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
@@ -130,7 +130,6 @@ public class UsageResource extends JaxRsResourceBase {
@QueryParam(QUERY_START_DATE) final String startDate,
@QueryParam(QUERY_END_DATE) final String endDate,
@javax.ws.rs.core.Context final HttpServletRequest request) {
-
if (startDate == null || endDate == null) {
return Response.status(Status.BAD_REQUEST).build();
}
junction/pom.xml 2(+1 -1)
diff --git a/junction/pom.xml b/junction/pom.xml
index 422ded4..16f2cd0 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-junction</artifactId>
NEWS 3(+3 -0)
diff --git a/NEWS b/NEWS
index 8001aa9..3201911 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,6 @@
+0.16.2
+ See https://github.com/killbill/killbill/releases/tag/killbill-0.16.2
+
0.16.1
See https://github.com/killbill/killbill/releases/tag/killbill-0.16.1
overdue/pom.xml 2(+1 -1)
diff --git a/overdue/pom.xml b/overdue/pom.xml
index e7f373d..47b77b1 100644
--- a/overdue/pom.xml
+++ b/overdue/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-overdue</artifactId>
payment/pom.xml 2(+1 -1)
diff --git a/payment/pom.xml b/payment/pom.xml
index de0ab68..f33921d 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-payment</artifactId>
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.java
index 4ead401..9a87984 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.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
@@ -63,6 +63,7 @@ public class DefaultControlInitiated implements LeavingStateCallback {
final PaymentModelDao payment = paymentDao.getPayment(stateContext.getPaymentId(), stateContext.getInternalCallContext());
Preconditions.checkNotNull(payment, "payment cannot be null for id " + stateContext.getPaymentId());
stateContext.setPaymentExternalKey(payment.getExternalKey());
+ stateContext.setPaymentMethodId(payment.getPaymentMethodId());
} else if (stateContext.getPaymentExternalKey() == null) {
stateContext.setPaymentExternalKey(UUIDs.randomUUID().toString());
}
@@ -74,6 +75,11 @@ public class DefaultControlInitiated implements LeavingStateCallback {
stateContext.setPaymentTransactionExternalKey(UUIDs.randomUUID().toString());
}
+ if (stateContext.getPaymentMethodId() == null) {
+ // Similar logic in PaymentAutomatonRunner
+ stateContext.setPaymentMethodId(stateContext.getAccount().getPaymentMethodId());
+ }
+
if (state.getName().equals(initialState.getName()) || state.getName().equals(retriedState.getName())) {
try {
//
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.java
index 8b51735..2841907 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.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
@@ -72,7 +72,6 @@ public class PaymentStateControlContext extends PaymentStateContext {
this.result = result;
}
-
public PaymentTransaction getCurrentTransaction() {
if (result == null || result.getTransactions() == null) {
return null;
@@ -80,7 +79,8 @@ public class PaymentStateControlContext extends PaymentStateContext {
return Iterables.tryFind(result.getTransactions(), new Predicate<PaymentTransaction>() {
@Override
public boolean apply(final PaymentTransaction input) {
- return ((DefaultPaymentTransaction) input).getAttemptId().equals(getAttemptId());
+ final DefaultPaymentTransaction defaultPaymentTransaction = (DefaultPaymentTransaction) input;
+ return defaultPaymentTransaction.getAttemptId() == null ? getAttemptId() == null : defaultPaymentTransaction.getAttemptId().equals(getAttemptId());
}
}).orNull();
}
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiWithControl.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiWithControl.java
index aaffeda..acb1be0 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiWithControl.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiWithControl.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
@@ -23,17 +23,17 @@ import java.util.UUID;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
-import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
-import org.killbill.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
-import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
import org.killbill.billing.control.plugin.api.OnFailurePaymentControlResult;
import org.killbill.billing.control.plugin.api.OnSuccessPaymentControlResult;
import org.killbill.billing.control.plugin.api.PaymentControlApiException;
import org.killbill.billing.control.plugin.api.PaymentControlContext;
import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
import org.killbill.billing.control.plugin.api.PriorPaymentControlResult;
+import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
+import org.killbill.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
+import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
import org.killbill.billing.payment.retry.DefaultFailureCallResult;
import org.killbill.billing.payment.retry.DefaultOnSuccessPaymentControlResult;
import org.testng.Assert;
@@ -45,45 +45,107 @@ import com.google.inject.Inject;
public class TestPaymentApiWithControl extends PaymentTestSuiteWithEmbeddedDB {
+ private static final PaymentOptions PAYMENT_OPTIONS = new PaymentOptions() {
+ @Override
+ public boolean isExternalPayment() {
+ return false;
+ }
+
+ @Override
+ public List<String> getPaymentControlPluginNames() {
+ return ImmutableList.of(TestPaymentControlPluginApi.PLUGIN_NAME);
+ }
+ };
+
@Inject
private OSGIServiceRegistration<PaymentControlPluginApi> controlPluginRegistry;
private Account account;
- private UUID newPaymentMethodId;
+ private TestPaymentControlPluginApi testPaymentControlPluginApi;
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
super.beforeMethod();
account = testHelper.createTestAccount("bobo@gmail.com", true);
- final PaymentMethodPlugin paymentMethodInfo = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), false, null);
- newPaymentMethodId = paymentApi.addPaymentMethod(account, paymentMethodInfo.getExternalPaymentMethodId(), MockPaymentProviderPlugin.PLUGIN_NAME, false, paymentMethodInfo, ImmutableList.<PluginProperty>of(), callContext);
+ testPaymentControlPluginApi = new TestPaymentControlPluginApi();
controlPluginRegistry.registerService(new OSGIServiceDescriptor() {
- @Override
- public String getPluginSymbolicName() {
- return null;
- }
-
- @Override
- public String getPluginName() {
- return TestPaymentControlPluginApi.PLUGIN_NAME;
- }
-
- @Override
- public String getRegistrationName() {
- return TestPaymentControlPluginApi.PLUGIN_NAME;
- }
- }, new TestPaymentControlPluginApi(newPaymentMethodId));
+ @Override
+ public String getPluginSymbolicName() {
+ return null;
+ }
+
+ @Override
+ public String getPluginName() {
+ return TestPaymentControlPluginApi.PLUGIN_NAME;
+ }
+
+ @Override
+ public String getRegistrationName() {
+ return TestPaymentControlPluginApi.PLUGIN_NAME;
+ }
+ },
+ testPaymentControlPluginApi);
+ }
+ // Verify Payment control API can be used to change the paymentMethodId on the fly and this is reflected in the created Payment.
+ @Test(groups = "slow")
+ public void testCreateAuthWithControl() throws PaymentApiException {
+ final PaymentMethodPlugin paymentMethodInfo = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), false, null);
+ final UUID newPaymentMethodId = paymentApi.addPaymentMethod(account, paymentMethodInfo.getExternalPaymentMethodId(), MockPaymentProviderPlugin.PLUGIN_NAME, false, paymentMethodInfo, ImmutableList.<PluginProperty>of(), callContext);
+ testPaymentControlPluginApi.setNewPaymentMethodId(newPaymentMethodId);
+
+ final Payment payment = paymentApi.createAuthorizationWithPaymentControl(account, account.getPaymentMethodId(), null, BigDecimal.TEN, Currency.USD, UUID.randomUUID().toString(),
+ UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), PAYMENT_OPTIONS, callContext);
+ Assert.assertEquals(payment.getPaymentMethodId(), newPaymentMethodId);
+ }
+
+ @Test(groups = "slow")
+ public void testCreateAuthWithControlCaptureNoControl() throws PaymentApiException {
+ final BigDecimal requestedAmount = BigDecimal.TEN;
+
+ Payment payment = paymentApi.createAuthorizationWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, UUID.randomUUID().toString(),
+ UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), PAYMENT_OPTIONS, callContext);
+ Assert.assertEquals(payment.getAuthAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getTransactions().size(), 1);
+ Assert.assertNotNull(((DefaultPaymentTransaction) payment.getTransactions().get(0)).getAttemptId());
+
+ payment = paymentApi.createCapture(account, payment.getId(), payment.getAuthAmount(), payment.getCurrency(), UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), callContext);
+ Assert.assertEquals(payment.getAuthAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(payment.getCapturedAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(payment.getTransactions().size(), 2);
+ Assert.assertNotNull(((DefaultPaymentTransaction) payment.getTransactions().get(0)).getAttemptId());
+ Assert.assertNull(((DefaultPaymentTransaction) payment.getTransactions().get(1)).getAttemptId());
+ }
+
+ @Test(groups = "slow")
+ public void testCreateAuthNoControlCaptureWithControl() throws PaymentApiException {
+ final BigDecimal requestedAmount = BigDecimal.TEN;
+
+ Payment payment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, UUID.randomUUID().toString(),
+ UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), callContext);
+ Assert.assertEquals(payment.getAuthAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getTransactions().size(), 1);
+ Assert.assertNull(((DefaultPaymentTransaction) payment.getTransactions().get(0)).getAttemptId());
+
+ payment = paymentApi.createCaptureWithPaymentControl(account, payment.getId(), payment.getAuthAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
+ ImmutableList.<PluginProperty>of(), PAYMENT_OPTIONS, callContext);
+ Assert.assertEquals(payment.getAuthAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(payment.getCapturedAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(payment.getTransactions().size(), 2);
+ Assert.assertNull(((DefaultPaymentTransaction) payment.getTransactions().get(0)).getAttemptId());
+ Assert.assertNotNull(((DefaultPaymentTransaction) payment.getTransactions().get(1)).getAttemptId());
}
public static class TestPaymentControlPluginApi implements PaymentControlPluginApi {
public static final String PLUGIN_NAME = "TEST_CONTROL_API_PLUGIN_NAME";
- private final UUID newPaymentMethodId;
+ private UUID newPaymentMethodId;
- public TestPaymentControlPluginApi(final UUID newPaymentMethodId) {
+ public void setNewPaymentMethodId(final UUID newPaymentMethodId) {
this.newPaymentMethodId = newPaymentMethodId;
}
@@ -127,31 +189,4 @@ public class TestPaymentApiWithControl extends PaymentTestSuiteWithEmbeddedDB {
return new DefaultFailureCallResult(null);
}
}
-
- // Verify Payment control API can be used to change the paymentMethodId on the fly and this is reflected in the created Payment.
- @Test(groups = "slow")
- public void testCreateAuthWithControl() throws PaymentApiException {
-
- final BigDecimal requestedAmount = BigDecimal.TEN;
-
- final String paymentExternalKey = "dfdf";
- final String transactionExternalKey = "qwqwqw";
-
- final PaymentOptions paymentOptions = new PaymentOptions() {
- @Override
- public boolean isExternalPayment() {
- return false;
- }
-
- @Override
- public List<String> getPaymentControlPluginNames() {
- return ImmutableList.of(TestPaymentControlPluginApi.PLUGIN_NAME);
- }
- };
-
- final Payment payment = paymentApi.createAuthorizationWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, paymentExternalKey, transactionExternalKey,
- ImmutableList.<PluginProperty>of(), paymentOptions, callContext);
-
- Assert.assertEquals(payment.getPaymentMethodId(), newPaymentMethodId);
- }
}
pom.xml 2(+1 -1)
diff --git a/pom.xml b/pom.xml
index e9f27a9..8fb6873 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,7 +24,7 @@
<version>0.83-SNAPSHOT</version>
</parent>
<artifactId>killbill</artifactId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<packaging>pom</packaging>
<name>killbill</name>
<description>Library for managing recurring subscriptions and the associated billing</description>
profiles/killbill/pom.xml 2(+1 -1)
diff --git a/profiles/killbill/pom.xml b/profiles/killbill/pom.xml
index 20c2b32..a698349 100644
--- a/profiles/killbill/pom.xml
+++ b/profiles/killbill/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>killbill-profiles</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-profiles-killbill</artifactId>
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
index 752ef48..e310cc1 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 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
@@ -93,6 +93,105 @@ public class TestInvoice extends TestJaxrsBase {
final Invoice firstInvoiceByNumberJson = killBillClient.getInvoice(invoiceJson.getInvoiceNumber());
assertEquals(firstInvoiceByNumberJson, invoiceJson);
+ // Check we can retrieve the HTML version
+ final String htmlInvoice = killBillClient.getInvoiceAsHtml(invoiceJson.getInvoiceId());
+ assertEquals(htmlInvoice, "<html>\n" +
+ " <head>\n" +
+ " <style type=\"text/css\">\n" +
+ " th {align=left; width=225px; border-bottom: solid 2px black;}\n" +
+ " </style>\n" +
+ " </head>\n" +
+ " <body>\n" +
+ " <h1>invoiceTitle</h1>\n" +
+ " <table>\n" +
+ " <tr>\n" +
+ " <td rowspan=3 width=350px>Insert image here</td>\n" +
+ " <td width=100px/>\n" +
+ " <td width=225px/>\n" +
+ " <td width=225px/>\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td />\n" +
+ " <td align=right>invoiceDate</td>\n" +
+ " <td>25 avr. 2012</td>\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td />\n" +
+ " <td align=right>invoiceNumber</td>\n" +
+ " <td>1</td>\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td>companyName</td>\n" +
+ " <td></td>\n" +
+ " <td align=right>accountOwnerName</td>\n" +
+ " <td>" + accountJson.getName() + "</td>\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td>companyAddress</td>\n" +
+ " <td />\n" +
+ " <td />\n" +
+ " <td>" + accountJson.getEmail() + "</td>\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td>companyCityProvincePostalCode</td>\n" +
+ " <td />\n" +
+ " <td />\n" +
+ " <td>" + accountJson.getPhone() + "</td>\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td>companyCountry</td>\n" +
+ " <td />\n" +
+ " <td />\n" +
+ " <td />\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td><companyUrl</td>\n" +
+ " <td />\n" +
+ " <td />\n" +
+ " <td />\n" +
+ " </tr>\n" +
+ " </table>\n" +
+ " <br />\n" +
+ " <br />\n" +
+ " <br />\n" +
+ " <table>\n" +
+ " <tr>\n" +
+ " <th>invoiceItemBundleName</td>\n" +
+ " <th>invoiceItemDescription</td>\n" +
+ " <th>invoiceItemServicePeriod</td>\n" +
+ " <th>invoiceItemAmount</td>\n" +
+ " </tr>\n" +
+ " \n" +
+ " <tr>\n" +
+ " <td>shotgun-monthly-trial</td>\n" +
+ " <td>Monthly shotgun plan</td>\n" +
+ " <td>25 avr. 2012</td>\n" +
+ " <td>USD 0E-9</td>\n" +
+ " </tr>\n" +
+ " \n" +
+ " <tr>\n" +
+ " <td colspan=4 />\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td colspan=2 />\n" +
+ " <td align=right><strong>invoiceAmount</strong></td>\n" +
+ " <td align=right><strong>0.00</strong></td>\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td colspan=2 />\n" +
+ " <td align=right><strong>invoiceAmountPaid</strong></td>\n" +
+ " <td align=right><strong>0</strong></td>\n" +
+ " </tr>\n" +
+ " <tr>\n" +
+ " <td colspan=2 />\n" +
+ " <td align=right><strong>invoiceBalance</strong></td>\n" +
+ " <td align=right><strong>0.00</strong></td>\n" +
+ " </tr>\n" +
+ " </table>\n" +
+ " </body>\n" +
+ "</html>\n" +
+ "\n");
+
// Then create a dryRun for next upcoming invoice
final InvoiceDryRun dryRunArg = new InvoiceDryRun(DryRunType.UPCOMING_INVOICE, null,
null, null, null, null, null, null, null, null, null, null);
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestUsage.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestUsage.java
new file mode 100644
index 0000000..2e9dd6a
--- /dev/null
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestUsage.java
@@ -0,0 +1,118 @@
+/*
+ * 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.jaxrs;
+
+import java.util.UUID;
+
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.PriceListSet;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.client.model.Account;
+import org.killbill.billing.client.model.Bundle;
+import org.killbill.billing.client.model.RolledUpUsage;
+import org.killbill.billing.client.model.Subscription;
+import org.killbill.billing.client.model.SubscriptionUsageRecord;
+import org.killbill.billing.client.model.UnitUsageRecord;
+import org.killbill.billing.client.model.UsageRecord;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class TestUsage extends TestJaxrsBase {
+
+ @Test(groups = "slow", description = "Can record and retrieve usage data")
+ public void testRecordUsage() throws Exception {
+ final Account accountJson = createAccountWithDefaultPaymentMethod();
+
+ final Subscription base = new Subscription();
+ base.setAccountId(accountJson.getAccountId());
+ base.setProductName("Pistol");
+ base.setProductCategory(ProductCategory.BASE);
+ base.setBillingPeriod(BillingPeriod.MONTHLY);
+ base.setPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
+
+ final Subscription addOn = new Subscription();
+ addOn.setAccountId(accountJson.getAccountId());
+ addOn.setProductName("Bullets");
+ addOn.setProductCategory(ProductCategory.ADD_ON);
+ addOn.setBillingPeriod(BillingPeriod.NO_BILLING_PERIOD);
+ addOn.setPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
+
+ final Bundle bundle = killBillClient.createSubscriptionWithAddOns(ImmutableList.<Subscription>of(base, addOn),
+ null,
+ DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC,
+ createdBy,
+ reason,
+ comment);
+ final UUID addOnSubscriptionId = Iterables.<Subscription>find(bundle.getSubscriptions(),
+ new Predicate<Subscription>() {
+ @Override
+ public boolean apply(final Subscription input) {
+ return ProductCategory.ADD_ON.equals(input.getProductCategory());
+ }
+ }).getSubscriptionId();
+
+ clock.addDays(1);
+
+ final UsageRecord usageRecord1 = new UsageRecord();
+ usageRecord1.setAmount(10L);
+ usageRecord1.setRecordDate(clock.getUTCToday().minusDays(1));
+
+ final UsageRecord usageRecord2 = new UsageRecord();
+ usageRecord2.setAmount(5L);
+ usageRecord2.setRecordDate(clock.getUTCToday());
+
+ final UnitUsageRecord unitUsageRecord = new UnitUsageRecord();
+ unitUsageRecord.setUnitType("bullets");
+ unitUsageRecord.setUsageRecords(ImmutableList.<UsageRecord>of(usageRecord1, usageRecord2));
+
+ final SubscriptionUsageRecord usage = new SubscriptionUsageRecord();
+ usage.setSubscriptionId(addOnSubscriptionId);
+ usage.setUnitUsageRecords(ImmutableList.<UnitUsageRecord>of(unitUsageRecord));
+
+ killBillClient.createSubscriptionUsageRecord(usage, createdBy, reason, comment);
+
+ final RolledUpUsage retrievedUsage1 = killBillClient.getRolledUpUsage(addOnSubscriptionId, unitUsageRecord.getUnitType(), clock.getUTCToday().minusDays(1), clock.getUTCToday());
+ Assert.assertEquals(retrievedUsage1.getSubscriptionId(), usage.getSubscriptionId());
+ Assert.assertEquals(retrievedUsage1.getRolledUpUnits().size(), 1);
+ Assert.assertEquals(retrievedUsage1.getRolledUpUnits().get(0).getUnitType(), unitUsageRecord.getUnitType());
+ // endDate is excluded
+ Assert.assertEquals((long) retrievedUsage1.getRolledUpUnits().get(0).getAmount(), 10);
+
+ final RolledUpUsage retrievedUsage2 = killBillClient.getRolledUpUsage(addOnSubscriptionId, unitUsageRecord.getUnitType(), clock.getUTCToday().minusDays(1), clock.getUTCToday().plusDays(1));
+ Assert.assertEquals(retrievedUsage2.getSubscriptionId(), usage.getSubscriptionId());
+ Assert.assertEquals(retrievedUsage2.getRolledUpUnits().size(), 1);
+ Assert.assertEquals(retrievedUsage2.getRolledUpUnits().get(0).getUnitType(), unitUsageRecord.getUnitType());
+ Assert.assertEquals((long) retrievedUsage2.getRolledUpUnits().get(0).getAmount(), 15);
+
+ final RolledUpUsage retrievedUsage3 = killBillClient.getRolledUpUsage(addOnSubscriptionId, unitUsageRecord.getUnitType(), clock.getUTCToday(), clock.getUTCToday().plusDays(1));
+ Assert.assertEquals(retrievedUsage3.getSubscriptionId(), usage.getSubscriptionId());
+ Assert.assertEquals(retrievedUsage3.getRolledUpUnits().size(), 1);
+ Assert.assertEquals(retrievedUsage3.getRolledUpUnits().get(0).getUnitType(), unitUsageRecord.getUnitType());
+ Assert.assertEquals((long) retrievedUsage3.getRolledUpUnits().get(0).getAmount(), 5);
+
+ final RolledUpUsage retrievedUsage4 = killBillClient.getRolledUpUsage(addOnSubscriptionId, null, clock.getUTCToday(), clock.getUTCToday().plusDays(1));
+ Assert.assertEquals(retrievedUsage4.getSubscriptionId(), usage.getSubscriptionId());
+ Assert.assertEquals(retrievedUsage4.getRolledUpUnits().size(), 1);
+ Assert.assertEquals(retrievedUsage4.getRolledUpUnits().get(0).getUnitType(), "bullets");
+ Assert.assertEquals((long) retrievedUsage4.getRolledUpUnits().get(0).getAmount(), 5);
+ }
+}
profiles/killpay/pom.xml 2(+1 -1)
diff --git a/profiles/killpay/pom.xml b/profiles/killpay/pom.xml
index 6d43052..829b07a 100644
--- a/profiles/killpay/pom.xml
+++ b/profiles/killpay/pom.xml
@@ -20,7 +20,7 @@
<parent>
<artifactId>killbill-profiles</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-profiles-killpay</artifactId>
profiles/pom.xml 2(+1 -1)
diff --git a/profiles/pom.xml b/profiles/pom.xml
index 442ac18..9bf3c9f 100644
--- a/profiles/pom.xml
+++ b/profiles/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-profiles</artifactId>
subscription/pom.xml 2(+1 -1)
diff --git a/subscription/pom.xml b/subscription/pom.xml
index 825e97b..5ae804d 100644
--- a/subscription/pom.xml
+++ b/subscription/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-subscription</artifactId>
tenant/pom.xml 2(+1 -1)
diff --git a/tenant/pom.xml b/tenant/pom.xml
index a106e2b..9662fc8 100644
--- a/tenant/pom.xml
+++ b/tenant/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-tenant</artifactId>
usage/pom.xml 2(+1 -1)
diff --git a/usage/pom.xml b/usage/pom.xml
index 4d9a683..d316e77 100644
--- a/usage/pom.xml
+++ b/usage/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-usage</artifactId>
util/pom.xml 2(+1 -1)
diff --git a/util/pom.xml b/util/pom.xml
index 508a73f..0e250e7 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.16.2-SNAPSHOT</version>
+ <version>0.16.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-util</artifactId>