killbill-memoizeit
Changes
entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java 45(+38 -7)
entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java 44(+30 -14)
Details
diff --git a/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java b/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
index f29f04e..20027f2 100644
--- a/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
+++ b/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
@@ -62,6 +62,8 @@ public interface EventsStream {
boolean isSubscriptionCancelled();
+ boolean isBlockChange(final DateTime effectiveDate);
+
int getDefaultBillCycleDayLocal();
Collection<BlockingState> getPendingEntitlementCancellationEvents();
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 d8d941c..eb1ff14 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
@@ -44,6 +44,7 @@ import org.killbill.billing.entitlement.EventsStream;
import org.killbill.billing.entitlement.api.EntitlementPluginExecution.WithEntitlementPlugin;
import org.killbill.billing.entitlement.api.svcs.DefaultEntitlementApiBase;
import org.killbill.billing.entitlement.block.BlockingChecker;
+import org.killbill.billing.entitlement.block.BlockingChecker.BlockingAggregator;
import org.killbill.billing.entitlement.dao.BlockingStateDao;
import org.killbill.billing.entitlement.engine.core.EntitlementUtils;
import org.killbill.billing.entitlement.engine.core.EventsStreamBuilder;
@@ -157,6 +158,10 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
try {
final DateTime now = clock.getUTCNow();
+
+ final DateTime entitlementRequestedDate = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(), now, contextWithValidAccountRecordId);
+ checkForAccountBlockingChange(accountId, entitlementRequestedDate, contextWithValidAccountRecordId);
+
final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.createBundleForAccount(accountId, externalKey, contextWithValidAccountRecordId);
final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = getFirstBaseEntitlementWithAddOnsSpecifier(updatedPluginContext.getBaseEntitlementWithAddOnsSpecifiers());
@@ -165,7 +170,6 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
final DateTime billingRequestedDate = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getBillingEffectiveDate(), now, contextWithValidAccountRecordId);
final SubscriptionBase subscription = subscriptionBaseInternalApi.createSubscription(bundle.getId(), specifier.getPlanPhaseSpecifier(), specifier.getOverrides(), billingRequestedDate, isMigrated, contextWithValidAccountRecordId);
- final DateTime entitlementRequestedDate = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(), now, contextWithValidAccountRecordId);
final BlockingState newBlockingState = new DefaultBlockingState(subscription.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_START, EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, entitlementRequestedDate);
entitlementUtils.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableList.<BlockingState>of(newBlockingState), subscription.getBundleId(), contextWithValidAccountRecordId);
@@ -221,11 +225,25 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
try {
+ final DateTime now = clock.getUTCNow();
+
+ // Verify if operation is allowed by looking for is_block_change on Account
+ final Iterator<BaseEntitlementWithAddOnsSpecifier> it1 = baseEntitlementWithAddOnsSpecifiers.iterator();
+ DateTime upTo = null;
+ while (it1.hasNext()) {
+ final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = it1.next();
+ final DateTime cur = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(), now, contextWithValidAccountRecordId);
+ if (cur != null) {
+ upTo = upTo == null || upTo.compareTo(cur) < 0 ? cur : upTo;
+ }
+ }
+ // Note that to fully check for block_change we should also look for BlockingState at the BUNDLE/SUBSCRIPTION level in case some of the input contain a BP that already exists.
+ checkForAccountBlockingChange(accountId, upTo, contextWithValidAccountRecordId);
+
final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns = subscriptionBaseInternalApi.createBaseSubscriptionsWithAddOns(accountId, baseEntitlementWithAddOnsSpecifiers, contextWithValidAccountRecordId);
final Map<BlockingState, UUID> blockingStateMap = new HashMap<BlockingState, UUID>();
int i = 0;
- final DateTime now = clock.getUTCNow();
for (final Iterator<BaseEntitlementWithAddOnsSpecifier> it = baseEntitlementWithAddOnsSpecifiers.iterator(); it.hasNext(); i++) {
final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = it.next();
for (final SubscriptionBase subscriptionBase : subscriptionsWithAddOns.get(i).getSubscriptionBaseList()) {
@@ -291,6 +309,11 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
final WithEntitlementPlugin<Entitlement> addEntitlementWithPlugin = new WithEntitlementPlugin<Entitlement>() {
@Override
public Entitlement doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
+
+ final DateTime now = clock.getUTCNow();
+ final InternalCallContext context = internalCallContextFactory.createInternalCallContext(bundleId, ObjectType.BUNDLE, callContext);
+ final DateTime entitlementRequestedDate = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(), now, context);
+
final EventsStream eventsStreamForBaseSubscription = eventsStreamBuilder.buildForBaseSubscription(bundleId, callContext);
if (eventsStreamForBaseSubscription.isEntitlementCancelled() ||
@@ -301,7 +324,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
}
// Check the base entitlement state is not blocked
- if (eventsStreamForBaseSubscription.isBlockChange()) {
+ if (eventsStreamForBaseSubscription.isBlockChange(entitlementRequestedDate)) {
throw new EntitlementApiException(new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, BlockingChecker.ACTION_CHANGE, BlockingChecker.TYPE_SUBSCRIPTION, eventsStreamForBaseSubscription.getEntitlementId().toString()));
}
@@ -309,12 +332,9 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = getFirstBaseEntitlementWithAddOnsSpecifier(updatedPluginContext.getBaseEntitlementWithAddOnsSpecifiers());
final EntitlementSpecifier specifier = getFirstEntitlementSpecifier(baseEntitlementWithAddOnsSpecifier);
- final DateTime now = clock.getUTCNow();
- final InternalCallContext context = internalCallContextFactory.createInternalCallContext(eventsStreamForBaseSubscription.getAccountId(), callContext);
final DateTime billingRequestedDate = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getBillingEffectiveDate(), now, context);
final SubscriptionBase subscription = subscriptionBaseInternalApi.createSubscription(bundleId, specifier.getPlanPhaseSpecifier(), specifier.getOverrides(), billingRequestedDate, isMigrated, context);
- final DateTime entitlementRequestedDate = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(), now, context);
final BlockingState newBlockingState = new DefaultBlockingState(subscription.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_START, EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, entitlementRequestedDate);
entitlementUtils.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableList.<BlockingState>of(newBlockingState), subscription.getBundleId(), context);
@@ -417,7 +437,6 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(bundleId, ObjectType.BUNDLE, context);
super.resume(bundleId, localEffectiveDate, properties, contextWithValidAccountRecordId);
-
}
@Override
@@ -515,4 +534,16 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
};
return pluginExecution.executeWithPlugin(transferWithPlugin, pluginContext);
}
+
+ private void checkForAccountBlockingChange(final UUID accountId, @Nullable final DateTime upTo, final InternalCallContext context) throws EntitlementApiException {
+
+ try {
+ final BlockingAggregator blockingAggregator = checker.getBlockedStatus(accountId, BlockingStateType.ACCOUNT, upTo, context);
+ if (blockingAggregator.isBlockChange()) {
+ throw new EntitlementApiException(new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, BlockingChecker.ACTION_CHANGE, BlockingChecker.TYPE_ACCOUNT, accountId.toString()));
+ }
+ } catch (final BlockingApiException e) {
+ throw new EntitlementApiException(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 27d9591..c991055 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
@@ -48,6 +48,7 @@ import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
import org.killbill.billing.subscription.api.user.SubscriptionBaseTransition;
import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
@@ -72,7 +73,7 @@ public class DefaultEventsStream implements EventsStream {
private final LocalDate utcToday;
private final int defaultBillCycleDayLocal;
- private BlockingAggregator blockingAggregator;
+ private BlockingAggregator currentStateBlockingAggregator;
private List<BlockingState> subscriptionEntitlementStates;
private LocalDate entitlementEffectiveStartDate;
private DateTime entitlementEffectiveStartDateTime;
@@ -167,7 +168,7 @@ public class DefaultEventsStream implements EventsStream {
@Override
public boolean isBlockChange() {
- return blockingAggregator.isBlockChange();
+ return currentStateBlockingAggregator.isBlockChange();
}
public boolean isEntitlementFutureCancelled() {
@@ -199,6 +200,13 @@ public class DefaultEventsStream implements EventsStream {
}
@Override
+ public boolean isBlockChange(final DateTime effectiveDate) {
+ Preconditions.checkState(effectiveDate != null);
+ final BlockingAggregator aggregator = getBlockingAggregator(effectiveDate);
+ return aggregator.isBlockChange();
+ }
+
+ @Override
public int getDefaultBillCycleDayLocal() {
return defaultBillCycleDayLocal;
}
@@ -385,24 +393,32 @@ public class DefaultEventsStream implements EventsStream {
private void setup() {
computeEntitlementBlockingStates();
- computeBlockingAggregator();
+ computeCurrentBlockingAggregator();
computeEntitlementStartEvent();
computeEntitlementCancelEvent();
computeStateForEntitlement();
}
- private void computeBlockingAggregator() {
+ private void computeCurrentBlockingAggregator() {
+ currentStateBlockingAggregator = getBlockingAggregator(null);
+ }
+
+ private BlockingAggregator getBlockingAggregator(final DateTime upTo) {
- final List<BlockingState> currentSubscriptionBlockingStatesForServices = filterCurrentBlockableStatePerService(BlockingStateType.SUBSCRIPTION, subscription.getId());
- final List<BlockingState> currentBundleBlockingStatesForServices = filterCurrentBlockableStatePerService(BlockingStateType.SUBSCRIPTION_BUNDLE, subscription.getBundleId());
- final List<BlockingState> currentAccountBlockingStatesForServices = filterCurrentBlockableStatePerService(BlockingStateType.ACCOUNT, account.getId());
- blockingAggregator = blockingChecker.getBlockedStatus(currentAccountBlockingStatesForServices,
- currentBundleBlockingStatesForServices,
- currentSubscriptionBlockingStatesForServices,
- internalTenantContext);
+ final List<BlockingState> currentSubscriptionBlockingStatesForServices = filterCurrentBlockableStatePerService(BlockingStateType.SUBSCRIPTION, subscription.getId(), upTo);
+ final List<BlockingState> currentBundleBlockingStatesForServices = filterCurrentBlockableStatePerService(BlockingStateType.SUBSCRIPTION_BUNDLE, subscription.getBundleId(), upTo);
+ final List<BlockingState> currentAccountBlockingStatesForServices = filterCurrentBlockableStatePerService(BlockingStateType.ACCOUNT, account.getId(), upTo);
+ return blockingChecker.getBlockedStatus(currentAccountBlockingStatesForServices,
+ currentBundleBlockingStatesForServices,
+ currentSubscriptionBlockingStatesForServices, internalTenantContext);
}
- private List<BlockingState> filterCurrentBlockableStatePerService(final BlockingStateType type, final UUID blockableId) {
+
+
+ private List<BlockingState> filterCurrentBlockableStatePerService(final BlockingStateType type, final UUID blockableId, @Nullable final DateTime upTo) {
+
+ final DateTime resolvedUpTo = upTo != null ? upTo : utcNow;
+
final Map<String, BlockingState> currentBlockingStatePerService = new HashMap<String, BlockingState>();
for (final BlockingState blockingState : blockingStates) {
if (!blockingState.getBlockedId().equals(blockableId)) {
@@ -411,7 +427,7 @@ public class DefaultEventsStream implements EventsStream {
if (blockingState.getType() != type) {
continue;
}
- if (blockingState.getEffectiveDate().isAfter(utcNow)) {
+ if (blockingState.getEffectiveDate().isAfter(resolvedUpTo)) {
continue;
}
@@ -462,7 +478,7 @@ public class DefaultEventsStream implements EventsStream {
entitlementState = EntitlementState.PENDING;
} else {
// Gather states across all services and check if one of them is set to 'blockEntitlement'
- entitlementState = (blockingAggregator != null && blockingAggregator.isBlockEntitlement() ? EntitlementState.BLOCKED : EntitlementState.ACTIVE);
+ entitlementState = (currentStateBlockingAggregator != null && currentStateBlockingAggregator.isBlockEntitlement() ? EntitlementState.BLOCKED : EntitlementState.ACTIVE);
}
}
}
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
index 718b61e..4965c31 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
@@ -355,7 +355,9 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
clock.addDays(5);
testListener.pushExpectedEvents(NextEvent.BLOCK);
- entitlementApi.pause(baseEntitlement.getBundleId(), new LocalDate(clock.getUTCNow()), ImmutableList.<PluginProperty>of(), callContext);
+ final LocalDate blockingStateDate = new LocalDate(clock.getUTCNow());
+ entitlementApi.pause(baseEntitlement.getBundleId(), blockingStateDate, ImmutableList
+ .<PluginProperty>of(), callContext);
assertListenerStatus();
// Verify blocking state
@@ -374,7 +376,8 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
// Try to add an ADD_ON, it should fail because BASE is blocked
try {
final PlanPhaseSpecifier spec3 = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec3, null, effectiveDateSpec1, effectiveDateSpec1, false, ImmutableList.<PluginProperty>of(), callContext);
+ entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec3, null, blockingStateDate, effectiveDateSpec1, false, ImmutableList.<PluginProperty>of(), callContext);
+ fail("Should not be able to create ADD-ON because BP is paused");
} catch (EntitlementApiException e) {
assertEquals(e.getCode(), ErrorCode.BLOCK_BLOCKED_ACTION.getCode());
}
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java
index d96a38c..8bfc522 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java
@@ -21,18 +21,22 @@ package org.killbill.billing.entitlement.block;
import java.util.List;
import java.util.UUID;
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.killbill.billing.ErrorCode;
import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.api.TestApiListener.NextEvent;
-import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
import org.killbill.billing.catalog.api.PriceListSet;
-import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
import org.killbill.billing.entitlement.api.BlockingState;
import org.killbill.billing.entitlement.api.BlockingStateType;
import org.killbill.billing.entitlement.api.Entitlement;
import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
+import org.killbill.billing.entitlement.api.EntitlementApiException;
+import org.killbill.billing.entitlement.api.SubscriptionApiException;
import org.killbill.billing.junction.DefaultBlockingState;
import org.killbill.billing.payment.api.PluginProperty;
import org.testng.Assert;
@@ -150,7 +154,6 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
subscriptionApi.addBlockingState(state2, null, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
-
baseEntitlement = entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
assertEquals(baseEntitlement.getState(), EntitlementState.BLOCKED);
@@ -164,7 +167,6 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
baseEntitlement = entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
assertEquals(baseEntitlement.getState(), EntitlementState.BLOCKED);
-
// Remove blocking at bundle level.
clock.addDays(1);
testListener.pushExpectedEvent(NextEvent.BLOCK);
@@ -183,7 +185,121 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
return input.getService().equals(service);
}
}));
-
Assert.assertEquals(history.size(), 4);
}
+
+ @Test(groups = "slow")
+ public void testCreateBaseSubscriptionOnBlockedChangeAcount() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+ final LocalDate initialDate = new LocalDate(2017, 5, 1);
+ clock.setDay(initialDate);
+
+ final Account account = createAccount(getAccountData(1));
+
+ testListener.pushExpectedEvent(NextEvent.BLOCK);
+ final BlockingState blockChangeAccount = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, "State1", "Service1", true, false, false, clock.getUTCNow());
+ subscriptionApi.addBlockingState(blockChangeAccount, null, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ // Try create subscription right now
+ try {
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("shotgun-monthly", null);
+ entitlementApi.createBaseEntitlement(account.getId(), spec, "xyzqe", null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
+ Assert.fail("Should fail to create entitlement when ACCOUNT has been 'change' blocked");
+ } catch (final EntitlementApiException e) {
+ assertEquals(e.getCode(), ErrorCode.BLOCK_BLOCKED_ACTION.getCode());
+ }
+
+ // Try create subscription in the future
+ try {
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("shotgun-monthly", null);
+ entitlementApi.createBaseEntitlement(account.getId(), spec, "xyzqe", null, initialDate.plusDays(3), null, false, ImmutableList.<PluginProperty>of(), callContext);
+ Assert.fail("Should fail to create entitlement when ACCOUNT has been 'change' blocked");
+ } catch (final EntitlementApiException e) {
+ assertEquals(e.getCode(), ErrorCode.BLOCK_BLOCKED_ACTION.getCode());
+ }
+
+ // Try create subscription in the past
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("shotgun-monthly", null);
+ testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CREATE);
+ entitlementApi.createBaseEntitlement(account.getId(), spec, "xyzqe", null, initialDate.minusDays(3), null, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+ }
+
+ @Test(groups = "slow")
+ public void testCreateAOSubscriptionOnBlockedChangeAcount() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+ final LocalDate initialDate = new LocalDate(2017, 5, 1);
+ clock.setDay(initialDate);
+
+ final Account account = createAccount(getAccountData(1));
+
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("shotgun-monthly", null);
+ testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CREATE);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "xyzqe", null, initialDate.minusDays(3), null, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ testListener.pushExpectedEvent(NextEvent.BLOCK);
+ final BlockingState blockChangeAccount = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, "State1", "Service1", true, false, false, clock.getUTCNow());
+ subscriptionApi.addBlockingState(blockChangeAccount, null, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ // Try create subscription right now
+ try {
+ final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ entitlementApi.addEntitlement(entitlement.getBundleId(), addOnSpec, null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+ } catch (final EntitlementApiException e) {
+ assertEquals(e.getCode(), ErrorCode.BLOCK_BLOCKED_ACTION.getCode());
+ }
+
+ // Try create subscription in the future
+ try {
+ final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ entitlementApi.addEntitlement(entitlement.getBundleId(), addOnSpec, null, initialDate.plusDays(2), null, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+ } catch (final EntitlementApiException e) {
+ assertEquals(e.getCode(), ErrorCode.BLOCK_BLOCKED_ACTION.getCode());
+ }
+
+ // Try create subscription in the past
+ final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ entitlementApi.addEntitlement(entitlement.getBundleId(), addOnSpec, null, initialDate.minusDays(2), null, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+ }
+
+ @Test(groups = "slow")
+ public void testCreateAOSubscriptionOnFutureBlockedChangeAcount() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+ final LocalDate initialDate = new LocalDate(2017, 5, 1);
+ clock.setDay(initialDate);
+
+ final Account account = createAccount(getAccountData(1));
+
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("shotgun-monthly", null);
+ testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CREATE);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "xyzqe", null, initialDate.minusDays(3), null, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ // Create future BlockingState
+ final LocalDate blockingChange = initialDate.plusDays(3);
+ final BlockingState blockChangeAccount = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, "State1", "Service1", true, false, false, null);
+ subscriptionApi.addBlockingState(blockChangeAccount, blockingChange, ImmutableList.<PluginProperty>of(), callContext);
+
+ // Create ADD_ON in the future as well
+ try {
+ final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ entitlementApi.addEntitlement(entitlement.getBundleId(), addOnSpec, null, blockingChange, null, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+ } catch (final EntitlementApiException e) {
+ assertEquals(e.getCode(), ErrorCode.BLOCK_BLOCKED_ACTION.getCode());
+ }
+
+ // Create ADD_ON now (prior future BlockingState)
+ final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+ entitlementApi.addEntitlement(entitlement.getBundleId(), addOnSpec, null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+ }
+
}