killbill-aplcache
Changes
account/pom.xml 2(+1 -1)
account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountChangeEvent.java 57(+33 -24)
account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java 6(+3 -3)
api/pom.xml 2(+1 -1)
beatrix/pom.xml 2(+1 -1)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java 4(+2 -2)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java 8(+4 -4)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java 19(+6 -13)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java 2(+1 -1)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithDifferentBillingPeriods.java 10(+5 -5)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java 314(+292 -22)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java 26(+13 -13)
catalog/pom.xml 2(+1 -1)
catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java 17(+9 -8)
currency/pom.xml 2(+1 -1)
entitlement/pom.xml 2(+1 -1)
entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java 25(+13 -12)
entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java 10(+6 -4)
entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java 14(+10 -4)
entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionEvent.java 14(+9 -5)
entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java 28(+15 -13)
entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java 13(+6 -7)
entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java 3(+1 -2)
entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java 18(+16 -2)
entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java 8(+5 -3)
entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java 33(+18 -15)
entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEntitlementDateHelper.java 6(+4 -2)
invoice/pom.xml 2(+1 -1)
jaxrs/pom.xml 2(+1 -1)
junction/pom.xml 2(+1 -1)
junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java 4(+2 -2)
junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java 18(+8 -10)
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/janitor/IncompletePaymentTransactionTask.java 28(+17 -11)
payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java 24(+21 -3)
payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java 46(+33 -13)
payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java 15(+13 -2)
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 d28390d..b876bcd 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.1-SNAPSHOT</version>
+ <version>0.16.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-account</artifactId>
diff --git a/account/src/main/java/org/killbill/billing/account/api/DefaultChangedField.java b/account/src/main/java/org/killbill/billing/account/api/DefaultChangedField.java
index 7ee0e32..8b11cfb 100644
--- a/account/src/main/java/org/killbill/billing/account/api/DefaultChangedField.java
+++ b/account/src/main/java/org/killbill/billing/account/api/DefaultChangedField.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -17,7 +19,6 @@
package org.killbill.billing.account.api;
import org.joda.time.DateTime;
-
import org.killbill.billing.events.ChangedField;
import com.fasterxml.jackson.annotation.JsonCreator;
@@ -41,12 +42,6 @@ public class DefaultChangedField implements ChangedField {
this.newValue = newValue;
}
- public DefaultChangedField(final String fieldName,
- final String oldValue,
- final String newValue) {
- this(fieldName, oldValue, newValue, new DateTime());
- }
-
@Override
public String getFieldName() {
return fieldName;
@@ -124,5 +119,4 @@ public class DefaultChangedField implements ChangedField {
}
return true;
}
-
}
diff --git a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountChangeEvent.java b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountChangeEvent.java
index 9ee7111..d68f0dc 100644
--- a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountChangeEvent.java
+++ b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountChangeEvent.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -17,9 +19,11 @@
package org.killbill.billing.account.api.user;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.UUID;
+import org.joda.time.DateTime;
import org.killbill.billing.account.api.DefaultChangedField;
import org.killbill.billing.account.dao.AccountModelDao;
import org.killbill.billing.events.AccountChangeInternalEvent;
@@ -47,10 +51,16 @@ public class DefaultAccountChangeEvent extends BusEventBase implements AccountCh
this.changedFields = changedFields;
}
- public DefaultAccountChangeEvent(final UUID id, final AccountModelDao oldData, final AccountModelDao newData, final Long searchKey1, final Long searchKey2, final UUID userToken) {
+ public DefaultAccountChangeEvent(final UUID id,
+ final AccountModelDao oldData,
+ final AccountModelDao newData,
+ final Long searchKey1,
+ final Long searchKey2,
+ final UUID userToken,
+ final DateTime changeDate) {
super(searchKey1, searchKey2, userToken);
this.accountId = id;
- this.changedFields = calculateChangedFields(oldData, newData);
+ this.changedFields = calculateChangedFields(oldData, newData, changeDate);
}
@JsonIgnore
@@ -73,7 +83,7 @@ public class DefaultAccountChangeEvent extends BusEventBase implements AccountCh
@JsonIgnore
@Override
public boolean hasChanges() {
- return (changedFields.size() > 0);
+ return (!changedFields.isEmpty());
}
@Override
@@ -116,57 +126,56 @@ public class DefaultAccountChangeEvent extends BusEventBase implements AccountCh
return true;
}
- private List<ChangedField> calculateChangedFields(final AccountModelDao oldData, final AccountModelDao newData) {
-
+ private List<ChangedField> calculateChangedFields(final AccountModelDao oldData, final AccountModelDao newData, final DateTime changeDate) {
final List<ChangedField> tmpChangedFields = new ArrayList<ChangedField>();
addIfValueChanged(tmpChangedFields, "externalKey",
- oldData.getExternalKey(), newData.getExternalKey());
+ oldData.getExternalKey(), newData.getExternalKey(), changeDate);
addIfValueChanged(tmpChangedFields, "email",
- oldData.getEmail(), newData.getEmail());
+ oldData.getEmail(), newData.getEmail(), changeDate);
addIfValueChanged(tmpChangedFields, "firstName",
- oldData.getName(), newData.getName());
+ oldData.getName(), newData.getName(), changeDate);
addIfValueChanged(tmpChangedFields, "currency",
(oldData.getCurrency() != null) ? oldData.getCurrency().toString() : null,
- (newData.getCurrency() != null) ? newData.getCurrency().toString() : null);
+ (newData.getCurrency() != null) ? newData.getCurrency().toString() : null, changeDate);
addIfValueChanged(tmpChangedFields,
"billCycleDayLocal",
- String.valueOf(oldData.getBillingCycleDayLocal()), String.valueOf(newData.getBillingCycleDayLocal()));
+ String.valueOf(oldData.getBillingCycleDayLocal()), String.valueOf(newData.getBillingCycleDayLocal()), changeDate);
addIfValueChanged(tmpChangedFields, "paymentMethodId",
(oldData.getPaymentMethodId() != null) ? oldData.getPaymentMethodId().toString() : null,
- (newData.getPaymentMethodId() != null) ? newData.getPaymentMethodId().toString() : null);
+ (newData.getPaymentMethodId() != null) ? newData.getPaymentMethodId().toString() : null, changeDate);
- addIfValueChanged(tmpChangedFields, "locale", oldData.getLocale(), newData.getLocale());
+ addIfValueChanged(tmpChangedFields, "locale", oldData.getLocale(), newData.getLocale(), changeDate);
addIfValueChanged(tmpChangedFields, "timeZone",
(oldData.getTimeZone() == null) ? null : oldData.getTimeZone().toString(),
- (newData.getTimeZone() == null) ? null : newData.getTimeZone().toString());
+ (newData.getTimeZone() == null) ? null : newData.getTimeZone().toString(), changeDate);
- addIfValueChanged(tmpChangedFields, "address1", oldData.getAddress1(), newData.getAddress1());
- addIfValueChanged(tmpChangedFields, "address2", oldData.getAddress2(), newData.getAddress2());
- addIfValueChanged(tmpChangedFields, "city", oldData.getCity(), newData.getCity());
- addIfValueChanged(tmpChangedFields, "stateOrProvince", oldData.getStateOrProvince(), newData.getStateOrProvince());
- addIfValueChanged(tmpChangedFields, "country", oldData.getCountry(), newData.getCountry());
- addIfValueChanged(tmpChangedFields, "postalCode", oldData.getPostalCode(), newData.getPostalCode());
- addIfValueChanged(tmpChangedFields, "phone", oldData.getPhone(), newData.getPhone());
+ addIfValueChanged(tmpChangedFields, "address1", oldData.getAddress1(), newData.getAddress1(), changeDate);
+ addIfValueChanged(tmpChangedFields, "address2", oldData.getAddress2(), newData.getAddress2(), changeDate);
+ addIfValueChanged(tmpChangedFields, "city", oldData.getCity(), newData.getCity(), changeDate);
+ addIfValueChanged(tmpChangedFields, "stateOrProvince", oldData.getStateOrProvince(), newData.getStateOrProvince(), changeDate);
+ addIfValueChanged(tmpChangedFields, "country", oldData.getCountry(), newData.getCountry(), changeDate);
+ addIfValueChanged(tmpChangedFields, "postalCode", oldData.getPostalCode(), newData.getPostalCode(), changeDate);
+ addIfValueChanged(tmpChangedFields, "phone", oldData.getPhone(), newData.getPhone(), changeDate);
return tmpChangedFields;
}
- private void addIfValueChanged(final List<ChangedField> inputList, final String key, final String oldData, final String newData) {
+ private void addIfValueChanged(final Collection<ChangedField> inputList, final String key, final String oldData, final String newData, final DateTime changeDate) {
// If both null => no changes
if (newData == null && oldData == null) {
// If only one is null
} else if (newData == null || oldData == null) {
- inputList.add(new DefaultChangedField(key, oldData, newData));
+ inputList.add(new DefaultChangedField(key, oldData, newData, changeDate));
// If neither are null we can safely compare values
} else if (!newData.equals(oldData)) {
- inputList.add(new DefaultChangedField(key, oldData, newData));
+ inputList.add(new DefaultChangedField(key, oldData, newData, changeDate));
}
}
}
diff --git a/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java b/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
index 31c33e3..a106fb0 100644
--- a/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
+++ b/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -57,6 +59,7 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
private final PersistentBus eventBus;
private final InternalCallContextFactory internalCallContextFactory;
+ private final Clock clock;
@Inject
public DefaultAccountDao(final IDBI dbi, final PersistentBus eventBus, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher,
@@ -64,6 +67,7 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao), AccountSqlDao.class);
this.eventBus = eventBus;
this.internalCallContextFactory = internalCallContextFactory;
+ this.clock = clock;
}
@Override
@@ -157,8 +161,8 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
specifiedAccount,
context.getAccountRecordId(),
context.getTenantRecordId(),
- context.getUserToken()
- );
+ context.getUserToken(),
+ clock.getUTCNow());
try {
eventBus.postFromTransaction(changeEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
} catch (final EventBusException e) {
@@ -195,8 +199,8 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
final AccountChangeInternalEvent changeEvent = new DefaultAccountChangeEvent(accountId, currentAccount, account,
context.getAccountRecordId(),
context.getTenantRecordId(),
- context.getUserToken()
- );
+ context.getUserToken(),
+ clock.getUTCNow());
try {
eventBus.postFromTransaction(changeEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
diff --git a/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java b/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java
index f3d7863..6cf12f7 100644
--- a/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java
+++ b/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.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
@@ -52,7 +52,7 @@ public class TestDefaultAccountUserApiWithMocks extends AccountTestSuiteNoDB {
@BeforeMethod(groups = "fast")
public void setUp() throws Exception {
- accountDao = new MockAccountDao(Mockito.mock(PersistentBus.class));
+ accountDao = new MockAccountDao(Mockito.mock(PersistentBus.class), clock);
accountUserApi = new DefaultAccountUserApi(accountDao, nonEntityDao, controllerDispatcher, internalFactory);
}
diff --git a/account/src/test/java/org/killbill/billing/account/api/user/TestEventJson.java b/account/src/test/java/org/killbill/billing/account/api/user/TestEventJson.java
index 23ccdf7..23e3aa3 100644
--- a/account/src/test/java/org/killbill/billing/account/api/user/TestEventJson.java
+++ b/account/src/test/java/org/killbill/billing/account/api/user/TestEventJson.java
@@ -37,8 +37,8 @@ public class TestEventJson extends AccountTestSuiteNoDB {
@Test(groups = "fast", description="Test Account event deserialization")
public void testDefaultAccountChangeEvent() throws Exception {
final List<ChangedField> changes = new ArrayList<ChangedField>();
- changes.add(new DefaultChangedField("fieldXX", "valueX", "valueXXX"));
- changes.add(new DefaultChangedField("fieldYY", "valueY", "valueYYY"));
+ changes.add(new DefaultChangedField("fieldXX", "valueX", "valueXXX", clock.getUTCNow()));
+ changes.add(new DefaultChangedField("fieldYY", "valueY", "valueYYY", clock.getUTCNow()));
final AccountChangeInternalEvent e = new DefaultAccountChangeEvent(changes, UUID.randomUUID(), 1L, 2L, null);
final String json = mapper.writeValueAsString(e);
diff --git a/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java b/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
index a06aa3f..0c33683 100644
--- a/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
+++ b/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -16,13 +18,12 @@
package org.killbill.billing.account.dao;
+import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
-import org.testng.Assert;
-
import org.killbill.billing.BillingExceptionBase;
import org.killbill.billing.ErrorCode;
import org.killbill.billing.account.api.Account;
@@ -33,8 +34,6 @@ import org.killbill.billing.account.api.DefaultMutableAccountData;
import org.killbill.billing.account.api.user.DefaultAccountChangeEvent;
import org.killbill.billing.account.api.user.DefaultAccountCreationEvent;
import org.killbill.billing.account.api.user.DefaultAccountCreationEvent.DefaultAccountData;
-import org.killbill.bus.api.PersistentBus;
-import org.killbill.bus.api.PersistentBus.EventBusException;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.events.AccountChangeInternalEvent;
@@ -42,6 +41,10 @@ import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.entity.DefaultPagination;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.billing.util.entity.dao.MockEntityDaoBase;
+import org.killbill.bus.api.PersistentBus;
+import org.killbill.bus.api.PersistentBus.EventBusException;
+import org.killbill.clock.Clock;
+import org.testng.Assert;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
@@ -52,10 +55,12 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account,
private final MockEntityDaoBase<AccountEmailModelDao, AccountEmail, AccountApiException> accountEmailSqlDao = new MockEntityDaoBase<AccountEmailModelDao, AccountEmail, AccountApiException>();
private final PersistentBus eventBus;
+ private final Clock clock;
@Inject
- public MockAccountDao(final PersistentBus eventBus) {
+ public MockAccountDao(final PersistentBus eventBus, final Clock clock) {
this.eventBus = eventBus;
+ this.clock = clock;
}
@Override
@@ -81,8 +86,8 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account,
final long tenantRecordId = context == null ? InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID
: context.getTenantRecordId();
final AccountChangeInternalEvent changeEvent = new DefaultAccountChangeEvent(account.getId(), currentAccount, account,
- accountRecordId, tenantRecordId, UUID.randomUUID()
- );
+ accountRecordId, tenantRecordId, UUID.randomUUID(),
+ clock.getUTCNow());
if (changeEvent.hasChanges()) {
try {
eventBus.post(changeEvent);
@@ -106,7 +111,7 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account,
@Override
public Pagination<AccountModelDao> searchAccounts(final String searchKey, final Long offset, final Long limit, final InternalTenantContext context) {
- final List<AccountModelDao> results = new LinkedList<AccountModelDao>();
+ final Collection<AccountModelDao> results = new LinkedList<AccountModelDao>();
int maxNbRecords = 0;
for (final AccountModelDao account : getAll(context)) {
maxNbRecords++;
@@ -145,7 +150,7 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account,
public void addEmail(final AccountEmailModelDao email, final InternalCallContext context) {
try {
accountEmailSqlDao.create(email, context);
- } catch (BillingExceptionBase billingExceptionBase) {
+ } catch (final BillingExceptionBase billingExceptionBase) {
Assert.fail(billingExceptionBase.toString());
}
}
@@ -170,5 +175,4 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account,
final AccountModelDao account = getById(accountId, context);
return account != null ? account.getBillingCycleDayLocal() : 0;
}
-
}
api/pom.xml 2(+1 -1)
diff --git a/api/pom.xml b/api/pom.xml
index 3ef8182..ef10494 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.1-SNAPSHOT</version>
+ <version>0.16.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-internal-api</artifactId>
diff --git a/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java b/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java
index f1429a0..7dced4f 100644
--- a/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java
+++ b/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java
@@ -53,8 +53,8 @@ public class InternalCallContext extends InternalTenantContext {
this.contextUserType = userType;
this.reasonCode = reasonCode;
this.comments = comment;
- this.createdDate = new DateTime(createdDate, DateTimeZone.UTC);
- this.updatedDate = updatedDate;
+ this.createdDate = toUTCDateTime(createdDate);
+ this.updatedDate = toUTCDateTime(updatedDate);
}
public InternalCallContext(final Long tenantRecordId, @Nullable final Long accountRecordId, final CallContext callContext) {
diff --git a/api/src/main/java/org/killbill/billing/callcontext/InternalTenantContext.java b/api/src/main/java/org/killbill/billing/callcontext/InternalTenantContext.java
index f35241d..8995fd1 100644
--- a/api/src/main/java/org/killbill/billing/callcontext/InternalTenantContext.java
+++ b/api/src/main/java/org/killbill/billing/callcontext/InternalTenantContext.java
@@ -25,7 +25,7 @@ import org.killbill.billing.util.callcontext.TenantContext;
/**
* Internal use only
*/
-public class InternalTenantContext {
+public class InternalTenantContext extends TimeAwareContext {
protected final Long tenantRecordId;
protected final Long accountRecordId;
diff --git a/api/src/main/java/org/killbill/billing/callcontext/TimeAwareContext.java b/api/src/main/java/org/killbill/billing/callcontext/TimeAwareContext.java
new file mode 100644
index 0000000..bb68757
--- /dev/null
+++ b/api/src/main/java/org/killbill/billing/callcontext/TimeAwareContext.java
@@ -0,0 +1,49 @@
+/*
+ * 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.callcontext;
+
+import java.util.Date;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+
+public class TimeAwareContext {
+
+ // From JDK to Joda (see http://www.joda.org/joda-time/userguide.html#JDK_Interoperability)
+ public DateTime toUTCDateTime(final Date date) {
+ return toUTCDateTime(new DateTime(date));
+ }
+
+ // Create a DateTime object forcing the time zone to be UTC
+ public DateTime toUTCDateTime(final DateTime dateTime) {
+ return toDateTime(dateTime, DateTimeZone.UTC);
+ }
+
+ // Create a DateTime object using the specified timezone (usually, the one on the account)
+ // TODO Should we cache the accountTimeZone in the context?
+ public DateTime toDateTime(final DateTime dateTime, final DateTimeZone accountTimeZone) {
+ return dateTime.toDateTime(accountTimeZone);
+ }
+
+ // Create a LocalDate object using the specified timezone (usually, the one on the account)
+ // TODO Should we cache the accountTimeZone in the context?
+ public LocalDate toLocalDate(final DateTime dateTime, final DateTimeZone accountTimeZone) {
+ return new LocalDate(dateTime, accountTimeZone);
+ }
+}
diff --git a/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java b/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
index b50879e..6c44797 100644
--- a/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
@@ -43,8 +43,6 @@ public interface InvoiceInternalApi {
public InvoicePayment getInvoicePaymentForAttempt(UUID paymentId, InternalTenantContext context) throws InvoiceApiException;
- public InvoicePayment getInvoicePaymentForRefund(UUID paymentId, InternalTenantContext context) throws InvoiceApiException;
-
public InvoicePayment getInvoicePaymentForChargeback(UUID paymentId, InternalTenantContext context) throws InvoiceApiException;
public Invoice getInvoiceForPaymentId(UUID paymentId, InternalTenantContext context) throws InvoiceApiException;
beatrix/pom.xml 2(+1 -1)
diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 37aa74e..0376865 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.1-SNAPSHOT</version>
+ <version>0.16.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-beatrix</artifactId>
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 b97040b..2fe3884 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
@@ -943,9 +943,9 @@ public class TestOverdueIntegration extends TestOverdueBase {
createPaymentAndCheckForCompletion(account, invoice, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
} else {
if (extraPayment) {
- createPaymentAndCheckForCompletion(account, invoice, NextEvent.BLOCK, NextEvent.TAG, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ createPaymentAndCheckForCompletion(account, invoice, NextEvent.BLOCK, NextEvent.TAG, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
} else {
- createPaymentAndCheckForCompletion(account, invoice, NextEvent.BLOCK, NextEvent.TAG, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ createPaymentAndCheckForCompletion(account, invoice, NextEvent.BLOCK, NextEvent.TAG, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
}
}
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
index 9575752..f3fa2f8 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
@@ -84,10 +84,10 @@ public class TestOverdueWithSubscriptionCancellation extends TestOverdueBase {
// Cancel addOn1 one day after
clock.addDays(1);
- cancelEntitlementAndCheckForCompletion(addOn1, clock.getUTCNow(), NextEvent.BLOCK, NextEvent.CANCEL);
+ cancelEntitlementAndCheckForCompletion(addOn1, clock.getUTCNow(), NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE);
// DAY 30 have to get out of trial before first payment
- addDaysAndCheckForCompletion(29, NextEvent.PHASE, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+ addDaysAndCheckForCompletion(29, NextEvent.PHASE, NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
@@ -135,12 +135,12 @@ public class TestOverdueWithSubscriptionCancellation extends TestOverdueBase {
// Cancel bundle 2 one day after (2012-05-02)
clock.addDays(1);
- cancelEntitlementAndCheckForCompletion(baseEntitlement2, clock.getUTCNow(), NextEvent.BLOCK, NextEvent.CANCEL);
+ cancelEntitlementAndCheckForCompletion(baseEntitlement2, clock.getUTCNow(), NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE);
final SubscriptionBase cancelledBaseSubscription2 = ((DefaultEntitlement) entitlementApi.getEntitlementForId(baseEntitlement2.getId(), callContext)).getSubscriptionBase();
assertTrue(cancelledBaseSubscription2.getState() == EntitlementState.CANCELLED);
// DAY 30 have to get out of trial before first payment (2012-05-31)
- addDaysAndCheckForCompletion(29, NextEvent.PHASE, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+ addDaysAndCheckForCompletion(29, NextEvent.PHASE, NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
invoiceChecker.checkInvoice(account.getId(),
4,
callContext,
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java
index 7915e6e..e32ab28 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java
@@ -55,7 +55,6 @@ public class TestBundleTransfer extends TestIntegrationBase {
@Test(groups = "slow")
public void testBundleTransferWithBPAnnualOnly() throws Exception {
-
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(3));
// Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
@@ -88,23 +87,21 @@ public class TestBundleTransfer extends TestIntegrationBase {
transferApi.transferBundle(account.getId(), newAccount.getId(), "externalKey", clock.getUTCNow(), false, false, callContext);
assertListenerStatus();
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(newAccount.getId(), callContext);
+ final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(newAccount.getId(), callContext);
assertEquals(invoices.size(), 1);
final List<InvoiceItem> invoiceItems = invoices.get(0).getInvoiceItems();
assertEquals(invoiceItems.size(), 1);
- InvoiceItem theItem = invoiceItems.get(0);
+ final InvoiceItem theItem = invoiceItems.get(0);
assertTrue(theItem.getStartDate().compareTo(new LocalDate(2012, 5, 11)) == 0);
assertTrue(theItem.getEndDate().compareTo(new LocalDate(2013, 5, 11)) == 0);
assertTrue(theItem.getAmount().compareTo(new BigDecimal("2399.9500")) == 0);
checkNoMoreInvoiceToGenerate(account);
-
}
@Test(groups = "slow")
public void testBundleTransferWithBPMonthlyOnly() throws Exception {
-
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
// Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
@@ -249,7 +246,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 4, 1), new BigDecimal("399.95"), TransactionStatus.SUCCESS, secondInvoice.getId(), Currency.USD));
// Move past the phase for simplicity
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(30);
assertListenerStatus();
final Invoice thirdInvoice = invoiceChecker.checkInvoice(account.getId(), 3, callContext,
@@ -270,7 +267,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
final DateTime now = clock.getUTCNow();
final LocalDate transferDay = now.toLocalDate();
- busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.TRANSFER, NextEvent.TRANSFER, NextEvent.INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.TRANSFER, NextEvent.TRANSFER, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
final UUID newBundleId = entitlementApi.transferEntitlements(account.getId(), newAccount.getId(), bundleExternalKey, transferDay, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
@@ -313,6 +310,5 @@ public class TestBundleTransfer extends TestIntegrationBase {
}
checkNoMoreInvoiceToGenerate(account);
-
}
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
index 3a8a532..5c0e974 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
@@ -50,7 +50,6 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
@Test(groups = "slow")
public void testRetirePlan() throws Exception {
-
// Catalog v1 starts in 2011-01-01
// Catalog v2 starts in 2015-12-01
final LocalDate today = new LocalDate(2015, 11, 5);
@@ -85,7 +84,7 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
try {
entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
fail(); // force to fail is there is not an exception
- } catch (EntitlementApiException e) {
+ } catch (final EntitlementApiException e) {
assertTrue(e.getLocalizedMessage().startsWith("Could not find a plan matching: (product: 'Pistol', billing period: 'MONTHLY'"));
}
@@ -96,15 +95,13 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
assertEquals(invoices.size(), 3);
- for (Invoice invoice : invoices) {
+ for (final Invoice invoice : invoices) {
assertEquals(invoice.getInvoiceItems().get(0).getPlanName(), "pistol-monthly");
}
-
}
@Test(groups = "slow")
public void testRetireProduct() throws Exception {
-
// Catalog v1 starts in 2011-01-01
// Catalog v3 starts in 2016-01-01
final LocalDate today = new LocalDate(2015, 11, 5);
@@ -144,7 +141,7 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
try {
entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
fail(); // force to fail is there is not an exception
- } catch (EntitlementApiException e) {
+ } catch (final EntitlementApiException e) {
assertTrue(e.getLocalizedMessage().startsWith("Could not find any product named 'Pistol'"));
}
@@ -155,15 +152,13 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
assertEquals(invoices.size(), 4);
- for (Invoice invoice : invoices) {
+ for (final Invoice invoice : invoices) {
assertEquals(invoice.getInvoiceItems().get(0).getPlanName(), "pistol-monthly");
}
-
}
@Test(groups = "slow")
public void testRetirePriceList() throws Exception {
-
// Catalog v1 starts in 2011-01-01
// Catalog v2 starts in 2015-12-01
// Catalog v3 starts in 2016-01-01
@@ -203,7 +198,7 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
try {
entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
fail(); // force to fail is there is not an exception
- } catch (EntitlementApiException e) {
+ } catch (final EntitlementApiException e) {
assertTrue(e.getLocalizedMessage().startsWith("Could not find any product named 'Pistol'"));
}
@@ -213,7 +208,7 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
assertListenerStatus();
// Move out a month.
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addMonths(1);
assertListenerStatus();
@@ -225,7 +220,5 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
assertTrue(invoices.get(2).getInvoiceItems().get(0).getPhaseName().equals("discount-pistol-monthly-discount"));
assertTrue(invoices.get(3).getInvoiceItems().get(0).getPhaseName().equals("discount-pistol-monthly-discount"));
assertTrue(invoices.get(4).getInvoiceItems().get(0).getPhaseName().equals("discount-pistol-monthly-evergreen"));
-
}
-
}
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 b1ff283..27642ab 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
@@ -27,6 +27,7 @@ import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Interval;
import org.joda.time.LocalDate;
+import org.killbill.billing.ErrorCode;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountData;
import org.killbill.billing.api.TestApiListener.NextEvent;
@@ -65,7 +66,6 @@ public class TestIntegration extends TestIntegrationBase {
@Test(groups = "slow")
public void testCancelBPWithAOTheSameDay() throws Exception {
-
final AccountData accountData = getAccountData(1);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
@@ -103,7 +103,7 @@ public class TestIntegration extends TestIntegrationBase {
addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
- Invoice invoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext, expectedInvoices);
+ final Invoice invoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext, expectedInvoices);
paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 4, 1), new BigDecimal("399.95"), TransactionStatus.SUCCESS, invoice.getId(), Currency.USD));
expectedInvoices.clear();
@@ -128,16 +128,13 @@ public class TestIntegration extends TestIntegrationBase {
expectedInvoices);
checkNoMoreInvoiceToGenerate(account);
-
}
@Test(groups = "slow")
public void testBasePlanCompleteWithBillingDayInPast() throws Exception {
-
final int billingDay = 31;
final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
- log.info("Beginning test with BCD of " + billingDay);
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
// set clock to the initial start date
@@ -213,7 +210,7 @@ public class TestIntegration extends TestIntegrationBase {
invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
- addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, expectedInvoices);
invoiceChecker.checkChargedThroughDate(subscription.getId(), secondRecurringPistolDate, callContext);
expectedInvoices.clear();
@@ -239,21 +236,17 @@ public class TestIntegration extends TestIntegrationBase {
baseEntitlement = cancelEntitlementAndCheckForCompletion(baseEntitlement, clock.getUTCNow(), NextEvent.BLOCK);
// MOVE AFTER CANCEL DATE AND EXPECT EVENT : NextEvent.CANCEL
- addDaysAndCheckForCompletion(31, NextEvent.CANCEL);
+ addDaysAndCheckForCompletion(31, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 7, 31), callContext);
checkNoMoreInvoiceToGenerate(account);
-
- log.info("TEST PASSED !");
}
@Test(groups = "slow")
public void testBasePlanCompleteWithBillingDayAlignedWithTrial() throws Exception {
-
final int billingDay = 2;
final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
- log.info("Beginning test with BCD of " + billingDay);
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
// set clock to the initial start date
@@ -292,17 +285,15 @@ public class TestIntegration extends TestIntegrationBase {
//
- TestDryRunArguments dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null, SubscriptionEventType.CHANGE,
- subscription.getId(), subscription.getBundleId(), null, null);
+ final TestDryRunArguments dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null, SubscriptionEventType.CHANGE,
+ subscription.getId(), subscription.getBundleId(), null, null);
try {
invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
Assert.fail("Call should return no invoices");
- } catch (InvoiceApiException e) {
- System.out.print("foo");
-
+ } catch (final InvoiceApiException e) {
+ assertEquals(e.getCode(), ErrorCode.INVOICE_NOTHING_TO_DO.getCode());
}
-
baseEntitlement = changeEntitlementAndCheckForCompletion(baseEntitlement, "Pistol", BillingPeriod.MONTHLY, null);
subscription = subscriptionDataFromSubscription(baseEntitlement.getSubscriptionBase());
@@ -311,7 +302,7 @@ public class TestIntegration extends TestIntegrationBase {
//
final LocalDate firstRecurringPistolDate = subscription.getChargedThroughDate().toLocalDate();
final LocalDate secondRecurringPistolDate = firstRecurringPistolDate.plusMonths(1);
- addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 2), new LocalDate(2012, 5, 2), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
invoiceChecker.checkChargedThroughDate(subscription.getId(), secondRecurringPistolDate, callContext);
@@ -335,21 +326,18 @@ public class TestIntegration extends TestIntegrationBase {
//
cancelEntitlementAndCheckForCompletion(baseEntitlement, clock.getUTCNow(), NextEvent.BLOCK);
- // MOVE AFTER CANCEL DATE AND EXPECT EVENT : NextEvent.CANCEL
- addDaysAndCheckForCompletion(31, NextEvent.CANCEL);
+ // MOVE AFTER CANCEL DATE
+ addDaysAndCheckForCompletion(31, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 8, 2), callContext);
checkNoMoreInvoiceToGenerate(account);
-
}
@Test(groups = "slow")
public void testBasePlanCompleteWithBillingDayInFuture() throws Exception {
-
final int billingDay = 3;
final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
- log.info("Beginning test with BCD of " + billingDay);
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
// set clock to the initial start date
@@ -401,7 +389,7 @@ public class TestIntegration extends TestIntegrationBase {
//
final LocalDate firstRecurringPistolDate = subscription.getChargedThroughDate().toLocalDate();
final LocalDate secondRecurringPistolDate = firstRecurringPistolDate.plusMonths(1);
- addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 3), new LocalDate(2012, 5, 3), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
invoiceChecker.checkChargedThroughDate(subscription.getId(), secondRecurringPistolDate, callContext);
@@ -426,13 +414,10 @@ public class TestIntegration extends TestIntegrationBase {
baseEntitlement = cancelEntitlementAndCheckForCompletion(baseEntitlement, clock.getUTCNow(), NextEvent.BLOCK);
// MOVE AFTER CANCEL DATE AND EXPECT EVENT : NextEvent.CANCEL
- addDaysAndCheckForCompletion(31, NextEvent.CANCEL);
+ addDaysAndCheckForCompletion(31, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 8, 3), callContext);
checkNoMoreInvoiceToGenerate(account);
-
- log.info("TEST PASSED !");
-
}
@Test(groups = {"stress"}, enabled = false)
@@ -494,7 +479,6 @@ public class TestIntegration extends TestIntegrationBase {
// MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
final Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
- log.info("Moving clock from" + clock.getUTCNow() + " to " + clock.getUTCNow().plusDays(3));
clock.addDays(3);
final DefaultEntitlement aoEntitlement1 = addAOEntitlementAndCheckForCompletion(baseEntitlement.getBundleId(), "Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY,
@@ -504,36 +488,29 @@ public class TestIntegration extends TestIntegrationBase {
NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
// MOVE CLOCK A LITTLE BIT MORE -- EITHER STAY IN TRIAL OR GET OUT
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
- log.info("Moving clock from" + clock.getUTCNow() + " to " + clock.getUTCNow().plusDays(28));
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(28);// 26 / 5
assertListenerStatus();
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
- log.info("Moving clock from" + clock.getUTCNow() + " to " + clock.getUTCNow().plusDays(3));
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(3);// 29 / 5
assertListenerStatus();
- log.info("Moving clock from" + clock.getUTCNow() + " to " + clock.getUTCNow().plusDays(10));
clock.addDays(10);// 8 / 6
assertListenerStatus();
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
- log.info("Moving clock from" + clock.getUTCNow() + " to " + clock.getUTCNow().plusDays(18));
clock.addDays(18);// 26 / 6
assertListenerStatus();
- log.info("Moving clock from" + clock.getUTCNow() + " to " + clock.getUTCNow().plusDays(3));
clock.addDays(3);
assertListenerStatus();
checkNoMoreInvoiceToGenerate(account);
-
}
@Test(groups = "slow")
public void testCreateMultipleBPWithSameExternalKey() throws Exception {
-
final DateTime initialDate = new DateTime(2012, 4, 25, 0, 13, 42, 0, testTimeZone);
clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
@@ -546,7 +523,7 @@ public class TestIntegration extends TestIntegrationBase {
final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
final SubscriptionBundle initialBundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey("bundleKey", callContext);
- busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CANCEL);
+ busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE);
baseEntitlement.cancelEntitlementWithPolicy(EntitlementActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
@@ -565,7 +542,6 @@ public class TestIntegration extends TestIntegrationBase {
assertEquals(newBaseEntitlement.getState(), EntitlementState.ACTIVE);
checkNoMoreInvoiceToGenerate(account);
-
}
@Test(groups = "slow")
@@ -573,7 +549,6 @@ public class TestIntegration extends TestIntegrationBase {
final DateTime initialDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
final int billingDay = 2;
- log.info("Beginning test with BCD of " + billingDay);
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
final UUID accountId = account.getId();
assertNotNull(account);
@@ -581,9 +556,9 @@ public class TestIntegration extends TestIntegrationBase {
// set clock to the initial start date
clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
- String productName = "Shotgun";
- BillingPeriod term = BillingPeriod.MONTHLY;
- String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+ final String productName = "Shotgun";
+ final BillingPeriod term = BillingPeriod.MONTHLY;
+ final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
//
// CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
@@ -594,7 +569,7 @@ public class TestIntegration extends TestIntegrationBase {
//
// VERIFY CTD HAS BEEN SET
//
- DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) baseEntitlement.getSubscriptionBase();
+ final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) baseEntitlement.getSubscriptionBase();
final DateTime startDate = subscription.getCurrentPhaseStart();
final BigDecimal rate = subscription.getCurrentPhase().getFixed().getPrice().getPrice(Currency.USD);
verifyTestResult(accountId, subscription.getId(), startDate, null, rate, clock.getUTCNow(), 1);
@@ -610,8 +585,7 @@ public class TestIntegration extends TestIntegrationBase {
// PAUSE THE ENTITLEMENT
DefaultEntitlement entitlement = (DefaultEntitlement) entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
- busHandler.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK);
- busHandler.pushExpectedEvent(NextEvent.INVOICE);
+ busHandler.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.INVOICE);
entitlementApi.pause(entitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
@@ -628,8 +602,7 @@ public class TestIntegration extends TestIntegrationBase {
// MOVE CLOCK FORWARD ADN CHECK THERE IS NO NEW INVOICE
clock.addDeltaFromReality(AT_LEAST_ONE_MONTH_MS);
- busHandler.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK);
- busHandler.pushExpectedEvent(NextEvent.INVOICE);
+ busHandler.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE);
entitlementApi.resume(entitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
@@ -638,14 +611,10 @@ public class TestIntegration extends TestIntegrationBase {
new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 5), new LocalDate(2012, 4, 5), InvoiceItemType.CBA_ADJ, new BigDecimal("-224.96")));
checkNoMoreInvoiceToGenerate(account);
-
}
@Test(groups = "slow")
public void testForMultipleRecurringPhases() throws Exception {
-
- log.info("Starting testForMultipleRecurringPhases");
-
final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
clock.setDeltaFromReality(initialCreationDate.getMillis() - clock.getUTCNow().getMillis());
@@ -675,7 +644,7 @@ public class TestIntegration extends TestIntegrationBase {
assertListenerStatus();
}
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDeltaFromReality(AT_LEAST_ONE_MONTH_MS);
assertListenerStatus();
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
index cf2dae1..fef40bc 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
@@ -20,6 +20,7 @@ package org.killbill.billing.beatrix.integration;
import java.math.BigDecimal;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -308,10 +309,12 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
}
protected void checkNoMoreInvoiceToGenerate(final Account account) {
+ busHandler.pushExpectedEvent(NextEvent.NULL_INVOICE);
try {
invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), null, callContext);
fail("Should not have generated an extra invoice");
} catch (final InvoiceApiException e) {
+ assertListenerStatus();
assertEquals(e.getCode(), ErrorCode.INVOICE_NOTHING_TO_DO.getCode());
}
}
@@ -467,11 +470,15 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
}
protected Payment refundPaymentAndCheckForCompletion(final Account account, final Payment payment, final NextEvent... events) {
+ return refundPaymentAndCheckForCompletion(account, payment, payment.getPurchasedAmount(), payment.getCurrency(), events);
+ }
+
+ protected Payment refundPaymentAndCheckForCompletion(final Account account, final Payment payment, final BigDecimal amount, final Currency currency, final NextEvent... events) {
return doCallAndCheckForCompletion(new Function<Void, Payment>() {
@Override
public Payment apply(@Nullable final Void input) {
try {
- return paymentApi.createRefundWithPaymentControl(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
+ return paymentApi.createRefundWithPaymentControl(account, payment.getId(), amount, currency, UUID.randomUUID().toString(),
PLUGIN_PROPERTIES, PAYMENT_OPTIONS, refreshedCallContext());
} catch (final PaymentApiException e) {
fail(e.toString());
@@ -501,18 +508,21 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
}
protected Payment refundPaymentWithInvoiceItemAdjAndCheckForCompletion(final Account account, final Payment payment, final Map<UUID, BigDecimal> iias, final NextEvent... events) {
+ return refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment, payment.getPurchasedAmount(), payment.getCurrency(), iias, events);
+ }
+
+ protected Payment refundPaymentWithInvoiceItemAdjAndCheckForCompletion(final Account account, final Payment payment, final BigDecimal amount, final Currency currency, final Map<UUID, BigDecimal> iias, final NextEvent... events) {
return doCallAndCheckForCompletion(new Function<Void, Payment>() {
@Override
public Payment apply(@Nullable final Void input) {
-
- final List<PluginProperty> properties = new ArrayList<PluginProperty>();
+ final Collection<PluginProperty> properties = new ArrayList<PluginProperty>();
final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_REFUND_WITH_ADJUSTMENTS, "true", false);
properties.add(prop1);
final PluginProperty prop2 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_REFUND_IDS_WITH_AMOUNT_KEY, iias, false);
properties.add(prop2);
try {
- return paymentApi.createRefundWithPaymentControl(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
+ return paymentApi.createRefundWithPaymentControl(account, payment.getId(), amount, currency, UUID.randomUUID().toString(),
properties, PAYMENT_OPTIONS, refreshedCallContext());
} catch (final PaymentApiException e) {
fail(e.toString());
@@ -522,18 +532,21 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
}, events);
}
- protected void createChargeBackAndCheckForCompletion(final Account account, final Payment payment, final NextEvent... events) {
- doCallAndCheckForCompletion(new Function<Void, Void>() {
+ protected Payment createChargeBackAndCheckForCompletion(final Account account, final Payment payment, final NextEvent... events) {
+ return createChargeBackAndCheckForCompletion(account, payment, payment.getPurchasedAmount(), payment.getCurrency(), events);
+ }
+
+ protected Payment createChargeBackAndCheckForCompletion(final Account account, final Payment payment, final BigDecimal amount, final Currency currency, final NextEvent... events) {
+ return doCallAndCheckForCompletion(new Function<Void, Payment>() {
@Override
- public Void apply(@Nullable final Void input) {
+ public Payment apply(@Nullable final Void input) {
try {
- paymentApi.createChargebackWithPaymentControl(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
- PAYMENT_OPTIONS, refreshedCallContext());
- } catch (PaymentApiException e) {
+ return paymentApi.createChargebackWithPaymentControl(account, payment.getId(), amount, currency, UUID.randomUUID().toString(),
+ PAYMENT_OPTIONS, refreshedCallContext());
+ } catch (final PaymentApiException e) {
fail(e.toString());
return null;
}
- return null;
}
}, events);
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
index 403343f..7c34a8e 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
@@ -137,7 +137,7 @@ public class TestIntegrationWithAutoInvoiceOffTag extends TestIntegrationBase {
add_AUTO_INVOICING_OFF_Tag(bpEntitlement.getSubscriptionBase().getBundleId(), ObjectType.BUNDLE);
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(40); // DAY 40 out of trial
assertListenerStatus();
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithDifferentBillingPeriods.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithDifferentBillingPeriods.java
index 55c5715..270f9a1 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithDifferentBillingPeriods.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithDifferentBillingPeriods.java
@@ -153,7 +153,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
// Move to 1020-08-01
- busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(20);
clock.addMonths(2);
assertListenerStatus();
@@ -199,7 +199,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
// 2012-5-12
clock.addDays(10);
- busHandler.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK, NextEvent.INVOICE);
+ busHandler.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.INVOICE);
entitlementApi.pause(bpEntitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
@@ -218,7 +218,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
// 2012-6-4
clock.addDays(23);
- busHandler.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
entitlementApi.resume(bpEntitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
@@ -230,7 +230,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 4), new LocalDate(2012, 6, 4), InvoiceItemType.CBA_ADJ, new BigDecimal("-2327.62")));
invoiceChecker.checkInvoice(invoices.get(3).getId(), callContext, toBeChecked);
- busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addYears(1);
assertListenerStatus();
@@ -307,7 +307,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 12), new LocalDate(2013, 5, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-2327.62")));
invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
- busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addYears(1);
assertListenerStatus();
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
index fb08783..e79eb43 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
@@ -18,7 +18,10 @@
package org.killbill.billing.beatrix.integration;
import java.math.BigDecimal;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.UUID;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
@@ -35,6 +38,7 @@ import org.killbill.billing.entitlement.api.DefaultEntitlement;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.api.InvoicePaymentType;
import org.killbill.billing.invoice.model.ExternalChargeInvoiceItem;
import org.killbill.billing.payment.api.Payment;
import org.killbill.billing.payment.api.PluginProperty;
@@ -74,11 +78,10 @@ public class TestInvoicePayment extends TestIntegrationBase {
// Invoice is partially paid
final Payment payment1 = paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), new BigDecimal("249.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
- // TODO See https://github.com/killbill/killbill/issues/482
- //Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("239.95")), 0);
- assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
// 2012-06-30
addDaysAndCheckForCompletion(30, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
@@ -89,24 +92,244 @@ public class TestInvoicePayment extends TestIntegrationBase {
// Invoice is fully paid
final Payment payment2 = paymentChecker.checkPayment(account.getId(), 2, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 6, 30), new BigDecimal("249.95"), TransactionStatus.SUCCESS, invoice3.getId(), Currency.USD));
Assert.assertEquals(payment2.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment2.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("239.95")), 0);
Assert.assertEquals(invoice3.getBalance().compareTo(BigDecimal.ZERO), 0);
- assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
// Fully pay the second invoice
final Payment payment3 = createPaymentAndCheckForCompletion(account, invoice2, invoice2.getBalance(), account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
paymentChecker.checkPayment(account.getId(), 3, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 6, 30), new BigDecimal("239.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
Assert.assertEquals(payment3.getPurchasedAmount().compareTo(new BigDecimal("239.95")), 0);
+ Assert.assertEquals(payment3.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("239.95")), 0);
invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
invoice3 = invoiceUserApi.getInvoice(invoice3.getId(), callContext);
Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.ZERO), 0);
Assert.assertEquals(invoice3.getBalance().compareTo(BigDecimal.ZERO), 0);
- assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
}
@Test(groups = "slow")
- public void testPartialPayments() throws Exception {
+ public void testPartialRefunds() throws Exception {
+ // 2012-05-01T00:03:42.000Z
+ clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+ final AccountData accountData = getAccountData(0);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
+
+ // 2012-05-31 => DAY 30 have to get out of trial {I0, P0}
+ addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+ Invoice invoice2 = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
+
+ // Invoice is fully paid
+ Payment payment1 = paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), new BigDecimal("249.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+ // Trigger first partial refund ($1), no adjustment
+ payment1 = refundPaymentAndCheckForCompletion(account, payment1, BigDecimal.TEN, Currency.USD, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getRefundedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().size(), 2);
+ Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+ invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+ Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+ // Trigger second partial refund ($1), with item adjustment
+ final Map<UUID, BigDecimal> iias = new HashMap<UUID, BigDecimal>();
+ iias.put(invoice2.getInvoiceItems().get(0).getId(), BigDecimal.ONE);
+ payment1 = refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment1, BigDecimal.ONE, Currency.USD, iias, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getRefundedAmount().compareTo(new BigDecimal("11")), 0);
+ Assert.assertEquals(payment1.getTransactions().size(), 3);
+ Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(2).getAmount().compareTo(BigDecimal.ONE), 0);
+ Assert.assertEquals(payment1.getTransactions().get(2).getProcessedAmount().compareTo(BigDecimal.ONE), 0);
+ invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+ Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+ // Trigger third partial refund ($10), with item adjustment
+ iias.put(invoice2.getInvoiceItems().get(0).getId(), BigDecimal.TEN);
+ payment1 = refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment1, BigDecimal.TEN, Currency.USD, iias, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getRefundedAmount().compareTo(new BigDecimal("21")), 0);
+ Assert.assertEquals(payment1.getTransactions().size(), 4);
+ Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(2).getAmount().compareTo(BigDecimal.ONE), 0);
+ Assert.assertEquals(payment1.getTransactions().get(2).getProcessedAmount().compareTo(BigDecimal.ONE), 0);
+ Assert.assertEquals(payment1.getTransactions().get(3).getAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(3).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+ invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+ Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+ }
+
+ @Test(groups = "slow")
+ public void testPartialPaymentByPaymentPluginThenChargeback() throws Exception {
+ // 2012-05-01T00:03:42.000Z
+ clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+ final AccountData accountData = getAccountData(0);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
+
+ // Trigger a partial payment on the next invoice
+ paymentPlugin.overrideNextProcessedAmount(BigDecimal.TEN);
+
+ // 2012-05-31 => DAY 30 have to get out of trial {I0, P0}
+ addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+ Invoice invoice2 = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
+
+ // Invoice is partially paid
+ Payment payment1 = paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), new BigDecimal("249.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().size(), 1);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("239.95")), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+ // Trigger chargeback
+ payment1 = createChargeBackAndCheckForCompletion(account, payment1, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().size(), 2);
+ Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+ invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+ Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+ }
+
+ @Test(groups = "slow")
+ public void testAUTO_PAY_OFFThenPartialPayment() throws Exception {
+ // 2012-05-01T00:03:42.000Z
+ clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+ final AccountData accountData = getAccountData(0);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
+
+ // Put the account in AUTO_PAY_OFF to make sure payment system does not try to pay the initial invoice
+ add_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
+
+ // 2012-05-31 => DAY 30 have to get out of trial {I0, P0}
+ addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE);
+
+ Invoice invoice2 = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
+
+ // Invoice is not paid
+ Assert.assertEquals(paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext).size(), 0);
+ Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+ // Trigger partial payment
+ final Payment payment1 = createPaymentAndCheckForCompletion(account, invoice2, BigDecimal.TEN, account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), BigDecimal.TEN, TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
+ Assert.assertEquals(payment1.getTransactions().size(), 1);
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+ invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+ Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("239.95")), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+ // Remove AUTO_PAY_OFF and verify the invoice is fully paid
+ remove_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ final Payment payment2 = paymentChecker.checkPayment(account.getId(), 2, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), new BigDecimal("239.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
+ Assert.assertEquals(payment2.getTransactions().size(), 1);
+ Assert.assertEquals(payment2.getPurchasedAmount().compareTo(new BigDecimal("239.95")), 0);
+ Assert.assertEquals(payment2.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("239.95")), 0);
+ invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+ Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+ }
+
+ @Test(groups = "slow")
+ public void testPaymentDifferentCurrencyByPaymentPlugin() throws Exception {
+ // 2012-05-01T00:03:42.000Z
+ clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+ final AccountData accountData = getAccountData(0);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
+
+ // Trigger a payment on the next invoice with a different currency ($249.95 <-> 225.44€)
+ paymentPlugin.overrideNextProcessedAmount(new BigDecimal("225.44"));
+ paymentPlugin.overrideNextProcessedCurrency(Currency.EUR);
+
+ // 2012-05-31 => DAY 30 have to get out of trial {I0, P0}
+ addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+ Invoice invoice2 = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
+
+ // Invoice is fully paid
+ Payment payment1 = paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), new BigDecimal("249.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().size(), 1);
+ Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getCurrency(), Currency.USD);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("225.44")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedCurrency(), Currency.EUR);
+ Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
+
+ // Trigger chargeback in the original currency
+ payment1 = createChargeBackAndCheckForCompletion(account, payment1, new BigDecimal("225.44"), Currency.EUR, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().size(), 2);
+ Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getCurrency(), Currency.USD);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("225.44")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(0).getProcessedCurrency(), Currency.EUR);
+ Assert.assertEquals(payment1.getTransactions().get(1).getAmount().compareTo(new BigDecimal("225.44")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getCurrency(), Currency.EUR);
+ Assert.assertEquals(payment1.getTransactions().get(1).getProcessedAmount().compareTo(new BigDecimal("225.44")), 0);
+ Assert.assertEquals(payment1.getTransactions().get(1).getProcessedCurrency(), Currency.EUR);
+ invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+ Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+ }
+
+ @Test(groups = "slow")
+ public void testPartialRefundsOnPartialPayments() throws Exception {
final AccountData accountData = getAccountData(1);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
@@ -120,8 +343,9 @@ public class TestInvoicePayment extends TestIntegrationBase {
final InvoiceItem item1 = invoiceUserApi.insertExternalCharges(account.getId(), clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), callContext).get(0);
assertListenerStatus();
+ // Trigger first partial payment ($4) on first invoice
final Invoice invoice = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
- final Payment payment1 = createPaymentAndCheckForCompletion(account, invoice, new BigDecimal("4.00"), account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ Payment payment1 = createPaymentAndCheckForCompletion(account, invoice, new BigDecimal("4.00"), account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
Invoice invoice1 = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
assertTrue(invoice1.getBalance().compareTo(new BigDecimal("6.00")) == 0);
@@ -131,7 +355,8 @@ public class TestInvoicePayment extends TestIntegrationBase {
BigDecimal accountBalance = invoiceUserApi.getAccountBalance(account.getId(), callContext);
assertTrue(accountBalance.compareTo(new BigDecimal("6.00")) == 0);
- final Payment payment2 = createPaymentAndCheckForCompletion(account, invoice, new BigDecimal("6.00"), account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ // Trigger second partial payment ($6) on first invoice
+ Payment payment2 = createPaymentAndCheckForCompletion(account, invoice, new BigDecimal("6.00"), account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
invoice1 = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
assertTrue(invoice1.getBalance().compareTo(BigDecimal.ZERO) == 0);
@@ -141,27 +366,56 @@ public class TestInvoicePayment extends TestIntegrationBase {
accountBalance = invoiceUserApi.getAccountBalance(account.getId(), callContext);
assertTrue(accountBalance.compareTo(BigDecimal.ZERO) == 0);
-/*
- This does not work since item is paid across multiple payments and so the mount is bigger than the payment.
-
- // Now, issue refund with item adjustment on first invoice/item
- paymentApi.createRefundWithItemsAdjustments(account, payment1.getId(), Sets.<UUID>newHashSet(item1.getId()), callContext);
-
+ // Refund first payment with item adjustment
+ final Map<UUID, BigDecimal> iias = new HashMap<UUID, BigDecimal>();
+ iias.put(item1.getId(), new BigDecimal("4.00"));
+ payment1 = refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment1, new BigDecimal("4.00"), Currency.USD, iias, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
invoice1 = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
assertTrue(invoice1.getBalance().compareTo(BigDecimal.ZERO) == 0);
-
accountBalance = invoiceUserApi.getAccountBalance(account.getId(), callContext);
assertTrue(accountBalance.compareTo(BigDecimal.ZERO) == 0);
-*/
-
- refundPaymentAndCheckForCompletion(account, payment1, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
-
+ // Refund second payment with item adjustment
+ iias.put(item1.getId(), new BigDecimal("6.00"));
+ payment2 = refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment2, new BigDecimal("6.00"), Currency.USD, iias, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
invoice1 = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
- assertTrue(invoice1.getBalance().compareTo(new BigDecimal("4.00")) == 0);
-
+ assertTrue(invoice1.getBalance().compareTo(BigDecimal.ZERO) == 0);
accountBalance = invoiceUserApi.getAccountBalance(account.getId(), callContext);
- assertTrue(accountBalance.compareTo(new BigDecimal("4.00")) == 0);
+ assertTrue(accountBalance.compareTo(BigDecimal.ZERO) == 0);
+
+ Assert.assertEquals(invoice1.getPayments().size(), 4);
+
+ // Verify links for payment 1
+ Assert.assertEquals(invoice1.getPayments().get(0).getAmount().compareTo(new BigDecimal("4.00")), 0);
+ Assert.assertNull(invoice1.getPayments().get(0).getLinkedInvoicePaymentId());
+ Assert.assertNull(invoice1.getPayments().get(0).getPaymentCookieId());
+ Assert.assertEquals(invoice1.getPayments().get(0).getPaymentId(), payment1.getId());
+ Assert.assertEquals(invoice1.getPayments().get(0).getType(), InvoicePaymentType.ATTEMPT);
+ Assert.assertTrue(invoice1.getPayments().get(0).isSuccess());
+
+ // Verify links for payment 2
+ Assert.assertEquals(invoice1.getPayments().get(1).getAmount().compareTo(new BigDecimal("6.00")), 0);
+ Assert.assertNull(invoice1.getPayments().get(1).getLinkedInvoicePaymentId());
+ Assert.assertNull(invoice1.getPayments().get(1).getPaymentCookieId());
+ Assert.assertEquals(invoice1.getPayments().get(1).getPaymentId(), payment2.getId());
+ Assert.assertEquals(invoice1.getPayments().get(1).getType(), InvoicePaymentType.ATTEMPT);
+ Assert.assertTrue(invoice1.getPayments().get(1).isSuccess());
+
+ // Verify links for refund 1
+ Assert.assertEquals(invoice1.getPayments().get(2).getAmount().compareTo(new BigDecimal("-4.00")), 0);
+ Assert.assertEquals(invoice1.getPayments().get(2).getLinkedInvoicePaymentId(), invoice1.getPayments().get(0).getId());
+ Assert.assertEquals(invoice1.getPayments().get(2).getPaymentCookieId(), payment1.getTransactions().get(1).getExternalKey());
+ Assert.assertEquals(invoice1.getPayments().get(2).getPaymentId(), payment1.getId());
+ Assert.assertEquals(invoice1.getPayments().get(2).getType(), InvoicePaymentType.REFUND);
+ Assert.assertTrue(invoice1.getPayments().get(2).isSuccess());
+
+ // Verify links for refund 2
+ Assert.assertEquals(invoice1.getPayments().get(3).getAmount().compareTo(new BigDecimal("-6.00")), 0);
+ Assert.assertEquals(invoice1.getPayments().get(3).getLinkedInvoicePaymentId(), invoice1.getPayments().get(1).getId());
+ Assert.assertEquals(invoice1.getPayments().get(3).getPaymentCookieId(), payment2.getTransactions().get(1).getExternalKey());
+ Assert.assertEquals(invoice1.getPayments().get(3).getPaymentId(), payment2.getId());
+ Assert.assertEquals(invoice1.getPayments().get(3).getType(), InvoicePaymentType.REFUND);
+ Assert.assertTrue(invoice1.getPayments().get(3).isSuccess());
}
@Test(groups = "slow")
@@ -189,6 +443,8 @@ public class TestInvoicePayment extends TestIntegrationBase {
assertTrue(invoice1.getPaidAmount().compareTo(BigDecimal.ZERO) == 0);
assertTrue(invoice1.getChargedAmount().compareTo(new BigDecimal("249.95")) == 0);
assertEquals(invoice1.getPayments().size(), 1);
+ assertEquals(invoice1.getPayments().get(0).getAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(invoice1.getPayments().get(0).getCurrency(), Currency.USD);
assertFalse(invoice1.getPayments().get(0).isSuccess());
final BigDecimal accountBalance1 = invoiceUserApi.getAccountBalance(account.getId(), callContext);
@@ -196,6 +452,12 @@ public class TestInvoicePayment extends TestIntegrationBase {
final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
assertEquals(payments.size(), 1);
+ assertEquals(payments.get(0).getPurchasedAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payments.get(0).getTransactions().size(), 1);
+ assertEquals(payments.get(0).getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+ assertEquals(payments.get(0).getTransactions().get(0).getCurrency(), Currency.USD);
+ assertEquals(payments.get(0).getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payments.get(0).getTransactions().get(0).getProcessedCurrency(), Currency.USD);
// Trigger the payment retry
busHandler.pushExpectedEvents(NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
@@ -211,5 +473,13 @@ public class TestInvoicePayment extends TestIntegrationBase {
final BigDecimal accountBalance2 = invoiceUserApi.getAccountBalance(account.getId(), callContext);
assertTrue(accountBalance2.compareTo(BigDecimal.ZERO) == 0);
+
+ final List<Payment> payments2 = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+ assertEquals(payments2.size(), 1);
+ assertEquals(payments2.get(0).getTransactions().size(), 2);
+ assertEquals(payments2.get(0).getTransactions().get(1).getAmount().compareTo(new BigDecimal("249.95")), 0);
+ assertEquals(payments2.get(0).getTransactions().get(1).getCurrency(), Currency.USD);
+ assertEquals(payments2.get(0).getTransactions().get(1).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
+ assertEquals(payments2.get(0).getTransactions().get(1).getProcessedCurrency(), Currency.USD);
}
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java
index 7d6e4c8..3c5e59f 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java
@@ -212,7 +212,7 @@ public class TestSubscription extends TestIntegrationBase {
specifierList.add(addOnEntitlementSpecifier1);
specifierList.add(addOnEntitlementSpecifier2);
- busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE, NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE, NextEvent.CREATE, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
final Entitlement entitlement = entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
checkNoMoreInvoiceToGenerate(account);
@@ -252,7 +252,6 @@ public class TestSubscription extends TestIntegrationBase {
@Test(groups = "slow")
public void testCancelFutureSubscriptionWithPolicy() throws Exception {
-
final LocalDate initialDate = new LocalDate(2015, 9, 1);
clock.setDay(initialDate);
@@ -274,8 +273,7 @@ public class TestSubscription extends TestIntegrationBase {
assertListenerStatus();
// Move off trial and reach start/cancellation date
- // NextEvent.INVOICE is required because of #467
- busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CANCEL);
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
clock.addDays(30);
assertListenerStatus();
@@ -286,7 +284,6 @@ public class TestSubscription extends TestIntegrationBase {
@Test(groups = "slow")
public void testCancelFutureSubscriptionWithRequestedDate() throws Exception {
-
final LocalDate initialDate = new LocalDate(2015, 9, 1);
clock.setDay(initialDate);
@@ -317,13 +314,12 @@ public class TestSubscription extends TestIntegrationBase {
assertListenerStatus();
// Move off trial and reach start/cancellation date
- busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CANCEL);
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
clock.addDays(30);
assertListenerStatus();
// Just to make sure we really don't invoice for anything move to next month
clock.addMonths(1);
assertListenerStatus();
-
}
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java
index 24fa051..c044cf1 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java
@@ -52,10 +52,8 @@ public class TestWithTimeZones extends TestIntegrationBase {
// Verify that recurring invoice items are correctly computed although we went through and out of daylight saving transitions
@Test(groups = "slow")
public void testWithDayLightSaving() throws Exception {
-
// Start with a date in daylight saving period and make sure we use a time of 8 hour so that we we reach standard time
// the next month where the difference is 9 hours, a transformation from DateTime to LocalDate with the account time zone would bring us a day earlier
- //
clock.setTime(new DateTime("2015-09-01T08:01:01.000Z"));
final DateTimeZone tz = DateTimeZone.forID("America/Juneau");
@@ -76,9 +74,9 @@ public class TestWithTimeZones extends TestIntegrationBase {
final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
- TestDryRunArguments dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null,
- SubscriptionEventType.START_BILLING, null, null, clock.getUTCNow(), null);
- Invoice dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
+ final TestDryRunArguments dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null,
+ SubscriptionEventType.START_BILLING, null, null, clock.getUTCNow(), null);
+ final Invoice dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 9, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
@@ -97,7 +95,7 @@ public class TestWithTimeZones extends TestIntegrationBase {
LocalDate startDate = new LocalDate(2015, 11, 1);
// We loop 18 times to go over a year and transitions several times between winter and summer (daylight saving)
for (int i = 0; i < 18; i++) {
- LocalDate endDate = startDate.plusMonths(1);
+ final LocalDate endDate = startDate.plusMonths(1);
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addMonths(1);
@@ -112,7 +110,6 @@ public class TestWithTimeZones extends TestIntegrationBase {
// Verify cancellation logic when we exit daylight savind period
@Test(groups = "slow")
public void testCancellationFrom_PDT_to_PST() throws Exception {
-
// Start with a date in daylight saving period (PDT) and make sure we use a time of 7 hour so that we we reach standard time (PST)
// the next month where the difference is 8 hours, a transformation from DateTime to LocalDate with the account time zone would bring us a day earlier
// (e.g new LocalDate("2015-12-01T07:01:01.000Z", tz) -> "2015-11-30.
@@ -147,23 +144,22 @@ public class TestWithTimeZones extends TestIntegrationBase {
// Verify first entitlement is correctly cancelled on the right date
Assert.assertEquals(entitlement.getEffectiveEndDate(), cancellationDate);
+ busHandler.pushExpectedEvent(NextEvent.NULL_INVOICE);
// We move the clock to the date of the next invoice notification 2015-12-01 07:01:01 (invoice is using a fixed offset of 7 hours and all billing events are converted using that offset)
clock.setTime(new DateTime("2015-12-01T07:01:02"));
- // Unfortunately we did not plug NullInvoice events so we cannot synchronize on that (See https://github.com/killbill/killbill/issues/448)
assertListenerStatus();
// We now move the clock to the date of the cancellation (one hour later), which match the cancellation day from the client point of view
//
- // For curious reader, the reason why the time end up being '8:01:0' comes from (https://github.com/killbill/killbill-commons/blob/master/clock/src/main/java/org/killbill/clock/ClockUtil.java#L51):
+ // For the curious reader, the reason why the time end up being '8:01:0' comes from (https://github.com/killbill/killbill-commons/blob/master/clock/src/main/java/org/killbill/clock/ClockUtil.java#L51):
// We compute a DateTime in the account timezone by specifying explicitly the year-month-day we want to end up in, and shoving *a time*. The reason why we end up on an 8:01:02 is not necessarily so important,
// What's important is that by construction that DateTime is guaranteed to match a LocalDate of 2015-12-01
- //
- busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK);
+ busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
clock.setTime(new DateTime("2015-12-01T08:01:02"));
assertListenerStatus();
- // Verify second that there was no repair (so the cancellation did correctly happen on the "2015-12-01"
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ // Verify second that there was no repair (so the cancellation did correctly happen on the "2015-12-01")
+ final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
Assert.assertEquals(invoices.size(), 1);
}
@@ -171,7 +167,6 @@ public class TestWithTimeZones extends TestIntegrationBase {
// an offset of 8 hours and then go through 7 hours so anyway we would stay in the same day.
@Test(groups = "slow")
public void testCancellationFrom_PST_to_PDT() throws Exception {
-
clock.setTime(new DateTime("2015-02-01T08:01:01.000Z"));
final DateTimeZone tz = DateTimeZone.forID("America/Los_Angeles");
@@ -203,16 +198,13 @@ public class TestWithTimeZones extends TestIntegrationBase {
// Verify first entitlement is correctly cancelled on the right date
Assert.assertEquals(entitlement.getEffectiveEndDate(), cancellationDate);
-
// We now move the clock to the date of the cancellation which match the cancellation day from the client point of view
- busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK);
+ busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
clock.setTime(new DateTime("2015-03-01T08:01:02"));
assertListenerStatus();
// Verify second that there was no repair (so the cancellation did correctly happen on the "2015-12-01"
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
Assert.assertEquals(invoices.size(), 1);
-
}
-
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java
index 54107d1..80ccbac 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java
@@ -60,7 +60,6 @@ public class TestConsumableInArrear extends TestIntegrationBase {
@Test(groups = "slow")
public void testWithNoUsageInPeriodAndOldUsage() throws Exception {
-
final AccountData accountData = getAccountData(1);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
@@ -80,12 +79,12 @@ public class TestConsumableInArrear extends TestIntegrationBase {
//
// ADD ADD_ON ON THE SAME DAY
//
- final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE);
+ final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.NULL_INVOICE);
setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 1), 99L, callContext);
setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 15), 100L, callContext);
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(30);
assertListenerStatus();
@@ -93,9 +92,10 @@ public class TestConsumableInArrear extends TestIntegrationBase {
new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.USAGE, new BigDecimal("5.90")));
- // We don't expect any invoice, but we want to give the system the time to verify there is nothing to do so we can fail
+ // We don't expect any invoice
+ busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE);
clock.addMonths(1);
- Thread.sleep(1000);
+ assertListenerStatus();
setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 6, 1), 50L, callContext);
setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 6, 16), 300L, callContext);
@@ -128,7 +128,6 @@ public class TestConsumableInArrear extends TestIntegrationBase {
@Test(groups = "slow")
public void testWithCancellation() throws Exception {
-
final AccountData accountData = getAccountData(1);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
@@ -148,12 +147,12 @@ public class TestConsumableInArrear extends TestIntegrationBase {
//
// ADD ADD_ON ON THE SAME DAY
//
- final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE);
+ final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.NULL_INVOICE);
setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 1), 99L, callContext);
setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 15), 100L, callContext);
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(30);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 2, callContext,
@@ -174,14 +173,13 @@ public class TestConsumableInArrear extends TestIntegrationBase {
invoiceChecker.checkInvoice(account.getId(), 3, callContext,
new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 5, 28), InvoiceItemType.USAGE, new BigDecimal("5.90")));
+ busHandler.pushExpectedEvent(NextEvent.NULL_INVOICE);
clock.addDays(4);
- Thread.sleep(1000);
-
+ assertListenerStatus();
}
@Test(groups = "slow")
public void testWithDayLightSaving() throws Exception {
-
clock.setTime(new DateTime("2015-09-01T08:01:01.000Z"));
final DateTimeZone tz = DateTimeZone.forID("America/Juneau");
@@ -212,20 +210,22 @@ public class TestConsumableInArrear extends TestIntegrationBase {
//
// ADD ADD_ON ON THE SAME DAY
//
- final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE);
+ final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.NULL_INVOICE);
assertListenerStatus();
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(30);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 2, callContext,
new ExpectedInvoiceItemCheck(new LocalDate(2015, 10, 1), new LocalDate(2016, 10, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
// 2015-11-1
+ busHandler.pushExpectedEvent(NextEvent.NULL_INVOICE);
clock.addMonths(1);
assertListenerStatus();
// 2015-12-1
+ busHandler.pushExpectedEvent(NextEvent.NULL_INVOICE);
clock.addMonths(1);
assertListenerStatus();
catalog/pom.xml 2(+1 -1)
diff --git a/catalog/pom.xml b/catalog/pom.xml
index 714169b..e75310f 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.1-SNAPSHOT</version>
+ <version>0.16.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-catalog</artifactId>
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
index 7cedb32..1955c5e 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -19,6 +21,8 @@ package org.killbill.billing.catalog;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
@@ -85,31 +89,24 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements
this.effectiveDateForExistingSubscriptons = in.getEffectiveDateForExistingSubscriptons();
this.product = (DefaultProduct) in.getProduct();
this.initialPhases = new DefaultPlanPhase[in.getInitialPhases().length];
- for (int i = 0; i< overrides.length - 1; i++) {
+ for (int i = 0; i < overrides.length - 1; i++) {
final DefaultPlanPhase newPhase = new DefaultPlanPhase(this, in.getInitialPhases()[i], overrides[i]);
initialPhases[i] = newPhase;
}
this.finalPhase = new DefaultPlanPhase(this, in.getFinalPhase(), overrides[overrides.length - 1]);
this.priceList = in.getPriceList();
}
- /* (non-Javadoc)
- * @see org.killbill.billing.catalog.IPlan#getEffectiveDateForExistingSubscriptons()
- */
+
@Override
public Date getEffectiveDateForExistingSubscriptons() {
return effectiveDateForExistingSubscriptons;
- } /* (non-Javadoc)
- * @see org.killbill.billing.catalog.IPlan#getPhases()
- */
+ }
@Override
public DefaultPlanPhase[] getInitialPhases() {
return initialPhases;
}
- /* (non-Javadoc)
- * @see org.killbill.billing.catalog.IPlan#getProduct()
- */
@Override
public Product getProduct() {
return product;
@@ -120,9 +117,6 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements
return priceList;
}
- /* (non-Javadoc)
- * @see org.killbill.billing.catalog.IPlan#getName()
- */
@Override
public String getName() {
return name;
@@ -163,9 +157,6 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements
return finalPhase.getRecurring() != null ? finalPhase.getRecurring().getBillingPeriod() : BillingPeriod.NO_BILLING_PERIOD;
}
- /* (non-Javadoc)
- * @see org.killbill.billing.catalog.IPlan#getPlansAllowedInBundle()
- */
@Override
public int getPlansAllowedInBundle() {
return plansAllowedInBundle;
@@ -176,10 +167,8 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements
*/
@Override
public Iterator<PlanPhase> getInitialPhaseIterator() {
- final ArrayList<PlanPhase> list = new ArrayList<PlanPhase>();
- for (final DefaultPlanPhase p : initialPhases) {
- list.add(p);
- }
+ final Collection<PlanPhase> list = new ArrayList<PlanPhase>();
+ Collections.addAll(list, initialPhases);
return list.iterator();
}
@@ -199,12 +188,10 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements
this.priceList = findPriceListForPlan(catalog);
}
-
-
@Override
public ValidationErrors validate(final StandaloneCatalog catalog, final ValidationErrors errors) {
if (effectiveDateForExistingSubscriptons != null &&
- catalog.getEffectiveDate().getTime() > effectiveDateForExistingSubscriptons.getTime()) {
+ catalog.getEffectiveDate().getTime() > effectiveDateForExistingSubscriptons.getTime()) {
errors.add(new ValidationError(String.format("Price effective date %s is before catalog effective date '%s'",
effectiveDateForExistingSubscriptons,
catalog.getEffectiveDate().getTime()),
@@ -253,8 +240,8 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements
@Override
public DateTime dateOfFirstRecurringNonZeroCharge(final DateTime subscriptionStartDate, final PhaseType initialPhaseType) {
- DateTime result = subscriptionStartDate.toDateTime();
- boolean skipPhase = initialPhaseType == null ? false : true;
+ DateTime result = subscriptionStartDate;
+ boolean skipPhase = initialPhaseType != null;
for (final PlanPhase phase : getAllPhases()) {
if (skipPhase) {
if (phase.getPhaseType() != initialPhaseType) {
@@ -318,9 +305,9 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements
@Override
public String toString() {
return "DefaultPlan [name=" + name + ", effectiveDateForExistingSubscriptons="
- + effectiveDateForExistingSubscriptons + ", product=" + product + ", initialPhases="
- + Arrays.toString(initialPhases) + ", finalPhase=" + finalPhase + ", plansAllowedInBundle="
- + plansAllowedInBundle + "]";
+ + effectiveDateForExistingSubscriptons + ", product=" + product + ", initialPhases="
+ + Arrays.toString(initialPhases) + ", finalPhase=" + finalPhase + ", plansAllowedInBundle="
+ + plansAllowedInBundle + "]";
}
private DefaultPriceList findPriceListForPlan(final StandaloneCatalog catalog) {
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java b/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
index 9b8df53..7bf1326 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
@@ -55,13 +55,10 @@ public class VersionedCatalogLoader implements CatalogLoader {
this.internalCallContextFactory = internalCallContextFactory;
}
- /* (non-Javadoc)
- * @see org.killbill.billing.catalog.io.ICatalogLoader#loadDefaultCatalog(java.lang.String)
- */
@Override
public VersionedCatalog loadDefaultCatalog(final String uriString) throws CatalogApiException {
try {
- List<URI> xmlURIs;
+ final List<URI> xmlURIs;
if (uriString.endsWith(XML_EXTENSION)) { // Assume its an xml file
xmlURIs = new ArrayList<URI>();
xmlURIs.add(new URI(uriString));
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
index ac2326a..5fcece4 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.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
@@ -24,7 +24,6 @@ import java.util.regex.Matcher;
import javax.annotation.Nullable;
-import org.joda.time.DateTime;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.BillingActionPolicy;
@@ -141,15 +140,14 @@ public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<Standal
}
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(overrides.getCallContext());
- return priceOverride.getOrCreateOverriddenPlan(defaultPlan, new DateTime(getEffectiveDate()), overrides.getOverrides(), internalCallContext);
+ return priceOverride.getOrCreateOverriddenPlan(defaultPlan, internalCallContext.toUTCDateTime(getEffectiveDate()), overrides.getOverrides(), internalCallContext);
}
@Override
public Plan findCurrentPlan(final String planName) throws CatalogApiException {
-
final Matcher m = DefaultPriceOverride.CUSTOM_PLAN_NAME_PATTERN.matcher(planName);
if (m.matches()) {
- final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(tenantRecordId, null);
+ final InternalTenantContext internalTenantContext = createInternalTenantContext();
return priceOverride.getOverriddenPlan(planName, standaloneCatalog, internalTenantContext);
}
return standaloneCatalog.findCurrentPlan(planName);
@@ -165,8 +163,8 @@ public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<Standal
final String planName = DefaultPlanPhase.planName(phaseName);
final Matcher m = DefaultPriceOverride.CUSTOM_PLAN_NAME_PATTERN.matcher(planName);
if (m.matches()) {
- final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(tenantRecordId, null);
- Plan plan = priceOverride.getOverriddenPlan(planName, standaloneCatalog, internalTenantContext);
+ final InternalTenantContext internalTenantContext = createInternalTenantContext();
+ final Plan plan = priceOverride.getOverriddenPlan(planName, standaloneCatalog, internalTenantContext);
return plan.findPhase(phaseName);
}
return standaloneCatalog.findCurrentPhase(phaseName);
@@ -241,4 +239,7 @@ public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<Standal
return standaloneCatalog.findCurrentPriceList(priceListName);
}
+ private InternalTenantContext createInternalTenantContext() {
+ return internalCallContextFactory.createInternalTenantContext(tenantRecordId, null);
+ }
}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
index b0aa352..dbe3b2b 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.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
@@ -65,21 +65,23 @@ import org.killbill.xmlloader.ValidationErrors;
public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPriceOverride> implements Catalog, StaticCatalog {
private final Clock clock;
+ private final InternalTenantContext internalTenantContext;
+ @XmlElement(name = "catalogVersion", required = true)
+ private final List<StandaloneCatalogWithPriceOverride> versions;
private String catalogName;
private BillingMode recurringBillingMode;
- @XmlElement(name = "catalogVersion", required = true)
- private List<StandaloneCatalogWithPriceOverride> versions;
-
// Required for JAXB deserialization
public VersionedCatalog() {
this.clock = null;
- versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
+ this.versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
+ this.internalTenantContext = new InternalTenantContext(null, null);
}
public VersionedCatalog(final Clock clock) {
this.clock = clock;
- versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
+ this.versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
+ this.internalTenantContext = new InternalTenantContext(null, null);
}
public VersionedCatalog(final Clock clock, final String catalogName, final BillingMode recurringBillingMode, final List<StandaloneCatalogWithPriceOverride> versions, final InternalTenantContext tenantContext) {
@@ -91,9 +93,9 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
final StandaloneCatalogWithPriceOverride catalogWithTenantInfo = new StandaloneCatalogWithPriceOverride(cur, tenantContext);
this.versions.add(catalogWithTenantInfo);
}
+ this.internalTenantContext = tenantContext;
}
-
//
// Private methods
//
@@ -154,16 +156,16 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
final DateTime subscriptionStartDate)
throws CatalogApiException {
final List<StandaloneCatalogWithPriceOverride> catalogs = versionsBeforeDate(requestedDate.toDate());
- if (catalogs.size() == 0) {
+ if (catalogs.isEmpty()) {
throw new CatalogApiException(ErrorCode.CAT_NO_CATALOG_FOR_GIVEN_DATE, requestedDate.toDate().toString());
}
for (int i = catalogs.size() - 1; i >= 0; i--) { // Working backwards to find the latest applicable plan
final StandaloneCatalogWithPriceOverride c = catalogs.get(i);
- Plan plan;
+ final Plan plan;
try {
plan = wrapper.findPlan(c);
- } catch (CatalogApiException e) {
+ } catch (final CatalogApiException e) {
if (e.getCode() != ErrorCode.CAT_NO_SUCH_PLAN.getCode()) {
throw e;
} else {
@@ -172,12 +174,12 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
}
}
- DateTime catalogEffectiveDate = new DateTime(c.getEffectiveDate());
+ final DateTime catalogEffectiveDate = internalTenantContext.toUTCDateTime(c.getEffectiveDate());
if (!subscriptionStartDate.isBefore(catalogEffectiveDate)) { // Its a new subscription this plan always applies
return plan;
} else { //Its an existing subscription
if (plan.getEffectiveDateForExistingSubscriptons() != null) { //if it is null any change to this does not apply to existing subscriptions
- final DateTime existingSubscriptionDate = new DateTime(plan.getEffectiveDateForExistingSubscriptons());
+ final DateTime existingSubscriptionDate = internalTenantContext.toUTCDateTime(plan.getEffectiveDateForExistingSubscriptons());
if (requestedDate.isAfter(existingSubscriptionDate)) { // this plan is now applicable to existing subs
return plan;
}
@@ -271,10 +273,10 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
@Override
public Plan createOrFindPlan(final String productName,
- final BillingPeriod term,
- final String priceListName,
- final PlanPhasePriceOverridesWithCallContext overrides,
- final DateTime requestedDate)
+ final BillingPeriod term,
+ final String priceListName,
+ final PlanPhasePriceOverridesWithCallContext overrides,
+ final DateTime requestedDate)
throws CatalogApiException {
return versionForDate(requestedDate).createOrFindCurrentPlan(productName, term, priceListName, overrides);
}
@@ -289,11 +291,11 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
@Override
public Plan createOrFindPlan(final String productName,
- final BillingPeriod term,
- final String priceListName,
- final PlanPhasePriceOverridesWithCallContext overrides,
- final DateTime requestedDate,
- final DateTime subscriptionStartDate)
+ final BillingPeriod term,
+ final String priceListName,
+ final PlanPhasePriceOverridesWithCallContext overrides,
+ final DateTime requestedDate,
+ final DateTime subscriptionStartDate)
throws CatalogApiException {
return findPlan(new PlanRequestWrapper(productName, term, priceListName, overrides), requestedDate, subscriptionStartDate);
}
@@ -434,7 +436,7 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
@Override
public Plan createOrFindCurrentPlan(final String productName, final BillingPeriod term,
- final String priceList, PlanPhasePriceOverridesWithCallContext overrides) throws CatalogApiException {
+ final String priceList, final PlanPhasePriceOverridesWithCallContext overrides) throws CatalogApiException {
return versionForDate(clock.getUTCNow()).createOrFindCurrentPlan(productName, term, priceList, overrides);
}
@@ -512,7 +514,7 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
}
@Override
- public boolean compliesWithLimits(String phaseName, String unit, double value) throws CatalogApiException {
+ public boolean compliesWithLimits(final String phaseName, final String unit, final double value) throws CatalogApiException {
return versionForDate(clock.getUTCNow()).compliesWithLimits(phaseName, unit, value);
}
}
currency/pom.xml 2(+1 -1)
diff --git a/currency/pom.xml b/currency/pom.xml
index dfec699..049a773 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.1-SNAPSHOT</version>
+ <version>0.16.2-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 c66ead0..83f81aa 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.1-SNAPSHOT</version>
+ <version>0.16.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-entitlement</artifactId>
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java
index 065076f..617db77 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.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
@@ -31,6 +31,7 @@ import javax.annotation.Nullable;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
+import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.PlanPhase;
@@ -41,10 +42,8 @@ import org.killbill.billing.entitlement.block.BlockingChecker.BlockingAggregator
import org.killbill.billing.entitlement.block.DefaultBlockingChecker.DefaultBlockingAggregator;
import org.killbill.billing.junction.DefaultBlockingState;
-import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
// Given an event stream (across one or multiple entitlements), insert the blocking events at the right place
public class BlockingStateOrdering extends EntitlementOrderingBase {
@@ -53,11 +52,11 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
private BlockingStateOrdering() {}
- public static void insertSorted(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone, final LinkedList<SubscriptionEvent> result) {
- INSTANCE.computeEvents(entitlements, accountTimeZone, result);
+ public static void insertSorted(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext, final LinkedList<SubscriptionEvent> result) {
+ INSTANCE.computeEvents(entitlements, accountTimeZone, internalTenantContext, result);
}
- private void computeEvents(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone, final LinkedList<SubscriptionEvent> result) {
+ private void computeEvents(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext, final LinkedList<SubscriptionEvent> result) {
final Collection<UUID> allEntitlementUUIDs = new HashSet<UUID>();
final Collection<BlockingState> blockingStates = new LinkedList<BlockingState>();
for (final Entitlement entitlement : entitlements) {
@@ -69,13 +68,13 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
// Trust the incoming ordering here: blocking states were sorted using ProxyBlockingStateDao#sortedCopy
for (final BlockingState bs : blockingStates) {
final List<SubscriptionEvent> newEvents = new ArrayList<SubscriptionEvent>();
- final int index = insertFromBlockingEvent(accountTimeZone, allEntitlementUUIDs, result, bs, bs.getEffectiveDate(), newEvents);
+ final int index = insertFromBlockingEvent(accountTimeZone, internalTenantContext, allEntitlementUUIDs, result, bs, bs.getEffectiveDate(), newEvents);
insertAfterIndex(result, newEvents, index);
}
}
// Returns the index and the newEvents generated from the incoming blocking state event. Those new events will all be created for the same effectiveDate and should be ordered.
- private int insertFromBlockingEvent(final DateTimeZone accountTimeZone, final Collection<UUID> allEntitlementUUIDs, final List<SubscriptionEvent> result, final BlockingState bs, final DateTime bsEffectiveDate, final List<SubscriptionEvent> newEvents) {
+ private int insertFromBlockingEvent(final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext, final Collection<UUID> allEntitlementUUIDs, final List<SubscriptionEvent> result, final BlockingState bs, final DateTime bsEffectiveDate, final Collection<SubscriptionEvent> newEvents) {
// Keep the current state per entitlement
final Map<UUID, TargetState> targetStates = new HashMap<UUID, TargetState>();
for (final UUID cur : allEntitlementUUIDs) {
@@ -134,7 +133,7 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
final List<SubscriptionEventType> eventTypes = curTargetState.addStateAndReturnEventTypes(bs);
for (final SubscriptionEventType t : eventTypes) {
- newEvents.add(toSubscriptionEvent(prevNext[0], prevNext[1], targetEntitlementId, bs, t, accountTimeZone));
+ newEvents.add(toSubscriptionEvent(prevNext[0], prevNext[1], targetEntitlementId, bs, t, accountTimeZone, internalTenantContext));
}
}
return index;
@@ -176,7 +175,8 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
}
private SubscriptionEvent toSubscriptionEvent(@Nullable final SubscriptionEvent prev, @Nullable final SubscriptionEvent next,
- final UUID entitlementId, final BlockingState in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone) {
+ final UUID entitlementId, final BlockingState in, final SubscriptionEventType eventType,
+ final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext) {
final Product prevProduct;
final Plan prevPlan;
final PlanPhase prevPlanPhase;
@@ -257,7 +257,8 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
nextPriceList,
nextBillingPeriod,
in.getCreatedDate(),
- accountTimeZone);
+ accountTimeZone,
+ internalTenantContext);
}
private void insertAfterIndex(final LinkedList<SubscriptionEvent> original, final Collection<SubscriptionEvent> newEvents, final int index) {
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
index ca69a1f..b0f2135 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
@@ -30,6 +30,7 @@ import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import org.killbill.billing.ErrorCode;
import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Plan;
@@ -76,6 +77,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
private final SecurityApi securityApi;
protected final EventsStreamBuilder eventsStreamBuilder;
protected final EntitlementDateHelper dateHelper;
+ protected final InternalTenantContext internalTenantContext;
protected final InternalCallContextFactory internalCallContextFactory;
protected final Clock clock;
protected final BlockingChecker checker;
@@ -98,14 +100,14 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
final InternalCallContextFactory internalCallContextFactory, final TenantContext tenantContext) throws EntitlementApiException {
this(eventsStreamBuilder.buildForEntitlement(entitlementId, tenantContext), eventsStreamBuilder,
entitlementApi, pluginExecution, blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
- entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory);
+ entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory.createInternalTenantContext(tenantContext), internalCallContextFactory);
}
public DefaultEntitlement(final EventsStream eventsStream, final EventsStreamBuilder eventsStreamBuilder,
final EntitlementApi entitlementApi, final EntitlementPluginExecution pluginExecution, final BlockingStateDao blockingStateDao,
final SubscriptionBaseInternalApi subscriptionInternalApi, final BlockingChecker checker,
final NotificationQueueService notificationQueueService, final EntitlementUtils entitlementUtils,
- final EntitlementDateHelper dateHelper, final Clock clock, final SecurityApi securityApi, final InternalCallContextFactory internalCallContextFactory) {
+ final EntitlementDateHelper dateHelper, final Clock clock, final SecurityApi securityApi, final InternalTenantContext internalTenantContext, final InternalCallContextFactory internalCallContextFactory) {
super(eventsStream.getEntitlementId(), eventsStream.getSubscriptionBase().getCreatedDate(), eventsStream.getSubscriptionBase().getUpdatedDate());
this.eventsStreamBuilder = eventsStreamBuilder;
this.eventsStream = eventsStream;
@@ -113,6 +115,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
this.entitlementApi = entitlementApi;
this.pluginExecution = pluginExecution;
this.subscriptionInternalApi = subscriptionInternalApi;
+ this.internalTenantContext = internalTenantContext;
this.internalCallContextFactory = internalCallContextFactory;
this.clock = clock;
this.securityApi = securityApi;
@@ -135,6 +138,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
in.getDateHelper(),
in.getClock(),
in.getSecurityApi(),
+ in.getInternalTenantContext(),
in.getInternalCallContextFactory());
}
@@ -164,6 +168,10 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
return dateHelper;
}
+ public InternalTenantContext getInternalTenantContext() {
+ return internalTenantContext;
+ }
+
public InternalCallContextFactory getInternalCallContextFactory() {
return internalCallContextFactory;
}
@@ -236,7 +244,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
@Override
public LocalDate getEffectiveStartDate() {
- return new LocalDate(getSubscriptionBase().getStartDate(), eventsStream.getAccountTimeZone());
+ return internalTenantContext.toLocalDate(getSubscriptionBase().getStartDate(), eventsStream.getAccountTimeZone());
}
@Override
@@ -438,8 +446,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
// (we don't want an entitlement cancel date one second or so after the subscription cancel date or add-ons cancellations
// computations won't work).
final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
- final LocalDate effectiveLocalDate = new LocalDate(updatedPluginContext.getEffectiveDate(), eventsStream.getAccountTimeZone());
- final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(effectiveLocalDate, getSubscriptionBase().getStartDate(), contextWithValidAccountRecordId);
+ final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), getSubscriptionBase().getStartDate(), contextWithValidAccountRecordId);
try {
// Cancel subscription base first, to correctly compute the add-ons entitlements we need to cancel (see below)
@@ -468,10 +475,14 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
final LocalDate cancellationDate;
switch (entitlementPolicy) {
case IMMEDIATE:
- cancellationDate = new LocalDate(clock.getUTCNow(), eventsStream.getAccountTimeZone());
+ cancellationDate = clock.getToday(eventsStream.getAccountTimeZone());
break;
case END_OF_TERM:
- cancellationDate = getSubscriptionBase().getChargedThroughDate() != null ? new LocalDate(getSubscriptionBase().getChargedThroughDate(), eventsStream.getAccountTimeZone()) : new LocalDate(clock.getUTCNow(), eventsStream.getAccountTimeZone());
+ if (getSubscriptionBase().getChargedThroughDate() != null) {
+ cancellationDate = internalTenantContext.toLocalDate(getSubscriptionBase().getChargedThroughDate(), eventsStream.getAccountTimeZone());
+ } else {
+ cancellationDate = clock.getToday(eventsStream.getAccountTimeZone());
+ }
break;
default:
throw new RuntimeException("Unsupported policy " + entitlementPolicy);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
index 0560b0e..6e6cd45 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
@@ -135,7 +135,6 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
public Entitlement doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(accountId, callContext);
try {
-
if (entitlementUtils.getFirstActiveSubscriptionIdForKeyOrNull(externalKey, contextWithValidAccountRecordId) != null) {
throw new EntitlementApiException(new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_ACTIVE_BUNDLE_KEY_EXISTS, externalKey));
}
@@ -337,7 +336,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
public Entitlement apply(final EventsStream eventsStream) {
return new DefaultEntitlement(eventsStream, eventsStreamBuilder, entitlementApi, pluginExecution,
blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
- entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory);
+ entitlementUtils, dateHelper, clock, securityApi, context, internalCallContextFactory);
}
});
}
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java
index 3667835..8a953e2 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.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
@@ -31,7 +31,7 @@ public class DefaultSubscription extends DefaultEntitlement implements Subscript
@Override
public LocalDate getBillingStartDate() {
- return new LocalDate(getSubscriptionBase().getStartDate(), getAccountTimeZone());
+ return internalTenantContext.toLocalDate(getSubscriptionBase().getStartDate(), getAccountTimeZone());
}
@Override
@@ -51,16 +51,16 @@ public class DefaultSubscription extends DefaultEntitlement implements Subscript
futureOrCurrentEndDate = futureOrCurrentEndDateForSubscription;
}
- return futureOrCurrentEndDate != null ? new LocalDate(futureOrCurrentEndDate, getAccountTimeZone()) : null;
+ return futureOrCurrentEndDate != null ? internalTenantContext.toLocalDate(futureOrCurrentEndDate, getAccountTimeZone()) : null;
}
@Override
public LocalDate getChargedThroughDate() {
- return getSubscriptionBase().getChargedThroughDate() != null ? new LocalDate(getSubscriptionBase().getChargedThroughDate(), getAccountTimeZone()) : null;
+ return getSubscriptionBase().getChargedThroughDate() != null ? internalTenantContext.toLocalDate(getSubscriptionBase().getChargedThroughDate(), getAccountTimeZone()) : null;
}
@Override
public List<SubscriptionEvent> getSubscriptionEvents() {
- return SubscriptionEventOrdering.sortedCopy(this, getAccountTimeZone());
+ return SubscriptionEventOrdering.sortedCopy(this, getAccountTimeZone(), internalTenantContext);
}
}
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 17f87de..c9b180b 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
@@ -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
@@ -431,10 +431,11 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
}
private List<SubscriptionBundle> getSubscriptionBundlesForAccount(final UUID accountId, final TenantContext tenantContext) throws SubscriptionApiException {
+ final InternalTenantContext internalTenantContextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
+
// Retrieve entitlements
final AccountEntitlements accountEntitlements;
try {
- final InternalTenantContext internalTenantContextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
accountEntitlements = entitlementInternalApi.getAllEntitlementsForAccountId(accountId, internalTenantContextWithValidAccountRecordId);
} catch (final EntitlementApiException e) {
throw new SubscriptionApiException(e);
@@ -455,7 +456,8 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
accountId,
bundleId,
externalKey,
- accountEntitlements.getEntitlements().get(bundleId));
+ accountEntitlements.getEntitlements().get(bundleId),
+ internalTenantContextWithValidAccountRecordId);
final SubscriptionBaseBundle baseBundle = accountEntitlements.getBundles().get(bundleId);
final SubscriptionBundle subscriptionBundle = new DefaultSubscriptionBundle(bundleId,
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
index 91e5758..4ae583f 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionBundleTimeline.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
@@ -22,6 +22,7 @@ import java.util.List;
import java.util.UUID;
import org.joda.time.DateTimeZone;
+import org.killbill.billing.callcontext.InternalTenantContext;
public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTimeline {
@@ -30,11 +31,16 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
private final String externalKey;
private final List<SubscriptionEvent> events;
- public DefaultSubscriptionBundleTimeline(final DateTimeZone accountTimeZone, final UUID accountId, final UUID bundleId, final String externalKey, final Iterable<Entitlement> entitlements) {
+ public DefaultSubscriptionBundleTimeline(final DateTimeZone accountTimeZone,
+ final UUID accountId,
+ final UUID bundleId,
+ final String externalKey,
+ final Iterable<Entitlement> entitlements,
+ final InternalTenantContext internalTenantContext) {
this.accountId = accountId;
this.bundleId = bundleId;
this.externalKey = externalKey;
- this.events = SubscriptionEventOrdering.sortedCopy(entitlements, accountTimeZone);
+ this.events = SubscriptionEventOrdering.sortedCopy(entitlements, accountTimeZone, internalTenantContext);
}
@Override
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionEvent.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionEvent.java
index 78af96f..b4e630a 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionEvent.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionEvent.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -21,7 +23,7 @@ import java.util.UUID;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
-
+import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.PlanPhase;
@@ -51,6 +53,7 @@ public class DefaultSubscriptionEvent implements SubscriptionEvent {
private final BillingPeriod nextBillingPeriod;
private final DateTime createdDate;
private final DateTimeZone accountTimeZone;
+ private final InternalTenantContext internalTenantContext;
public DefaultSubscriptionEvent(final UUID id,
final UUID entitlementId,
@@ -71,7 +74,8 @@ public class DefaultSubscriptionEvent implements SubscriptionEvent {
final PriceList nextPriceList,
final BillingPeriod nextBillingPeriod,
final DateTime createDate,
- final DateTimeZone accountTimeZone) {
+ final DateTimeZone accountTimeZone,
+ final InternalTenantContext internalTenantContext) {
this.id = id;
this.entitlementId = entitlementId;
this.effectiveDate = effectiveDate;
@@ -93,6 +97,7 @@ public class DefaultSubscriptionEvent implements SubscriptionEvent {
this.nextBillingPeriod = nextBillingPeriod;
this.createdDate = createDate;
this.accountTimeZone = accountTimeZone;
+ this.internalTenantContext = internalTenantContext;
}
public DateTimeZone getAccountTimeZone() {
@@ -119,10 +124,9 @@ public class DefaultSubscriptionEvent implements SubscriptionEvent {
@Override
public LocalDate getEffectiveDate() {
- return effectiveDate != null ? new LocalDate(effectiveDate, accountTimeZone) : null;
+ return effectiveDate != null ? internalTenantContext.toLocalDate(effectiveDate, accountTimeZone) : null;
}
- @Override
public SubscriptionEventType getSubscriptionEventType() {
return eventType;
}
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java
index d802daa..1378573 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -39,8 +41,8 @@ public class EntitlementDateHelper {
public DateTime fromLocalDateAndReferenceTime(final LocalDate requestedDate, final DateTime referenceDateTime, final InternalTenantContext callContext) throws EntitlementApiException {
try {
final ImmutableAccountData account = accountApi.getImmutableAccountDataByRecordId(callContext.getAccountRecordId(), callContext);
- return ClockUtil.computeDateTimeWithUTCReferenceTime(requestedDate, referenceDateTime.toDateTime(DateTimeZone.UTC).toLocalTime(), account.getTimeZone(), clock);
- } catch (AccountApiException e) {
+ return ClockUtil.computeDateTimeWithUTCReferenceTime(requestedDate, callContext.toUTCDateTime(referenceDateTime).toLocalTime(), account.getTimeZone(), clock);
+ } catch (final AccountApiException e) {
throw new EntitlementApiException(e);
}
}
@@ -50,13 +52,13 @@ public class EntitlementDateHelper {
*
* @param inputDate the fully qualified DateTime
* @param accountTimeZone the account timezone
+ * @param internalTenantContext the context
* @return true if the inputDate, once converted into a LocalDate using account timezone is less or equals than today
*/
// TODO Move to ClockUtils
- public boolean isBeforeOrEqualsToday(final DateTime inputDate, final DateTimeZone accountTimeZone) {
- final LocalDate localDateNowInAccountTimezone = new LocalDate(clock.getUTCNow(), accountTimeZone);
- final LocalDate targetDateInAccountTimezone = new LocalDate(inputDate, accountTimeZone);
+ public boolean isBeforeOrEqualsToday(final DateTime inputDate, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext) {
+ final LocalDate localDateNowInAccountTimezone = clock.getToday(accountTimeZone);
+ final LocalDate targetDateInAccountTimezone = internalTenantContext.toLocalDate(inputDate, accountTimeZone);
return targetDateInAccountTimezone.compareTo(localDateNowInAccountTimezone) <= 0;
}
-
}
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java
index 26ae84e..01bff1d 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.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
@@ -25,6 +25,7 @@ import java.util.List;
import java.util.Map;
import org.joda.time.DateTimeZone;
+import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.entitlement.DefaultEntitlementService;
import org.killbill.billing.subscription.api.SubscriptionBase;
import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
@@ -47,20 +48,20 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
private SubscriptionEventOrdering() {}
- public static List<SubscriptionEvent> sortedCopy(final Entitlement entitlement, final DateTimeZone accountTimeZone) {
- return sortedCopy(ImmutableList.<Entitlement>of(entitlement), accountTimeZone);
+ public static List<SubscriptionEvent> sortedCopy(final Entitlement entitlement, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext) {
+ return sortedCopy(ImmutableList.<Entitlement>of(entitlement), accountTimeZone, internalTenantContext);
}
- public static List<SubscriptionEvent> sortedCopy(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone) {
- return INSTANCE.computeEvents(entitlements, accountTimeZone);
+ public static List<SubscriptionEvent> sortedCopy(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext) {
+ return INSTANCE.computeEvents(entitlements, accountTimeZone, internalTenantContext);
}
- private List<SubscriptionEvent> computeEvents(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone) {
+ private List<SubscriptionEvent> computeEvents(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext) {
// Compute base events across all entitlements (already ordered per entitlement)
- final LinkedList<SubscriptionEvent> result = computeSubscriptionBaseEvents(entitlements, accountTimeZone);
+ final LinkedList<SubscriptionEvent> result = computeSubscriptionBaseEvents(entitlements, accountTimeZone, internalTenantContext);
// Add blocking states at the right place
- BlockingStateOrdering.insertSorted(entitlements, accountTimeZone, result);
+ BlockingStateOrdering.insertSorted(entitlements, accountTimeZone, internalTenantContext, result);
// Final cleanups
reOrderSubscriptionEventsOnSameDateByType(result);
@@ -70,7 +71,7 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
}
// Compute the initial stream of events based on the subscription base events
- private LinkedList<SubscriptionEvent> computeSubscriptionBaseEvents(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone) {
+ private LinkedList<SubscriptionEvent> computeSubscriptionBaseEvents(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext) {
final LinkedList<SubscriptionEvent> result = new LinkedList<SubscriptionEvent>();
for (final Entitlement cur : entitlements) {
Preconditions.checkState(cur instanceof DefaultEntitlement, "Entitlement %s is not a DefaultEntitlement", cur);
@@ -79,7 +80,7 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
for (final SubscriptionBaseTransition tr : baseTransitions) {
final List<SubscriptionEventType> eventTypes = toEventTypes(tr.getTransitionType());
for (final SubscriptionEventType eventType : eventTypes) {
- final SubscriptionEvent event = toSubscriptionEvent(tr, eventType, accountTimeZone);
+ final SubscriptionEvent event = toSubscriptionEvent(tr, eventType, accountTimeZone, internalTenantContext);
insertSubscriptionEvent(event, result);
}
}
@@ -150,7 +151,7 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
result.add(index, event);
}
- private SubscriptionEvent toSubscriptionEvent(final SubscriptionBaseTransition in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone) {
+ private SubscriptionEvent toSubscriptionEvent(final SubscriptionBaseTransition in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext) {
return new DefaultSubscriptionEvent(in.getId(),
in.getSubscriptionId(),
in.getEffectiveTransitionTime(),
@@ -170,7 +171,8 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
in.getNextPriceList(),
(in.getNextPlan() != null ? in.getNextPlan().getRecurringBillingPeriod() : null),
in.getCreatedDate(),
- accountTimeZone);
+ accountTimeZone,
+ internalTenantContext);
}
//
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
index 7bda096..bcce602 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.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
@@ -124,7 +124,6 @@ public class DefaultEntitlementApiBase {
}
public AccountEntitlements getAllEntitlementsForAccountId(final UUID accountId, final InternalTenantContext tenantContext) throws EntitlementApiException {
-
final AccountEventsStreams accountEventsStreams = eventsStreamBuilder.buildForAccount(tenantContext);
final Map<UUID, Collection<Entitlement>> entitlementsPerBundle = new HashMap<UUID, Collection<Entitlement>>();
@@ -136,7 +135,7 @@ public class DefaultEntitlementApiBase {
for (final EventsStream eventsStream : accountEventsStreams.getEventsStreams().get(bundleId)) {
final Entitlement entitlement = new DefaultEntitlement(eventsStream, eventsStreamBuilder, entitlementApi, pluginExecution,
blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
- entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory);
+ entitlementUtils, dateHelper, clock, securityApi, tenantContext, internalCallContextFactory);
entitlementsPerBundle.get(bundleId).add(entitlement);
}
}
@@ -148,7 +147,7 @@ public class DefaultEntitlementApiBase {
final EventsStream eventsStream = eventsStreamBuilder.buildForEntitlement(entitlementId, tenantContext);
return new DefaultEntitlement(eventsStream, eventsStreamBuilder, entitlementApi, pluginExecution,
blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
- entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory);
+ entitlementUtils, dateHelper, clock, securityApi, tenantContext, internalCallContextFactory);
}
public void pause(final UUID bundleId, final LocalDate localEffectiveDate, final Iterable<PluginProperty> properties, final InternalCallContext internalCallContext) throws EntitlementApiException {
@@ -173,7 +172,7 @@ public class DefaultEntitlementApiBase {
final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(bundleId, internalCallContext);
final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), baseSubscription.getStartDate(), internalCallContext);
- if (!dateHelper.isBeforeOrEqualsToday(effectiveDate, account.getTimeZone())) {
+ if (!dateHelper.isBeforeOrEqualsToday(effectiveDate, account.getTimeZone(), internalCallContext)) {
recordPauseResumeNotificationEntry(baseSubscription.getId(), bundleId, effectiveDate, true, internalCallContext);
return null;
}
@@ -225,7 +224,7 @@ public class DefaultEntitlementApiBase {
final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), baseSubscription.getStartDate(), internalCallContext);
- if (!dateHelper.isBeforeOrEqualsToday(effectiveDate, account.getTimeZone())) {
+ if (!dateHelper.isBeforeOrEqualsToday(effectiveDate, account.getTimeZone(), internalCallContext)) {
recordPauseResumeNotificationEntry(baseSubscription.getId(), bundleId, effectiveDate, false, internalCallContext);
return null;
}
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
index d77e4ab..fbb664c 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
@@ -216,8 +216,7 @@ public class DefaultEntitlementInternalApi extends DefaultEntitlementApiBase imp
@Override
public Entitlement doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
- final LocalDate effectiveLocalDate = new LocalDate(updatedPluginContext.getEffectiveDate(), entitlement.getAccountTimeZone());
- DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(effectiveLocalDate, entitlement.getSubscriptionBase().getStartDate(), internalCallContext);
+ DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), entitlement.getSubscriptionBase().getStartDate(), internalCallContext);
// Avoid timing issues for IMM cancellations (we don't want an entitlement cancel date one second or so after the subscription cancel date or
// add-ons cancellations computations won't work).
if (effectiveDate.compareTo(entitlement.getSubscriptionBase().getEndDate()) > 0) {
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java b/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java
index 7e189c0..15612fc 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java
@@ -24,6 +24,9 @@ import java.util.Collection;
import java.util.UUID;
import org.joda.time.DateTime;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.account.api.ImmutableAccountData;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.entitlement.api.BlockingState;
import org.killbill.billing.entitlement.api.DefaultBlockingTransitionInternalEvent;
@@ -65,6 +68,7 @@ public class DefaultEntitlementService implements EntitlementService {
private static final Logger log = LoggerFactory.getLogger(DefaultEntitlementService.class);
private final EntitlementInternalApi entitlementInternalApi;
+ private final AccountInternalApi accountInternalApi;
private final BlockingStateDao blockingStateDao;
private final PersistentBus eventBus;
private final NotificationQueueService notificationQueueService;
@@ -75,12 +79,14 @@ public class DefaultEntitlementService implements EntitlementService {
@Inject
public DefaultEntitlementService(final EntitlementInternalApi entitlementInternalApi,
+ final AccountInternalApi accountInternalApi,
final BlockingStateDao blockingStateDao,
final PersistentBus eventBus,
final NotificationQueueService notificationQueueService,
final EntitlementUtils entitlementUtils,
final InternalCallContextFactory internalCallContextFactory) {
this.entitlementInternalApi = entitlementInternalApi;
+ this.accountInternalApi = accountInternalApi;
this.blockingStateDao = blockingStateDao;
this.eventBus = eventBus;
this.notificationQueueService = notificationQueueService;
@@ -136,15 +142,23 @@ public class DefaultEntitlementService implements EntitlementService {
return;
}
+ final ImmutableAccountData immutableAccountData;
+ try {
+ immutableAccountData = accountInternalApi.getImmutableAccountDataById(entitlement.getAccountId(), internalCallContext);
+ } catch (final AccountApiException e) {
+ log.error("Error processing event for entitlement {}" + entitlement.getId(), e);
+ return;
+ }
+
final EntitlementNotificationKeyAction entitlementNotificationKeyAction = key.getEntitlementNotificationKeyAction();
try {
if (EntitlementNotificationKeyAction.CHANGE.equals(entitlementNotificationKeyAction) ||
EntitlementNotificationKeyAction.CANCEL.equals(entitlementNotificationKeyAction)) {
blockAddOnsIfRequired(key, (DefaultEntitlement) entitlement, callContext, internalCallContext);
} else if (EntitlementNotificationKeyAction.PAUSE.equals(entitlementNotificationKeyAction)) {
- entitlementInternalApi.pause(key.getBundleId(), key.getEffectiveDate().toLocalDate(), ImmutableList.<PluginProperty>of(), internalCallContext);
+ entitlementInternalApi.pause(key.getBundleId(), internalCallContext.toLocalDate(key.getEffectiveDate(), immutableAccountData.getTimeZone()), ImmutableList.<PluginProperty>of(), internalCallContext);
} else if (EntitlementNotificationKeyAction.RESUME.equals(entitlementNotificationKeyAction)) {
- entitlementInternalApi.resume(key.getBundleId(), key.getEffectiveDate().toLocalDate(), ImmutableList.<PluginProperty>of(), internalCallContext);
+ entitlementInternalApi.resume(key.getBundleId(), internalCallContext.toLocalDate(key.getEffectiveDate(), immutableAccountData.getTimeZone()), ImmutableList.<PluginProperty>of(), internalCallContext);
}
} catch (final EntitlementApiException e) {
log.error("Error processing event for entitlement {}" + entitlement.getId(), e);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
index e7058f7..d428bdf 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -399,12 +401,12 @@ public class DefaultEventsStream implements EventsStream {
return DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(input.getStateName());
}
}).orNull();
- entitlementEffectiveEndDate = entitlementCancelEvent != null ? new LocalDate(entitlementCancelEvent.getEffectiveDate(), account.getTimeZone()) : null;
+ entitlementEffectiveEndDate = entitlementCancelEvent != null ? internalTenantContext.toLocalDate(entitlementCancelEvent.getEffectiveDate(), account.getTimeZone()) : null;
}
private void computeStateForEntitlement() {
// Current state for the ENTITLEMENT_SERVICE_NAME is set to cancelled
- if (entitlementEffectiveEndDate != null && entitlementEffectiveEndDate.compareTo(new LocalDate(utcNow, account.getTimeZone())) <= 0) {
+ if (entitlementEffectiveEndDate != null && entitlementEffectiveEndDate.compareTo(internalTenantContext.toLocalDate(utcNow, account.getTimeZone())) <= 0) {
entitlementState = EntitlementState.CANCELLED;
} else {
final LocalDate startDate = new LocalDate(getSubscriptionBase().getStartDate(), account.getTimeZone());
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
index 7c67a0e..f38553c 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -61,7 +63,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
public class TestSubscriptionBundleTimeline extends DefaultSubscriptionBundleTimeline {
public TestSubscriptionBundleTimeline(final DateTimeZone accountTimeZone, final UUID accountId, final UUID bundleId, final String externalKey, final Iterable<Entitlement> entitlements) {
- super(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ super(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
}
public SubscriptionEvent createEvent(final UUID subscriptionId, final SubscriptionEventType type, final DateTime effectiveDate) {
@@ -84,7 +86,8 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
null,
null,
null,
- null);
+ null,
+ internalCallContext);
}
}
@@ -293,7 +296,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final Entitlement entitlement = createEntitlement(entitlementId, allTransitions);
entitlements.add(entitlement);
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
assertEquals(timeline.getAccountId(), accountId);
assertEquals(timeline.getBundleId(), bundleId);
@@ -364,7 +367,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
entitlements.add(entitlement);
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
assertEquals(timeline.getAccountId(), accountId);
assertEquals(timeline.getBundleId(), bundleId);
@@ -459,7 +462,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
entitlements.add(entitlement);
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
assertEquals(timeline.getAccountId(), accountId);
assertEquals(timeline.getBundleId(), bundleId);
@@ -598,7 +601,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
entitlements.add(entitlement);
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
assertEquals(timeline.getAccountId(), accountId);
assertEquals(timeline.getBundleId(), bundleId);
@@ -722,7 +725,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
entitlements.add(entitlement);
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
assertEquals(timeline.getAccountId(), accountId);
assertEquals(timeline.getBundleId(), bundleId);
@@ -806,7 +809,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
entitlements.add(entitlement);
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
assertEquals(events.size(), 6);
@@ -904,7 +907,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final Entitlement entitlement2 = createEntitlement(entitlementId2, allTransitions2, blockingStates);
entitlements.add(entitlement2);
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
assertEquals(events.size(), 9);
@@ -1006,7 +1009,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
// Verify the timeline without the blocking state events
final ImmutableList<Entitlement> entitlementsWithoutBlockingStates = ImmutableList.<Entitlement>of(createEntitlement(entitlementId, allTransitions, blockingStates));
- final List<SubscriptionEvent> eventsWithoutBlockingStates = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlementsWithoutBlockingStates).getSubscriptionEvents();
+ final List<SubscriptionEvent> eventsWithoutBlockingStates = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlementsWithoutBlockingStates, internalCallContext).getSubscriptionEvents();
assertEquals(eventsWithoutBlockingStates.size(), 4);
assertEquals(eventsWithoutBlockingStates.get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
assertEquals(eventsWithoutBlockingStates.get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
@@ -1022,7 +1025,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
// Verify the timeline with the overdue event blocking the entitlement
final ImmutableList<Entitlement> entitlementsWithOverdueEvent = ImmutableList.<Entitlement>of(createEntitlement(entitlementId, allTransitions, blockingStates));
- final List<SubscriptionEvent> eventsWithOverdueEvent = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlementsWithOverdueEvent).getSubscriptionEvents();
+ final List<SubscriptionEvent> eventsWithOverdueEvent = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlementsWithOverdueEvent, internalCallContext).getSubscriptionEvents();
assertEquals(eventsWithOverdueEvent.size(), 5);
assertEquals(eventsWithOverdueEvent.get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
assertEquals(eventsWithOverdueEvent.get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
@@ -1041,7 +1044,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
entitlements.add(entitlement);
// Verify the timeline with both the overdue event and the entitlement cancel event
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
assertEquals(timeline.getAccountId(), accountId);
assertEquals(timeline.getBundleId(), bundleId);
@@ -1141,7 +1144,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
entitlements.add(entitlement);
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
assertEquals(events.size(), 4);
@@ -1234,7 +1237,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
entitlements.add(entitlement);
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
assertEquals(events.size(), 11);
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEntitlementDateHelper.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEntitlementDateHelper.java
index ae889c9..c1bd678 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEntitlementDateHelper.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEntitlementDateHelper.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -149,6 +151,6 @@ public class TestEntitlementDateHelper extends EntitlementTestSuiteNoDB {
// Check that our input date is greater than now
assertTrue(inputDateEquals.compareTo(clock.getUTCNow()) > 0);
// And yet since the LocalDate match the function returns true
- assertTrue(dateHelper.isBeforeOrEqualsToday(inputDateEquals, timeZoneUtcMinus8));
+ assertTrue(dateHelper.isBeforeOrEqualsToday(inputDateEquals, timeZoneUtcMinus8, internalCallContext));
}
}
invoice/pom.xml 2(+1 -1)
diff --git a/invoice/pom.xml b/invoice/pom.xml
index ddba6d5..4614397 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.1-SNAPSHOT</version>
+ <version>0.16.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-invoice</artifactId>
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
index 80472a9..56565d2 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
@@ -20,7 +20,6 @@ package org.killbill.billing.invoice.api.svcs;
import java.math.BigDecimal;
import java.util.Collection;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -111,11 +110,6 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
}
@Override
- public InvoicePayment getInvoicePaymentForRefund(final UUID paymentId, final InternalTenantContext context) throws InvoiceApiException {
- return getInvoicePayment(paymentId, InvoicePaymentType.REFUND, context);
- }
-
- @Override
public InvoicePayment getInvoicePaymentForChargeback(final UUID paymentId, final InternalTenantContext context) throws InvoiceApiException {
return getInvoicePayment(paymentId, InvoicePaymentType.CHARGED_BACK, context);
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
index f22f497..5166e98 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
@@ -542,7 +542,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}
// We expect the code to correctly pass the account currency -- the payment code, more generic accept chargeBack in different currencies,
// but this is only for direct payment (no invoice)
- Preconditions.checkArgument(invoicePayment.getCurrency() == currency);
+ Preconditions.checkArgument(invoicePayment.getCurrency() == currency, String.format("Invoice payment currency %s doesn't match chargeback currency %s", invoicePayment.getCurrency(), currency));
final UUID invoicePaymentId = invoicePayment.getId();
final BigDecimal maxChargedBackAmount = invoiceDaoHelper.getRemainingAmountPaidFromTransaction(invoicePaymentId, entitySqlDaoWrapperFactory, context);
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql b/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
index 1a3907c..f7cda2b 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
@@ -72,7 +72,6 @@ CREATE TABLE invoice_payments (
PRIMARY KEY(record_id)
) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
CREATE UNIQUE INDEX invoice_payments_id ON invoice_payments(id);
-CREATE UNIQUE INDEX idx_invoice_payments ON invoice_payments(payment_id, type);
CREATE INDEX invoice_payments_invoice_id ON invoice_payments(invoice_id);
CREATE INDEX invoice_payments_reversals ON invoice_payments(linked_invoice_payment_id);
CREATE INDEX invoice_payments_tenant_account_record_id ON invoice_payments(tenant_record_id, account_record_id);
jaxrs/pom.xml 2(+1 -1)
diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index 0921bda..d0f56d5 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.1-SNAPSHOT</version>
+ <version>0.16.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-jaxrs</artifactId>
junction/pom.xml 2(+1 -1)
diff --git a/junction/pom.xml b/junction/pom.xml
index 8d3bcb6..422ded4 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.1-SNAPSHOT</version>
+ <version>0.16.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-junction</artifactId>
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java
index df27149..8a45819 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java
@@ -141,8 +141,8 @@ public class BillCycleDayCalculator {
}
final DateTime date = plan.dateOfFirstRecurringNonZeroCharge(subscription.getStartDate(), initialPhaseType);
- final int bcdUTC = date.toDateTime(DateTimeZone.UTC).getDayOfMonth();
- final int bcdLocal = date.toDateTime(account.getTimeZone()).getDayOfMonth();
+ final int bcdUTC = context.toUTCDateTime(date).getDayOfMonth();
+ final int bcdLocal = context.toDateTime(date, account.getTimeZone()).getDayOfMonth();
log.info("Calculated BCD: subscription id {}, subscription start {}, timezone {}, bcd UTC {}, bcd local {}",
subscription.getId(), date.toDateTimeISO(), account.getTimeZone(), bcdUTC, bcdLocal);
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 3e36378..b43bd54 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
@@ -1,7 +1,8 @@
/*
- * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -33,8 +34,7 @@ import javax.annotation.Nullable;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
-
-import org.killbill.billing.account.api.Account;
+import org.joda.time.Days;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Currency;
@@ -103,7 +103,6 @@ public class BlockingCalculator {
final SortedSet<BillingEvent> billingEventsToAdd = new TreeSet<BillingEvent>();
final SortedSet<BillingEvent> billingEventsToRemove = new TreeSet<BillingEvent>();
-
final List<BlockingState> blockingEvents = blockingApi.getBlockingAllForAccount(context);
final Iterable<BlockingState> accountBlockingEvents = Iterables.filter(blockingEvents, new Predicate<BlockingState>() {
@@ -118,7 +117,7 @@ public class BlockingCalculator {
for (final UUID bundleId : bundleMap.keySet()) {
- final List<BlockingState> bundleBlockingEvents = perBundleBlockingEvents.get(bundleId) != null ? perBundleBlockingEvents.get(bundleId) : ImmutableList.<BlockingState>of();
+ final List<BlockingState> bundleBlockingEvents = perBundleBlockingEvents.get(bundleId) != null ? perBundleBlockingEvents.get(bundleId) : ImmutableList.<BlockingState>of();
for (final SubscriptionBase subscription : bundleMap.get(bundleId)) {
// Avoid inserting additional events for subscriptions that don't even have a START event
@@ -144,10 +143,9 @@ public class BlockingCalculator {
}
}
-
final List<BlockingState> getAggregateBlockingEventsPerSubscription(final Iterable<BlockingState> subscriptionBlockingEvents, final Iterable<BlockingState> bundleBlockingEvents, final Iterable<BlockingState> accountBlockingEvents) {
final Iterable<BlockingState> tmp = Iterables.concat(subscriptionBlockingEvents, bundleBlockingEvents, accountBlockingEvents);
- final List<BlockingState> result = Lists.newArrayList(tmp);
+ final List<BlockingState> result = Lists.newArrayList(tmp);
Collections.sort(result);
return result;
}
@@ -161,7 +159,7 @@ public class BlockingCalculator {
});
final Map<UUID, List<BlockingState>> perTypeBlockingEvents = new HashMap<UUID, List<BlockingState>>();
- for (final BlockingState cur : bundleBlockingEvents) {
+ for (final BlockingState cur : bundleBlockingEvents) {
if (!perTypeBlockingEvents.containsKey(cur.getBlockedId())) {
perTypeBlockingEvents.put(cur.getBlockedId(), new ArrayList<BlockingState>());
}
@@ -352,7 +350,7 @@ public class BlockingCalculator {
final DateTime endDate = firstNonBlocking == null ? null : firstNonBlocking.getEffectiveDate();
if (lastOne != null && lastOne.getEnd().compareTo(startDate) == 0) {
lastOne.setEnd(endDate);
- } else if (endDate == null || startDate.toLocalDate().compareTo(endDate.toLocalDate()) != 0) {
+ } else if (endDate == null || Days.daysBetween(startDate, endDate).getDays() >= 1) {
// Don't disable for periods less than a day (see https://github.com/killbill/killbill/issues/267)
result.add(new DisabledDuration(startDate, endDate));
}
NEWS 3(+3 -0)
diff --git a/NEWS b/NEWS
index 2bb5bfe..8001aa9 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,6 @@
+0.16.1
+ See https://github.com/killbill/killbill/releases/tag/killbill-0.16.1
+
0.16.0
TBD (point to 0.16 release page)
overdue/pom.xml 2(+1 -1)
diff --git a/overdue/pom.xml b/overdue/pom.xml
index c04472b..e7f373d 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.1-SNAPSHOT</version>
+ <version>0.16.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-overdue</artifactId>
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 2c942f6..91a0e03 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
@@ -318,7 +318,7 @@ public class OverdueStateApplicator {
computeEntitlementsToCancel(account, toBeCancelled, callContext);
try {
- entitlementInternalApi.cancel(toBeCancelled, new LocalDate(clock.getUTCNow(), account.getTimeZone()), actionPolicy, ImmutableList.<PluginProperty>of(), context);
+ entitlementInternalApi.cancel(toBeCancelled, clock.getToday(account.getTimeZone()), actionPolicy, ImmutableList.<PluginProperty>of(), context);
} catch (final EntitlementApiException e) {
throw new OverdueException(e);
}
payment/pom.xml 2(+1 -1)
diff --git a/payment/pom.xml b/payment/pom.xml
index 97119a8..de0ab68 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.1-SNAPSHOT</version>
+ <version>0.16.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-payment</artifactId>
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java
index f6c86e6..14e7ab0 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java
@@ -72,21 +72,26 @@ public class DefaultPayment extends EntityBase implements Payment {
private static BigDecimal getAmountForType(final Iterable<PaymentTransaction> transactions, final TransactionType transactiontype) {
BigDecimal result = BigDecimal.ZERO;
- final Iterable<PaymentTransaction> filtered = Iterables.filter(transactions, new Predicate<PaymentTransaction>() {
- @Override
- public boolean apply(final PaymentTransaction input) {
- return input.getTransactionType() == transactiontype && TransactionStatus.SUCCESS.equals(input.getTransactionStatus());
+ BigDecimal processedResult = BigDecimal.ZERO;
+ boolean shouldUseProcessedAmount = true;
+
+ for (final PaymentTransaction transaction : transactions) {
+ if (transaction.getTransactionType() != transactiontype || !TransactionStatus.SUCCESS.equals(transaction.getTransactionStatus())) {
+ continue;
}
- });
- if (TransactionType.AUTHORIZE.equals(transactiontype) && filtered.iterator().hasNext()) {
- // HACK - For multi-step AUTH, don't sum the individual transactions
- result = filtered.iterator().next().getAmount();
- } else {
- for (final PaymentTransaction dpt : filtered) {
- result = result.add(dpt.getAmount());
+
+ result = result.add(transaction.getAmount());
+
+ shouldUseProcessedAmount = shouldUseProcessedAmount && transaction.getCurrency().equals(transaction.getProcessedCurrency()) && transaction.getProcessedAmount() != null;
+ processedResult = shouldUseProcessedAmount ? processedResult.add(transaction.getProcessedAmount()) : BigDecimal.ZERO;
+
+ // For multi-step AUTH, don't sum the individual transactions
+ if (TransactionType.AUTHORIZE.equals(transactiontype)) {
+ break;
}
}
- return result;
+
+ return shouldUseProcessedAmount ? processedResult : result;
}
@Override
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
index 15dc191..c71407d 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
@@ -211,11 +211,23 @@ public class IncompletePaymentTransactionTask extends CompletionTaskBase<Payment
// Recompute new lastSuccessPaymentState. This is important to be able to allow new operations on the state machine (for e.g an AUTH_SUCCESS would now allow a CAPTURE operation)
final String lastSuccessPaymentState = paymentStateMachineHelper.isSuccessState(newPaymentState) ? newPaymentState : null;
- // Update the processedAmount, processedCurrency if we got a paymentTransactionInfoPlugin from the plugin and if this is a non error state
- final BigDecimal processedAmount = (paymentTransactionInfoPlugin != null && isPendingOrFinalTransactionStatus(transactionStatus)) ?
- paymentTransactionInfoPlugin.getAmount() : paymentTransaction.getProcessedAmount();
- final Currency processedCurrency = (paymentTransactionInfoPlugin != null && isPendingOrFinalTransactionStatus(transactionStatus)) ?
- paymentTransactionInfoPlugin.getCurrency() : paymentTransaction.getProcessedCurrency();
+ // Update processedAmount and processedCurrency
+ final BigDecimal processedAmount;
+ if (TransactionStatus.SUCCESS.equals(transactionStatus) || TransactionStatus.PENDING.equals(transactionStatus)) {
+ if (paymentTransactionInfoPlugin == null || paymentTransactionInfoPlugin.getAmount() == null) {
+ processedAmount = paymentTransaction.getProcessedAmount();
+ } else {
+ processedAmount = paymentTransactionInfoPlugin.getAmount();
+ }
+ } else {
+ processedAmount = BigDecimal.ZERO;
+ }
+ final Currency processedCurrency;
+ if (paymentTransactionInfoPlugin == null || paymentTransactionInfoPlugin.getCurrency() == null) {
+ processedCurrency = paymentTransaction.getProcessedCurrency();
+ } else {
+ processedCurrency = paymentTransactionInfoPlugin.getCurrency();
+ }
// Update the gatewayErrorCode, gatewayError if we got a paymentTransactionInfoPlugin
final String gatewayErrorCode = paymentTransactionInfoPlugin != null ? paymentTransactionInfoPlugin.getGatewayErrorCode() : paymentTransaction.getGatewayErrorCode();
@@ -239,12 +251,6 @@ public class IncompletePaymentTransactionTask extends CompletionTaskBase<Payment
return (newTransactionStatus != TransactionStatus.UNKNOWN) ? newTransactionStatus : currentTransactionStatus;
}
- private boolean isPendingOrFinalTransactionStatus(final TransactionStatus transactionStatus) {
- return (transactionStatus == TransactionStatus.PENDING ||
- transactionStatus == TransactionStatus.SUCCESS ||
- transactionStatus == TransactionStatus.PAYMENT_FAILURE);
- }
-
private PaymentPluginApi getPaymentPluginApi(final PaymentModelDao item, final String pluginName) {
final PaymentPluginApi pluginApi = pluginRegistry.getServiceForName(pluginName);
Preconditions.checkState(pluginApi != null, "Janitor IncompletePaymentTransactionTask cannot retrieve PaymentPluginApi for plugin %s (payment id %s), skipping", pluginName, item.getId());
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java
index 2b3a215..c6545dc 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java
@@ -30,6 +30,7 @@ import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.TransactionStatus;
+import org.killbill.billing.payment.api.TransactionType;
import org.killbill.billing.payment.dao.PaymentDao;
import org.killbill.billing.payment.dao.PaymentMethodModelDao;
import org.killbill.billing.payment.dao.PaymentModelDao;
@@ -89,7 +90,10 @@ public class PaymentAutomatonDAOHelper {
if (existingTransactions.isEmpty()) {
throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_SUCCESS_PAYMENT, paymentStateContext.getPaymentId());
}
- if (paymentStateContext.getCurrency() != null && existingTransactions.get(0).getCurrency() != paymentStateContext.getCurrency()) {
+ if (paymentStateContext.getCurrency() != null &&
+ existingTransactions.get(0).getCurrency() != paymentStateContext.getCurrency() &&
+ !TransactionType.CHARGEBACK.equals(paymentStateContext.getTransactionType())) {
+ // Note that we allow chargebacks in a different currency
throw new PaymentApiException(ErrorCode.PAYMENT_INVALID_PARAMETER, "currency", " should be " + existingTransactions.get(0).getCurrency() + " to match other existing transactions");
}
@@ -103,8 +107,22 @@ public class PaymentAutomatonDAOHelper {
public void processPaymentInfoPlugin(final TransactionStatus transactionStatus, @Nullable final PaymentTransactionInfoPlugin paymentInfoPlugin,
final String currentPaymentStateName) {
- final BigDecimal processedAmount = paymentInfoPlugin == null ? null : paymentInfoPlugin.getAmount();
- final Currency processedCurrency = paymentInfoPlugin == null ? null : paymentInfoPlugin.getCurrency();
+ final BigDecimal processedAmount;
+ if (TransactionStatus.SUCCESS.equals(transactionStatus) || TransactionStatus.PENDING.equals(transactionStatus)) {
+ if (paymentInfoPlugin == null || paymentInfoPlugin.getAmount() == null) {
+ processedAmount = paymentStateContext.getAmount();
+ } else {
+ processedAmount = paymentInfoPlugin.getAmount();
+ }
+ } else {
+ processedAmount = BigDecimal.ZERO;
+ }
+ final Currency processedCurrency;
+ if (paymentInfoPlugin == null || paymentInfoPlugin.getCurrency() == null) {
+ processedCurrency = paymentStateContext.getCurrency();
+ } else {
+ processedCurrency = paymentInfoPlugin.getCurrency();
+ }
final String gatewayErrorCode = paymentInfoPlugin == null ? null : paymentInfoPlugin.getGatewayErrorCode();
final String gatewayErrorMsg = paymentInfoPlugin == null ? null : paymentInfoPlugin.getGatewayError();
diff --git a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
index 6a45550..7ccaba6 100644
--- a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
@@ -33,6 +33,7 @@ import org.killbill.billing.ErrorCode;
import org.killbill.billing.ObjectType;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.control.plugin.api.OnFailurePaymentControlResult;
import org.killbill.billing.control.plugin.api.OnSuccessPaymentControlResult;
import org.killbill.billing.control.plugin.api.PaymentApiType;
@@ -152,11 +153,19 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
final UUID invoiceId = getInvoiceId(pluginProperties);
existingInvoicePayment = invoiceApi.getInvoicePaymentForAttempt(paymentControlContext.getPaymentId(), internalContext);
if (existingInvoicePayment != null && existingInvoicePayment.isSuccess()) {
+ // Only one successful purchase per payment (the invoice could be linked to multiple successful payments though)
log.info("onSuccessCall was already completed for payment purchase: " + paymentControlContext.getPaymentId());
} else {
- log.debug("Notifying invoice of successful payment: id={}, amount={}, currency={}, invoiceId={}", paymentControlContext.getPaymentId(), paymentControlContext.getProcessedAmount(), paymentControlContext.getCurrency(), invoiceId);
+ final BigDecimal invoicePaymentAmount;
+ if (paymentControlContext.getCurrency() == paymentControlContext.getProcessedCurrency()) {
+ invoicePaymentAmount = paymentControlContext.getProcessedAmount();
+ } else {
+ log.warn("Currency {} of invoice payment {} doesn't match invoice currency {}, assuming it is a full payment" , paymentControlContext.getProcessedCurrency(), paymentControlContext.getPaymentId(), paymentControlContext.getCurrency());
+ invoicePaymentAmount = paymentControlContext.getAmount();
+ }
+ log.debug("Notifying invoice of successful payment: id={}, amount={}, currency={}, invoiceId={}", paymentControlContext.getPaymentId(), invoicePaymentAmount, paymentControlContext.getCurrency(), invoiceId);
invoiceApi.notifyOfPayment(invoiceId,
- paymentControlContext.getProcessedAmount(),
+ invoicePaymentAmount,
paymentControlContext.getCurrency(),
paymentControlContext.getProcessedCurrency(),
paymentControlContext.getPaymentId(),
@@ -167,23 +176,34 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
break;
case REFUND:
- existingInvoicePayment = invoiceApi.getInvoicePaymentForRefund(paymentControlContext.getPaymentId(), internalContext);
- if (existingInvoicePayment != null) {
- log.info("onSuccessCall was already completed for payment refund: " + paymentControlContext.getPaymentId());
- } else {
- final Map<UUID, BigDecimal> idWithAmount = extractIdsWithAmountFromProperties(pluginProperties);
- final PluginProperty prop = getPluginProperty(pluginProperties, PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
- final boolean isAdjusted = prop != null ? Boolean.valueOf((String) prop.getValue()) : false;
- invoiceApi.createRefund(paymentControlContext.getPaymentId(), paymentControlContext.getAmount(), isAdjusted, idWithAmount, paymentControlContext.getTransactionExternalKey(), internalContext);
- }
+ final Map<UUID, BigDecimal> idWithAmount = extractIdsWithAmountFromProperties(pluginProperties);
+ final PluginProperty prop = getPluginProperty(pluginProperties, PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
+ final boolean isAdjusted = prop != null ? Boolean.valueOf((String) prop.getValue()) : false;
+ invoiceApi.createRefund(paymentControlContext.getPaymentId(), paymentControlContext.getAmount(), isAdjusted, idWithAmount, paymentControlContext.getTransactionExternalKey(), internalContext);
break;
case CHARGEBACK:
existingInvoicePayment = invoiceApi.getInvoicePaymentForChargeback(paymentControlContext.getPaymentId(), internalContext);
if (existingInvoicePayment != null) {
+ // We don't support partial chargebacks (yet?)
log.info("onSuccessCall was already completed for payment chargeback: " + paymentControlContext.getPaymentId());
} else {
- invoiceApi.createChargeback(paymentControlContext.getPaymentId(), paymentControlContext.getProcessedAmount(), paymentControlContext.getProcessedCurrency(), internalContext);
+ final InvoicePayment linkedInvoicePayment = invoiceApi.getInvoicePaymentForAttempt(paymentControlContext.getPaymentId(), internalContext);
+
+ final BigDecimal amount;
+ final Currency currency;
+ if (linkedInvoicePayment.getCurrency().equals(paymentControlContext.getProcessedCurrency()) && paymentControlContext.getProcessedAmount() != null) {
+ amount = paymentControlContext.getProcessedAmount();
+ currency = paymentControlContext.getProcessedCurrency();
+ } else if (linkedInvoicePayment.getCurrency().equals(paymentControlContext.getCurrency()) && paymentControlContext.getAmount() != null) {
+ amount = paymentControlContext.getAmount();
+ currency = paymentControlContext.getCurrency();
+ } else {
+ amount = linkedInvoicePayment.getAmount();
+ currency = linkedInvoicePayment.getCurrency();
+ }
+
+ invoiceApi.createChargeback(paymentControlContext.getPaymentId(), amount, currency, internalContext);
}
break;
@@ -209,7 +229,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
try {
log.debug("Notifying invoice of failed payment: id={}, amount={}, currency={}, invoiceId={}", paymentControlContext.getPaymentId(), paymentControlContext.getAmount(), paymentControlContext.getCurrency(), invoiceId);
invoiceApi.notifyOfPayment(invoiceId,
- paymentControlContext.getAmount(),
+ BigDecimal.ZERO,
paymentControlContext.getCurrency(),
// processed currency may be null so we use currency; processed currency will be updated if/when payment succeeds
paymentControlContext.getCurrency(),
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
index d5c1aa5..d6e293e 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
@@ -182,7 +182,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
assertEquals(payment.getTransactions().get(0).getPaymentId(), payment.getId());
assertEquals(payment.getTransactions().get(0).getAmount().compareTo(requestedAmount), 0);
assertEquals(payment.getTransactions().get(0).getCurrency(), Currency.AED);
- assertEquals(payment.getTransactions().get(0).getProcessedAmount().compareTo(requestedAmount), 0);
+ assertEquals(payment.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.ZERO), 0);
assertEquals(payment.getTransactions().get(0).getProcessedCurrency(), Currency.AED);
assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.PAYMENT_FAILURE);
@@ -405,7 +405,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
assertEquals(payment.getTransactions().get(0).getPaymentId(), payment.getId());
assertEquals(payment.getTransactions().get(0).getAmount().compareTo(requestedAmount), 0);
assertEquals(payment.getTransactions().get(0).getCurrency(), Currency.USD);
- assertEquals(payment.getTransactions().get(0).getProcessedAmount().compareTo(requestedAmount), 0); // This is weird...
+ assertEquals(payment.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.ZERO), 0);
assertEquals(payment.getTransactions().get(0).getProcessedCurrency(), Currency.USD);
assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.PAYMENT_FAILURE);
diff --git a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
index 811a61e..cc8c6e1 100644
--- a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -29,6 +29,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
+import javax.annotation.Nullable;
+
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.payment.api.PaymentMethodPlugin;
import org.killbill.billing.payment.api.PluginProperty;
@@ -71,6 +73,7 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
private final AtomicBoolean makeAllInvoicesFailWithError = new AtomicBoolean(false);
private final AtomicInteger makePluginWaitSomeMilliseconds = new AtomicInteger(0);
private final AtomicReference<BigDecimal> overrideNextProcessedAmount = new AtomicReference<BigDecimal>();
+ private final AtomicReference<Currency> overrideNextProcessedCurrency = new AtomicReference<Currency>();
private final Map<String, InternalPaymentInfo> payments = new ConcurrentHashMap<String, InternalPaymentInfo>();
private final Map<String, List<PaymentTransactionInfoPlugin>> paymentTransactions = new ConcurrentHashMap<String, List<PaymentTransactionInfoPlugin>>();
@@ -222,6 +225,10 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
overrideNextProcessedAmount.set(amount);
}
+ public void overrideNextProcessedCurrency(final Currency currency) {
+ overrideNextProcessedCurrency.set(currency);
+ }
+
public void updatePaymentTransactions(final UUID paymentId, final List<PaymentTransactionInfoPlugin> newTransactions) {
if (paymentTransactions.containsKey(paymentId.toString())) {
paymentTransactions.put (paymentId.toString(), newTransactions);
@@ -351,7 +358,7 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.REFUND, refundAmount, currency, properties);
}
- private PaymentTransactionInfoPlugin getPaymentTransactionInfoPluginResult(final UUID kbPaymentId, final UUID kbTransactionId, final TransactionType type, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> pluginProperties) throws PaymentPluginApiException {
+ private PaymentTransactionInfoPlugin getPaymentTransactionInfoPluginResult(final UUID kbPaymentId, final UUID kbTransactionId, final TransactionType type, final BigDecimal amount, @Nullable final Currency currency, final Iterable<PluginProperty> pluginProperties) throws PaymentPluginApiException {
if (makePluginWaitSomeMilliseconds.get() > 0) {
try {
Thread.sleep(makePluginWaitSomeMilliseconds.get());
@@ -388,8 +395,12 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
}
final BigDecimal processedAmount = MoreObjects.firstNonNull(overrideNextProcessedAmount.getAndSet(null), amount);
+ Currency processedCurrency = overrideNextProcessedCurrency.getAndSet(null);
+ if (processedCurrency == null) {
+ processedCurrency = currency;
+ }
- final PaymentTransactionInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, type, processedAmount, currency, clock.getUTCNow(), clock.getUTCNow(), status, errorCode, error);
+ final PaymentTransactionInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, type, processedAmount, processedCurrency, clock.getUTCNow(), clock.getUTCNow(), status, errorCode, error);
List<PaymentTransactionInfoPlugin> existingTransactions = paymentTransactions.get(kbPaymentId.toString());
if (existingTransactions == null) {
existingTransactions = new ArrayList<PaymentTransactionInfoPlugin>();
pom.xml 2(+1 -1)
diff --git a/pom.xml b/pom.xml
index 97f05eb..67ccff9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,7 +24,7 @@
<version>0.82-SNAPSHOT</version>
</parent>
<artifactId>killbill</artifactId>
- <version>0.16.1-SNAPSHOT</version>
+ <version>0.16.2-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 62c6d0c..81ad8e6 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.1-SNAPSHOT</version>
+ <version>0.16.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-profiles-killbill</artifactId>
profiles/killpay/pom.xml 2(+1 -1)
diff --git a/profiles/killpay/pom.xml b/profiles/killpay/pom.xml
index 98b792d..6d43052 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.1-SNAPSHOT</version>
+ <version>0.16.2-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 5bbaef8..442ac18 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.1-SNAPSHOT</version>
+ <version>0.16.2-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 0b9f959..825e97b 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.1-SNAPSHOT</version>
+ <version>0.16.2-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 793b2ce..a106e2b 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.1-SNAPSHOT</version>
+ <version>0.16.2-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 5861678..b3dde9e 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.1-SNAPSHOT</version>
+ <version>0.16.2-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 9a981a5..508a73f 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.1-SNAPSHOT</version>
+ <version>0.16.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-util</artifactId>
diff --git a/util/src/test/java/org/killbill/billing/api/TestApiListener.java b/util/src/test/java/org/killbill/billing/api/TestApiListener.java
index 690707a..d710a92 100644
--- a/util/src/test/java/org/killbill/billing/api/TestApiListener.java
+++ b/util/src/test/java/org/killbill/billing/api/TestApiListener.java
@@ -37,6 +37,7 @@ import org.killbill.billing.events.InvoiceCreationInternalEvent;
import org.killbill.billing.events.InvoiceNotificationInternalEvent;
import org.killbill.billing.events.InvoicePaymentErrorInternalEvent;
import org.killbill.billing.events.InvoicePaymentInfoInternalEvent;
+import org.killbill.billing.events.NullInvoiceInternalEvent;
import org.killbill.billing.events.PaymentErrorInternalEvent;
import org.killbill.billing.events.PaymentInfoInternalEvent;
import org.killbill.billing.events.PaymentPluginErrorInternalEvent;
@@ -113,6 +114,7 @@ public class TestApiListener {
RESUME,
PHASE,
BLOCK,
+ NULL_INVOICE,
INVOICE,
INVOICE_NOTIFICATION,
INVOICE_ADJUSTMENT,
@@ -239,6 +241,13 @@ public class TestApiListener {
}
@Subscribe
+ public void handleNullInvoiceEvents(final NullInvoiceInternalEvent event) {
+ log.info(String.format("Got Null Invoice event %s", event.toString()));
+ assertEqualsNicely(NextEvent.NULL_INVOICE);
+ notifyIfStackEmpty();
+ }
+
+ @Subscribe
public void handleInvoiceEvents(final InvoiceCreationInternalEvent event) {
log.info(String.format("Got Invoice event %s", event.toString()));
assertEqualsNicely(NextEvent.INVOICE);