diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java
index 9150555..bb9431c 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java
@@ -35,8 +35,10 @@ import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.clock.Clock;
+import com.ning.billing.entitlement.EntitlementService;
import com.ning.billing.entitlement.api.BlockingState;
import com.ning.billing.entitlement.api.BlockingStateType;
+import com.ning.billing.entitlement.api.DefaultEntitlementApi;
import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
import com.ning.billing.entitlement.api.EntitlementApiException;
import com.ning.billing.entitlement.engine.core.EventsStream;
@@ -228,10 +230,12 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
// Inject the extra blocking states into the stream if needed
for (final BlockingState blockingState : blockingStatesNotOnDisk) {
- // In case we're coming from getBlockingHistoryForService / getBlockingAll, make sure we don't add
- // blocking states for other add-ons on that base subscription
- if (blockingStateType == null ||
- (BlockingStateType.SUBSCRIPTION.equals(blockingStateType) && blockingState.getBlockedId().equals(blockableId))) {
+ if ((blockingStateType == null ||
+ // In case we're coming from getBlockingHistoryForService / getBlockingAll, make sure we don't add
+ // blocking states for other add-ons on that base subscription
+ (BlockingStateType.SUBSCRIPTION.equals(blockingStateType) && blockingState.getBlockedId().equals(blockableId))) &&
+ // If this entitlement is actually already cancelled, don't add the cancellation event we computed from subscription events (wrong)
+ !isEntitlementCancelled(blockingState.getBlockedId(), blockingStatesOnDiskCopy)) {
final BlockingStateModelDao blockingStateModelDao = new BlockingStateModelDao(blockingState, now, now);
blockingStatesOnDiskCopy.add(BlockingStateModelDao.toBlockingState(blockingStateModelDao));
}
@@ -241,4 +245,19 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
// Return the sorted list
return BLOCKING_STATE_ORDERING.immutableSortedCopy(blockingStatesOnDiskCopy);
}
+
+ private boolean isEntitlementCancelled(final UUID blockedId, final Iterable<BlockingState> blockingStates) {
+ // If this entitlement is already cancelled, there is nothing to do
+ return Iterables.<BlockingState>tryFind(blockingStates,
+ new Predicate<BlockingState>() {
+ @Override
+ public boolean apply(final BlockingState input) {
+ return input.getBlockedId().equals(blockedId) &&
+ BlockingStateType.SUBSCRIPTION.equals(input.getType()) &&
+ EntitlementService.ENTITLEMENT_SERVICE_NAME.equals(input.getService()) &&
+ DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(input.getStateName());
+ }
+ })
+ .orNull() != null;
+ }
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java
index 5398bce..5bf86ae 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java
@@ -40,7 +40,6 @@ import com.ning.billing.entitlement.api.BlockingState;
import com.ning.billing.entitlement.api.BlockingStateType;
import com.ning.billing.entitlement.api.DefaultEntitlement;
import com.ning.billing.entitlement.api.DefaultEntitlementApi;
-import com.ning.billing.entitlement.api.Entitlement;
import com.ning.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
import com.ning.billing.entitlement.api.EntitlementApiException;
import com.ning.billing.entitlement.dao.BlockingStateSqlDao;
@@ -124,6 +123,22 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
checkBlockingStatesDAO(cancelledBaseEntitlement, cancelledAddOnEntitlement, baseEffectiveCancellationOrChangeDate, true);
}
+ @Test(groups = "slow", description = "Verify add-ons blocking states are not impacted for add-on IMM cancellations")
+ public void testCancellationBaseEOTAddOnIMM() throws Exception {
+ // Cancel the base plan
+ final DefaultEntitlement cancelledBaseEntitlement = (DefaultEntitlement) baseEntitlement.cancelEntitlementWithPolicyOverrideBillingPolicy(EntitlementActionPolicy.END_OF_TERM, BillingActionPolicy.END_OF_TERM, callContext);
+ // No blocking event (EOT)
+ assertListenerStatus();
+
+ // Cancel the add-on
+ testListener.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK);
+ final DefaultEntitlement cancelledAddOnEntitlement = (DefaultEntitlement) addOnEntitlement.cancelEntitlementWithPolicyOverrideBillingPolicy(EntitlementActionPolicy.IMMEDIATE, BillingActionPolicy.IMMEDIATE, callContext);
+ assertListenerStatus();
+
+ // Verify the blocking states API doesn't mix the dates (all blocking states are on disk)
+ checkBlockingStatesDAO(cancelledBaseEntitlement, cancelledAddOnEntitlement, baseEffectiveCancellationOrChangeDate, clock.getUTCToday(), true);
+ }
+
@Test(groups = "slow", description = "Verify add-ons blocking states are not impacted by IMM cancellations")
public void testCancellationIMM() throws Exception {
// Approximate check, as the blocking state check (checkBlockingStatesDAO) could be a bit off
@@ -319,12 +334,17 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
}
// Test the DAO
- private void checkBlockingStatesDAO(final Entitlement baseEntitlement, final Entitlement addOnEntitlement, final LocalDate effectiveCancellationDate, final boolean isBaseCancelled) {
+ private void checkBlockingStatesDAO(final DefaultEntitlement baseEntitlement, final DefaultEntitlement addOnEntitlement, final LocalDate effectiveCancellationDate, final boolean isBaseCancelled) {
+ checkBlockingStatesDAO(baseEntitlement, addOnEntitlement, effectiveCancellationDate, effectiveCancellationDate, isBaseCancelled);
+ }
+
+ // Test the DAO
+ private void checkBlockingStatesDAO(final DefaultEntitlement baseEntitlement, final DefaultEntitlement addOnEntitlement, final LocalDate effectiveBaseCancellationDate, final LocalDate effectiveAddOnCancellationDate, final boolean isBaseCancelled) {
final List<BlockingState> blockingStatesForBaseEntitlement = blockingStateDao.getBlockingAll(baseEntitlement.getId(), BlockingStateType.SUBSCRIPTION, internalCallContext);
Assert.assertEquals(blockingStatesForBaseEntitlement.size(), isBaseCancelled ? 1 : 0);
if (isBaseCancelled) {
Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getBlockedId(), baseEntitlement.getId());
- Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getEffectiveDate().toLocalDate(), effectiveCancellationDate);
+ Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getEffectiveDate().toLocalDate(), effectiveBaseCancellationDate);
Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getType(), BlockingStateType.SUBSCRIPTION);
Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getService(), EntitlementService.ENTITLEMENT_SERVICE_NAME);
Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getStateName(), DefaultEntitlementApi.ENT_STATE_CANCELLED);
@@ -333,7 +353,7 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
final List<BlockingState> blockingStatesForAddOn = blockingStateDao.getBlockingAll(addOnEntitlement.getId(), BlockingStateType.SUBSCRIPTION, internalCallContext);
Assert.assertEquals(blockingStatesForAddOn.size(), 1);
Assert.assertEquals(blockingStatesForAddOn.get(0).getBlockedId(), addOnEntitlement.getId());
- Assert.assertEquals(blockingStatesForAddOn.get(0).getEffectiveDate().toLocalDate(), effectiveCancellationDate);
+ Assert.assertEquals(blockingStatesForAddOn.get(0).getEffectiveDate().toLocalDate(), effectiveAddOnCancellationDate);
Assert.assertEquals(blockingStatesForAddOn.get(0).getType(), BlockingStateType.SUBSCRIPTION);
Assert.assertEquals(blockingStatesForAddOn.get(0).getService(), EntitlementService.ENTITLEMENT_SERVICE_NAME);
Assert.assertEquals(blockingStatesForAddOn.get(0).getStateName(), DefaultEntitlementApi.ENT_STATE_CANCELLED);