diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
index cffcd6b..28f23e0 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
@@ -385,6 +385,18 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
return planChangeResult;
}
+ 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;
+ }
+
private void doChangePlan(final DefaultSubscriptionBase subscription,
final PlanSpecifier spec,
final List<PlanPhasePriceOverride> overrides,
@@ -394,6 +406,16 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(overrides, context);
final Plan newPlan = catalogService.getFullCatalog(true, true, internalCallContext).createOrFindPlan(spec, overridesWithContext, effectiveDate, subscription.getStartDate());
+ if (ProductCategory.ADD_ON.toString().equalsIgnoreCase(newPlan.getProduct().getCategory().toString())) {
+ if (newPlan.getPlansAllowedInBundle() != -1
+ && newPlan.getPlansAllowedInBundle() > 0
+ && countExistingAddOnsWithSamePlanName(dao.getSubscriptions(subscription.getBundleId(), null, internalCallContext), newPlan.getName())
+ >= newPlan.getPlansAllowedInBundle()) {
+ // the plan can be changed to the new value, because it has reached its limit by bundle
+ throw new SubscriptionBaseApiException(ErrorCode.SUB_CHANGE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE, newPlan.getName());
+ }
+ }
+
if (newPlan.getProduct().getCategory() != subscription.getCategory()) {
throw new SubscriptionBaseApiException(ErrorCode.SUB_CHANGE_INVALID, subscription.getId());
}