killbill-aplcache
#545 - Removed logic from JAXRS - Moved logic to the SubscriptionInternalApi. - …
9/2/2016 11:10:12 AM
Changes
Details
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 695e480..b16e5bc 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
@@ -248,6 +248,68 @@ public class TestSubscription extends TestIntegrationBase {
}
@Test(groups = "slow")
+ public void testCreateSubscriptionWithAddOnsWithLimitException() throws Exception {
+ final LocalDate initialDate = new LocalDate(2015, 10, 1);
+ clock.setDay(initialDate);
+
+ final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
+ final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+ final PlanPhaseSpecifier addOnSpec1 = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+ final PlanPhaseSpecifier addOnSpec2 = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+ final PlanPhaseSpecifier addOnSpec3 = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+ final String externalKey = "baseExternalKey";
+ EntitlementSpecifier baseEntitlementSpecifier = new DefaultEntitlementSpecifier(baseSpec, null);
+ EntitlementSpecifier addOnEntitlementSpecifier1 = new DefaultEntitlementSpecifier(addOnSpec1, null);
+ EntitlementSpecifier addOnEntitlementSpecifier2 = new DefaultEntitlementSpecifier(addOnSpec2, null);
+ EntitlementSpecifier addOnEntitlementSpecifier3 = new DefaultEntitlementSpecifier(addOnSpec3, null);
+
+ final List<EntitlementSpecifier> specifierList = new ArrayList<EntitlementSpecifier>();
+ specifierList.add(baseEntitlementSpecifier);
+ specifierList.add(addOnEntitlementSpecifier1);
+ specifierList.add(addOnEntitlementSpecifier2);
+ specifierList.add(addOnEntitlementSpecifier3);
+
+ try {
+ entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
+ } catch (final EntitlementApiException e) {
+ assertEquals(e.getCode(), ErrorCode.SUB_CREATE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE.getCode());
+ }
+ }
+
+ @Test(groups = "slow")
+ public void testCreateBaseSubscriptionAndAddOnsWithLimitException() throws Exception {
+ final LocalDate initialDate = new LocalDate(2015, 10, 1);
+ clock.setDay(initialDate);
+
+ final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
+ final PlanPhaseSpecifier addOnSpec1 = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+ final PlanPhaseSpecifier addOnSpec2 = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+ final PlanPhaseSpecifier addOnSpec3 = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+ // Create base subscription
+ final Entitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), account.getExternalKey(), "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+ assertNotNull(baseEntitlement);
+
+ // Create first add_on subscription
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOnSpec1, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOnSpec2, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ try {
+ entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOnSpec3, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
+ } catch (final EntitlementApiException e) {
+ assertEquals(e.getCode(), ErrorCode.SUB_CREATE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE.getCode());
+ }
+ }
+
+ @Test(groups = "slow")
public void testCancelFutureSubscriptionWithPolicy() throws Exception {
final LocalDate initialDate = new LocalDate(2015, 9, 1);
clock.setDay(initialDate);
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 885b7cb..856fd91 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
@@ -76,7 +76,7 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements
//No other value is allowed for Tiered ADDONS
//A value of -1 means unlimited
@XmlElement(required = false)
- private Integer plansAllowedInBundle = 1;
+ private Integer plansAllowedInBundle = -1;
private String priceListName;
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogUpdater.java b/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogUpdater.java
index c101cd0..6f2af55 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogUpdater.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogUpdater.java
@@ -383,7 +383,7 @@ public class TestCatalogUpdater extends CatalogTestSuiteNoDB {
" </recurring>\n" +
" <usages/>\n" +
" </finalPhase>\n" +
- " <plansAllowedInBundle>1</plansAllowedInBundle>\n" +
+ " <plansAllowedInBundle>-1</plansAllowedInBundle>\n" +
" </plan>\n" +
" <plan name=\"sports-monthly\">\n" +
" <product>Sports</product>\n" +
@@ -428,7 +428,7 @@ public class TestCatalogUpdater extends CatalogTestSuiteNoDB {
" </recurring>\n" +
" <usages/>\n" +
" </finalPhase>\n" +
- " <plansAllowedInBundle>1</plansAllowedInBundle>\n" +
+ " <plansAllowedInBundle>-1</plansAllowedInBundle>\n" +
" </plan>\n" +
" <plan name=\"super-monthly\">\n" +
" <product>Super</product>\n" +
@@ -473,7 +473,7 @@ public class TestCatalogUpdater extends CatalogTestSuiteNoDB {
" </recurring>\n" +
" <usages/>\n" +
" </finalPhase>\n" +
- " <plansAllowedInBundle>1</plansAllowedInBundle>\n" +
+ " <plansAllowedInBundle>-1</plansAllowedInBundle>\n" +
" </plan>\n" +
" <plan name=\"dynamic-annual\">\n" +
" <product>Dynamic</product>\n" +
@@ -510,7 +510,7 @@ public class TestCatalogUpdater extends CatalogTestSuiteNoDB {
" </recurring>\n" +
" <usages/>\n" +
" </finalPhase>\n" +
- " <plansAllowedInBundle>1</plansAllowedInBundle>\n" +
+ " <plansAllowedInBundle>-1</plansAllowedInBundle>\n" +
" </plan>\n" +
" </plans>\n" +
" <priceLists>\n" +
diff --git a/catalog/src/test/resources/catalogTest.xml b/catalog/src/test/resources/catalogTest.xml
index a1cdec3..670abb5 100644
--- a/catalog/src/test/resources/catalogTest.xml
+++ b/catalog/src/test/resources/catalogTest.xml
@@ -838,6 +838,7 @@
</recurringPrice>
</recurring>
</finalPhase>
+ <plansAllowedInBundle>2</plansAllowedInBundle>
</plan>
<plan name="cleaning-monthly">
<product>Cleaning</product>
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
index 30fccac..8a9a799 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
@@ -41,7 +41,6 @@ import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import org.joda.time.LocalDate;
-import org.killbill.billing.ErrorCode;
import org.killbill.billing.ObjectType;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountApiException;
@@ -64,7 +63,6 @@ import org.killbill.billing.entitlement.api.Subscription;
import org.killbill.billing.entitlement.api.SubscriptionApi;
import org.killbill.billing.entitlement.api.SubscriptionApiException;
import org.killbill.billing.entitlement.api.SubscriptionBundle;
-import org.killbill.billing.entitlement.api.SubscriptionEvent;
import org.killbill.billing.events.BlockingTransitionInternalEvent;
import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
import org.killbill.billing.events.InvoiceCreationInternalEvent;
@@ -204,21 +202,6 @@ public class SubscriptionResource extends JaxRsResourceBase {
new PlanPhaseSpecifier(entitlement.getProductName(),
BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList(), phaseType);
- // verify the number of subscriptions (of the same kind) allowed per bundle
- if (createAddOnEntitlement) {
- String currentPlanName = entitlement.getPlanName() != null ? entitlement.getPlanName() :
- entitlement.getProductName() + "-" + entitlement.getBillingPeriod();
- int existingSubscriptions = countExistingAddOnsWithSamePlanName(
- subscriptionApi.getSubscriptionBundle(getBundleIdForAddOnCreation(entitlement), callContext).getSubscriptions(), currentPlanName);
- Entitlement entitlementForId = entitlementApi.getEntitlementForId(getBundleIdForAddOnCreation(entitlement), callContext);
- if (entitlementForId.getLastActivePlan() != null
- && entitlementForId.getLastActivePlan().getPlansAllowedInBundle() != -1
- && existingSubscriptions == entitlementForId.getLastActivePlan().getPlansAllowedInBundle()) {
- // a new ADD_ON subscription of the same plan can't be added because it has reached its limit by bundle
- throw new EntitlementApiException(ErrorCode.SUB_CREATE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE, currentPlanName);
- }
- }
-
final LocalDate resolvedEntitlementDate = requestedDate != null ? toLocalDate(requestedDate) : toLocalDate(entitlementDate);
final LocalDate resolvedBillingDate = requestedDate != null ? toLocalDate(requestedDate) : toLocalDate(billingDate);
final List<PlanPhasePriceOverride> overrides = PhasePriceOverrideJson.toPlanPhasePriceOverrides(entitlement.getPriceOverrides(), spec, account.getCurrency());
@@ -256,27 +239,6 @@ public class SubscriptionResource extends JaxRsResourceBase {
return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion, callContext);
}
- private int countExistingAddOnsWithSamePlanName(final List<Subscription> subscriptionsForBundle, final String planName) {
- int countExistingAddOns = 0;
- for (Subscription subscription : subscriptionsForBundle) {
- if (getSubscriptionPlanName(subscription).equalsIgnoreCase(planName)
- && subscription.getLastActiveProductCategory() != null
- && ProductCategory.ADD_ON.toString().equals(subscription.getLastActiveProductCategory().toString())) {
- countExistingAddOns++;
- }
- }
- return countExistingAddOns;
- }
-
- private String getSubscriptionPlanName(final Subscription subscription) {
- final SubscriptionEvent firstEvent = subscription.getSubscriptionEvents().isEmpty() ? null : subscription.getSubscriptionEvents().get(0);
- if (subscription.getLastActivePlan() == null) {
- return firstEvent == null ? "" : firstEvent.getNextPlan().getName();
- } else {
- return subscription.getLastActivePlan().getName();
- }
- }
-
@TimedResource
@POST
@Path("/createEntitlementWithAddOns")
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
index dbf1446..ad3d7c7 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
@@ -37,7 +37,6 @@ 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.BillingAlignment;
-import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Catalog;
import org.killbill.billing.catalog.api.CatalogApiException;
import org.killbill.billing.catalog.api.CatalogService;
@@ -170,6 +169,18 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
}
final DefaultSubscriptionBase baseSubscription = (DefaultSubscriptionBase) dao.getBaseSubscription(bundleId, context);
+
+ // verify the number of subscriptions (of the same kind) allowed per bundle
+ if (ProductCategory.ADD_ON.toString().equalsIgnoreCase(plan.getProduct().getCategory().toString())) {
+ if (plan.getPlansAllowedInBundle() != -1
+ && plan.getPlansAllowedInBundle() > 0
+ && countExistingAddOnsWithSamePlanName(getSubscriptionsForBundle(bundleId, null, context), plan.getName())
+ >= plan.getPlansAllowedInBundle()) {
+ // a new ADD_ON subscription of the same plan can't be added because it has reached its limit by bundle
+ throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE, plan.getName());
+ }
+ }
+
final DateTime bundleStartDate = getBundleStartDateWithSanity(bundleId, baseSubscription, plan, effectiveDate, context);
return apiService.createPlan(new SubscriptionBuilder()
.setId(UUIDs.randomUUID())
@@ -184,6 +195,18 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
}
}
+ private int countExistingAddOnsWithSamePlanName(final List<SubscriptionBase> subscriptionsForBundle, final String planName) {
+ int countExistingAddOns = 0;
+ for (SubscriptionBase subscription : subscriptionsForBundle) {
+ if (subscription.getCurrentPlan().getName().equalsIgnoreCase(planName)
+ && subscription.getLastActiveProduct().getCategory() != null
+ && ProductCategory.ADD_ON.equals(subscription.getLastActiveProduct().getCategory())) {
+ countExistingAddOns++;
+ }
+ }
+ return countExistingAddOns;
+ }
+
@Override
public List<SubscriptionBase> createBaseSubscriptionWithAddOns(final UUID bundleId, final Iterable<EntitlementSpecifier> entitlements, final DateTime requestedDateWithMs, final boolean isMigrated, final InternalCallContext context) throws SubscriptionBaseApiException {
@@ -221,6 +244,18 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
}
}
+ // verify the number of subscriptions (of the same kind) allowed per bundle and the existing ones
+ if (ProductCategory.ADD_ON.toString().equalsIgnoreCase(plan.getProduct().getCategory().toString())) {
+ if (plan.getPlansAllowedInBundle() != -1 && plan.getPlansAllowedInBundle() > 0) {
+ int existingAddOnsWithSamePlanName = countExistingAddOnsWithSamePlanName(getSubscriptionsForBundle(bundleId, null, context), plan.getName());
+ int currentAddOnsWithSamePlanName = countCurrentAddOnsWithSamePlanName(entitlements, catalog, plan.getName(), effectiveDate, callContext);
+ if ((existingAddOnsWithSamePlanName + currentAddOnsWithSamePlanName) > plan.getPlansAllowedInBundle()) {
+ // a new ADD_ON subscription of the same plan can't be added because it has reached its limit by bundle
+ throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE, plan.getName());
+ }
+ }
+ }
+
SubscriptionSpecifier subscription = new SubscriptionSpecifier();
subscription.setRealPriceList(plan.getPriceListName());
subscription.setEffectiveDate(effectiveDate);
@@ -250,6 +285,25 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
}
}
+ private int countCurrentAddOnsWithSamePlanName(final Iterable<EntitlementSpecifier> entitlements,
+ final Catalog catalog, final String planName,
+ final DateTime effectiveDate, final CallContext callContext) throws CatalogApiException {
+ int countCurrentAddOns = 0;
+ for (EntitlementSpecifier entitlement : entitlements) {
+ final PlanPhaseSpecifier spec = entitlement.getPlanPhaseSpecifier();
+ final PlanPhasePriceOverridesWithCallContext overridesWithContext =
+ new DefaultPlanPhasePriceOverridesWithCallContext(entitlement.getOverrides(), callContext);
+ final Plan plan = catalog.createOrFindPlan(spec, overridesWithContext, effectiveDate);
+
+ if (plan.getName().equalsIgnoreCase(planName)
+ && plan.getProduct().getCategory() != null
+ && ProductCategory.ADD_ON.equals(plan.getProduct().getCategory())) {
+ countCurrentAddOns++;
+ }
+ }
+ return countCurrentAddOns;
+ }
+
@Override
public void cancelBaseSubscriptions(final Iterable<SubscriptionBase> subscriptions, final BillingActionPolicy policy, final InternalCallContext context) throws SubscriptionBaseApiException {
apiService.cancelWithPolicyNoValidation(Iterables.<SubscriptionBase, DefaultSubscriptionBase>transform(subscriptions,