diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/DefaultEntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/DefaultEntitlementDao.java
index bd6a4ce..f10b36b 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/DefaultEntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/DefaultEntitlementDao.java
@@ -684,10 +684,10 @@ public class DefaultEntitlementDao implements EntitlementDao {
}
private Subscription buildSubscription(final Subscription input, final InternalTenantContext context) {
+
if (input == null) {
return null;
}
-
final List<Subscription> bundleInput = new ArrayList<Subscription>();
if (input.getCategory() == ProductCategory.ADD_ON) {
final Subscription baseSubscription = getBaseSubscription(input.getBundleId(), false, context);
@@ -741,7 +741,7 @@ public class DefaultEntitlementDao implements EntitlementDao {
final Collection<EntitlementEvent> futureApiEvents = Collections2.filter(events, new Predicate<EntitlementEvent>() {
@Override
public boolean apply(final EntitlementEvent input) {
- return (input.getEffectiveDate().isAfter(clock.getUTCNow()) &&
+ return (input.isActive() && input.getEffectiveDate().isAfter(clock.getUTCNow()) &&
((input instanceof ApiEventCancel) || (input instanceof ApiEventChange)));
}
});
@@ -758,7 +758,7 @@ public class DefaultEntitlementDao implements EntitlementDao {
((!addonUtils.isAddonAvailableFromPlanName(baseProductName, futureBaseEvent.getEffectiveDate(), targetAddOnPlan)) ||
(addonUtils.isAddonIncludedFromPlanName(baseProductName, futureBaseEvent.getEffectiveDate(), targetAddOnPlan))));
- if (createCancelEvent) {
+ if (createCancelEvent && reloaded.getFutureEndDate() == null) {
final DateTime now = clock.getUTCNow();
final EntitlementEvent addOnCancelEvent = new ApiEventCancel(new ApiEventBuilder()
.setSubscriptionId(reloaded.getId())
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
index 81e1251..d0cd6b0 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
@@ -17,6 +17,7 @@
package com.ning.billing.entitlement.api.user;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
@@ -40,6 +41,7 @@ import com.ning.billing.catalog.api.PlanSpecifier;
import com.ning.billing.catalog.api.PriceListSet;
import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
import com.ning.billing.entitlement.api.user.SubscriptionStatusDryRun.DryRunChangeReason;
import com.ning.billing.util.clock.DefaultClock;
@@ -50,16 +52,16 @@ public class TestUserApiAddOn extends EntitlementTestSuiteWithEmbeddedDB {
public void testCreateCancelAddon() {
try {
final String baseProduct = "Shotgun";
- final BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+ final BillingPeriod baseTerm = BillingPeriod.ANNUAL;
final String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
- testUtil.createSubscription(bundle, baseProduct, baseTerm, basePriceList);
+ SubscriptionData baseSubscription = testUtil.createSubscription(bundle, baseProduct, baseTerm, basePriceList);
final String aoProduct = "Telescopic-Scope";
final BillingPeriod aoTerm = BillingPeriod.MONTHLY;
final String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
- final SubscriptionData aoSubscription = testUtil.createSubscription(bundle, aoProduct, aoTerm, aoPriceList);
+ SubscriptionData aoSubscription = testUtil.createSubscription(bundle, aoProduct, aoTerm, aoPriceList);
assertEquals(aoSubscription.getState(), SubscriptionState.ACTIVE);
final DateTime now = clock.getUTCNow();
@@ -68,9 +70,10 @@ public class TestUserApiAddOn extends EntitlementTestSuiteWithEmbeddedDB {
testListener.reset();
testListener.pushExpectedEvent(NextEvent.CANCEL);
assertTrue(testListener.isCompleted(5000));
-
+ aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId(), callContext);
assertEquals(aoSubscription.getState(), SubscriptionState.CANCELLED);
+
assertListenerStatus();
} catch (Exception e) {
Assert.fail(e.getMessage());
@@ -78,6 +81,88 @@ public class TestUserApiAddOn extends EntitlementTestSuiteWithEmbeddedDB {
}
@Test(groups = "slow")
+ public void testCreateCancelAddonAndThenBP() {
+ try {
+ final String baseProduct = "Shotgun";
+ final BillingPeriod baseTerm = BillingPeriod.ANNUAL;
+ final String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ SubscriptionData baseSubscription = testUtil.createSubscription(bundle, baseProduct, baseTerm, basePriceList);
+
+ final String aoProduct = "Telescopic-Scope";
+ final BillingPeriod aoTerm = BillingPeriod.MONTHLY;
+ final String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ SubscriptionData aoSubscription = testUtil.createSubscription(bundle, aoProduct, aoTerm, aoPriceList);
+ assertEquals(aoSubscription.getState(), SubscriptionState.ACTIVE);
+
+ // Move clock after a month
+ Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(1));
+ testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushExpectedEvent(NextEvent.PHASE);
+ clock.addDeltaFromReality(it.toDurationMillis());
+ assertTrue(testListener.isCompleted(5000));
+
+
+ // SET CTD TO CANCEL IN FUTURE
+ final DateTime now = clock.getUTCNow();
+ final Duration aoCtd = testUtil.getDurationMonth(1);
+ final DateTime newAOChargedThroughDate = DefaultClock.addDuration(now, aoCtd);
+ entitlementInternalApi.setChargedThroughDate(aoSubscription.getId(), newAOChargedThroughDate, internalCallContext);
+
+ final Duration bpCtd = testUtil.getDurationMonth(11);
+ final DateTime newBPChargedThroughDate = DefaultClock.addDuration(now, bpCtd);
+ entitlementInternalApi.setChargedThroughDate(baseSubscription.getId(), newBPChargedThroughDate, internalCallContext);
+
+ baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId(), callContext);
+ aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId(), callContext);
+
+ it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(1));
+ clock.addDeltaFromReality(it.toDurationMillis());
+
+ // CANCEL AO
+ aoSubscription.cancel(clock.getUTCNow(), callContext);
+ aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId(), callContext);
+ assertEquals(aoSubscription.getState(), SubscriptionState.ACTIVE);
+ assertTrue(aoSubscription.isSubscriptionFutureCancelled());
+
+ // CANCEL BASE NOW
+ baseSubscription.cancel(clock.getUTCNow(), callContext);
+ baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId(), callContext);
+ assertEquals(baseSubscription.getState(), SubscriptionState.ACTIVE);
+ assertTrue(baseSubscription.isSubscriptionFutureCancelled());
+
+ aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId(), callContext);
+ List<SubscriptionTransition> aoTransitions = aoSubscription.getAllTransitions();
+ assertEquals(aoTransitions.size(), 3);
+ assertEquals(aoTransitions.get(0).getTransitionType(), SubscriptionTransitionType.CREATE);
+ assertEquals(aoTransitions.get(1).getTransitionType(), SubscriptionTransitionType.PHASE);
+ assertEquals(aoTransitions.get(2).getTransitionType(), SubscriptionTransitionType.CANCEL);
+ assertTrue(aoSubscription.getFutureEndDate().compareTo(newAOChargedThroughDate) == 0);
+
+ testListener.pushExpectedEvent(NextEvent.UNCANCEL);
+ aoSubscription.uncancel(callContext);
+ assertTrue(testListener.isCompleted(5000));
+
+ aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId(), callContext);
+ aoTransitions = aoSubscription.getAllTransitions();
+ assertEquals(aoTransitions.size(), 4);
+ assertEquals(aoTransitions.get(0).getTransitionType(), SubscriptionTransitionType.CREATE);
+ assertEquals(aoTransitions.get(1).getTransitionType(), SubscriptionTransitionType.PHASE);
+ assertEquals(aoTransitions.get(2).getTransitionType(), SubscriptionTransitionType.UNCANCEL);
+ assertEquals(aoTransitions.get(3).getTransitionType(), SubscriptionTransitionType.CANCEL);
+ assertTrue(aoSubscription.getFutureEndDate().compareTo(newBPChargedThroughDate) == 0);
+
+ assertListenerStatus();
+ } catch (Exception e) {
+ Assert.fail(e.getMessage());
+ }
+ }
+
+
+
+
+ @Test(groups = "slow")
public void testCancelBPWithAddon() {
try {
final String baseProduct = "Shotgun";
@@ -138,6 +223,82 @@ public class TestUserApiAddOn extends EntitlementTestSuiteWithEmbeddedDB {
}
}
+
+ @Test(groups = "slow")
+ public void testCancelUncancelBPWithAddon() {
+ try {
+ final String baseProduct = "Shotgun";
+ final BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+ final String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ // CREATE BP
+ SubscriptionData baseSubscription = testUtil.createSubscription(bundle, baseProduct, baseTerm, basePriceList);
+
+ final String aoProduct = "Telescopic-Scope";
+ final BillingPeriod aoTerm = BillingPeriod.MONTHLY;
+ final String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ SubscriptionData aoSubscription = testUtil.createSubscription(bundle, aoProduct, aoTerm, aoPriceList);
+
+ testListener.reset();
+ testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushExpectedEvent(NextEvent.PHASE);
+
+ // MOVE CLOCK AFTER TRIAL + AO DISCOUNT
+ Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(2));
+ clock.addDeltaFromReality(it.toDurationMillis());
+ assertTrue(testListener.isCompleted(5000));
+
+ // SET CTD TO CANCEL IN FUTURE
+ final DateTime now = clock.getUTCNow();
+ final Duration ctd = testUtil.getDurationMonth(1);
+ // Why not just use clock.getUTCNow().plusMonths(1) ?
+ final DateTime newChargedThroughDate = DefaultClock.addDuration(now, ctd);
+ entitlementInternalApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate, internalCallContext);
+ baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId(), callContext);
+
+ // FUTURE CANCELLATION
+ baseSubscription.cancel(now, callContext);
+
+ // REFETCH AO SUBSCRIPTION AND CHECK THIS IS ACTIVE
+ aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId(), callContext);
+ assertEquals(aoSubscription.getState(), SubscriptionState.ACTIVE);
+ assertTrue(aoSubscription.isSubscriptionFutureCancelled());
+
+
+ testListener.reset();
+ testListener.pushExpectedEvent(NextEvent.UNCANCEL);
+ baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId(), callContext);
+ baseSubscription.uncancel(callContext);
+ assertTrue(testListener.isCompleted(5000));
+
+ aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId(), callContext);
+ assertEquals(aoSubscription.getState(), SubscriptionState.ACTIVE);
+ assertFalse(aoSubscription.isSubscriptionFutureCancelled());
+
+ // CANCEL AGAIN
+ it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(1));
+ clock.addDeltaFromReality(it.toDurationMillis());
+
+ baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId(), callContext);
+ baseSubscription.cancel(clock.getUTCNow(), callContext);
+ baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId(), callContext);
+ assertEquals(baseSubscription.getState(), SubscriptionState.ACTIVE);
+ assertTrue(baseSubscription.isSubscriptionFutureCancelled());
+
+ aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId(), callContext);
+ assertEquals(aoSubscription.getState(), SubscriptionState.ACTIVE);
+ assertTrue(aoSubscription.isSubscriptionFutureCancelled());
+ assertListenerStatus();
+
+ } catch (Exception e) {
+ Assert.fail(e.getMessage());
+ }
+ }
+
+
+
+
@Test(groups = "slow")
public void testChangeBPWithAddonIncluded() {
try {