killbill-memoizeit
Changes
entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java 5(+5 -0)
entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java 57(+54 -3)
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 42d9f4a..be4eac4 100644
--- a/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
+++ b/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
@@ -56,6 +56,8 @@ public interface EventsStream {
boolean isEntitlementActive();
+ boolean isEntitlementPending();
+
boolean isBlockChange();
boolean isEntitlementCancelled();
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 8df759d..7ea0b3b 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
@@ -299,8 +299,10 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
public Entitlement doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
final EventsStream eventsStreamForBaseSubscription = eventsStreamBuilder.buildForBaseSubscription(bundleId, callContext);
- // Check the base entitlement state is active
- if (!eventsStreamForBaseSubscription.isEntitlementActive()) {
+ if (eventsStreamForBaseSubscription.isEntitlementCancelled() ||
+ (eventsStreamForBaseSubscription.isEntitlementPending() &&
+ (baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate() == null ||
+ baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate().compareTo(eventsStreamForBaseSubscription.getEntitlementEffectiveStartDate()) < 0))) {
throw new EntitlementApiException(ErrorCode.SUB_GET_NO_SUCH_BASE_SUBSCRIPTION, bundleId);
}
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 19d7cb9..eded306 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
@@ -188,6 +188,11 @@ public class DefaultEventsStream implements EventsStream {
}
@Override
+ public boolean isEntitlementPending() {
+ return entitlementState == EntitlementState.PENDING;
+ }
+
+ @Override
public boolean isEntitlementCancelled() {
return entitlementState == EntitlementState.CANCELLED;
}
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 be7245c..0d5f109 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
@@ -279,6 +279,57 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
}
@Test(groups = "slow")
+ public void testAddEntitlementOnPendingBase() throws AccountApiException, EntitlementApiException {
+ final LocalDate initialDate = new LocalDate(2013, 8, 7);
+ clock.setDay(initialDate);
+
+ final Account account = createAccount(getAccountData(7));
+
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+ // Create entitlement and check each field
+ final LocalDate startDate = initialDate.plusDays(10);
+ final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, startDate, startDate, false, ImmutableList.<PluginProperty>of(), callContext);
+
+ // Add ADD_ON immediately. Because BASE is PENDING should fail
+ final PlanPhaseSpecifier spec1 = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+ try {
+ entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec1, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
+ fail("Should not succeed to create ADD_On prior BASE is active");
+ } catch (final EntitlementApiException e) {
+ assertEquals(e.getCode(), ErrorCode.SUB_GET_NO_SUCH_BASE_SUBSCRIPTION.getCode());
+ }
+
+
+ // Add ADD_ON with a startDate similar to BASE
+ final Entitlement telescopicEntitlement = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec1, null, startDate, startDate, false, ImmutableList.<PluginProperty>of(), callContext);
+
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.BLOCK);
+ clock.addDays(10);
+ assertListenerStatus();
+
+
+ assertEquals(telescopicEntitlement.getAccountId(), account.getId());
+ assertEquals(telescopicEntitlement.getExternalKey(), account.getExternalKey());
+
+ assertEquals(telescopicEntitlement.getEffectiveStartDate(), startDate);
+ assertNull(telescopicEntitlement.getEffectiveEndDate());
+
+ assertEquals(telescopicEntitlement.getLastActivePriceList().getName(), PriceListSet.DEFAULT_PRICELIST_NAME);
+ assertEquals(telescopicEntitlement.getLastActiveProduct().getName(), "Telescopic-Scope");
+ assertEquals(telescopicEntitlement.getLastActivePhase().getName(), "telescopic-scope-monthly-discount");
+ assertEquals(telescopicEntitlement.getLastActivePlan().getName(), "telescopic-scope-monthly");
+ assertEquals(telescopicEntitlement.getLastActiveProductCategory(), ProductCategory.ADD_ON);
+
+ List<Entitlement> bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(telescopicEntitlement.getBundleId(), callContext);
+ assertEquals(bundleEntitlements.size(), 2);
+
+ bundleEntitlements = entitlementApi.getAllEntitlementsForAccountIdAndExternalKey(account.getId(), account.getExternalKey(), callContext);
+ assertEquals(bundleEntitlements.size(), 2);
+ }
+
+ @Test(groups = "slow")
public void testPauseUnpause() throws AccountApiException, EntitlementApiException {
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
@@ -319,12 +370,12 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
assertEquals(cur.getState(), EntitlementState.BLOCKED);
}
- // Try to add an ADD_ON, it should fail
+ // 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);
- final Entitlement telescopicEntitlement3 = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec1, null, effectiveDateSpec1, effectiveDateSpec1, false, ImmutableList.<PluginProperty>of(), callContext);
+ entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec3, null, effectiveDateSpec1, effectiveDateSpec1, false, ImmutableList.<PluginProperty>of(), callContext);
} catch (EntitlementApiException e) {
- assertEquals(e.getCode(), ErrorCode.SUB_GET_NO_SUCH_BASE_SUBSCRIPTION.getCode());
+ assertEquals(e.getCode(), ErrorCode.BLOCK_BLOCKED_ACTION.getCode());
}
clock.addDays(3);
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
index e255419..1dd9003 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
@@ -204,6 +204,14 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
: getPreviousTransition().getNextPlan();
}
+ public Plan getCurrentOrPendingPlan() {
+ if (getState() == EntitlementState.PENDING) {
+ return getPendingTransition().getNextPlan();
+ } else {
+ return getCurrentPlan();
+ }
+ }
+
@Override
public PriceList getCurrentPriceList() {
return (getPreviousTransition() == null) ? null :
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/addon/AddonUtils.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/addon/AddonUtils.java
index 8fd9da7..5b238c2 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/addon/AddonUtils.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/addon/AddonUtils.java
@@ -47,19 +47,22 @@ public class AddonUtils {
public void checkAddonCreationRights(final DefaultSubscriptionBase baseSubscription, final Plan targetAddOnPlan, final DateTime requestedDate, final InternalTenantContext context)
throws SubscriptionBaseApiException, CatalogApiException {
- if (baseSubscription.getState() != EntitlementState.ACTIVE) {
+
+ if (baseSubscription.getState() == EntitlementState.CANCELLED ||
+ (baseSubscription.getState() == EntitlementState.PENDING && context.toLocalDate(baseSubscription.getStartDate()).compareTo(context.toLocalDate(requestedDate)) < 0)) {
throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_AO_BP_NON_ACTIVE, targetAddOnPlan.getName());
}
- final Product baseProduct = catalogService.getFullCatalog(true, true, context).findProduct(baseSubscription.getCurrentPlan().getProduct().getName(), requestedDate);
+ final Plan currentOrPendingPlan = baseSubscription.getCurrentOrPendingPlan();
+ final Product baseProduct = catalogService.getFullCatalog(true, true, context).findProduct(currentOrPendingPlan.getProduct().getName(), requestedDate);
if (isAddonIncluded(baseProduct, targetAddOnPlan)) {
throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_AO_ALREADY_INCLUDED,
- targetAddOnPlan.getName(), baseSubscription.getCurrentPlan().getProduct().getName());
+ targetAddOnPlan.getName(), currentOrPendingPlan.getProduct().getName());
}
if (!isAddonAvailable(baseProduct, targetAddOnPlan)) {
throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_AO_NOT_AVAILABLE,
- targetAddOnPlan.getName(), baseSubscription.getCurrentPlan().getProduct().getName());
+ targetAddOnPlan.getName(), currentOrPendingPlan.getProduct().getName());
}
}