Details
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
index ba71e6f..8bb42d5 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
@@ -28,15 +28,12 @@ import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
-import org.skife.jdbi.v2.IDBI;
-
import org.killbill.billing.ObjectType;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.ProductCategory;
-import org.killbill.clock.Clock;
import org.killbill.billing.entitlement.AccountEventsStreams;
import org.killbill.billing.entitlement.EventsStream;
import org.killbill.billing.entitlement.api.BlockingState;
@@ -51,10 +48,13 @@ import org.killbill.billing.subscription.api.SubscriptionBase;
import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
+import org.killbill.billing.subscription.api.user.SubscriptionBaseTransition;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.callcontext.TenantContext;
import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.clock.Clock;
+import org.skife.jdbi.v2.IDBI;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
@@ -172,13 +172,7 @@ public class EventsStreamBuilder {
for (final UUID bundleId : subscriptions.keySet()) {
final SubscriptionBaseBundle bundle = bundlesPerId.get(bundleId);
final List<SubscriptionBase> allSubscriptionsForBundle = subscriptions.get(bundleId);
- final SubscriptionBase baseSubscription = Iterables.<SubscriptionBase>tryFind(allSubscriptionsForBundle,
- new Predicate<SubscriptionBase>() {
- @Override
- public boolean apply(final SubscriptionBase input) {
- return ProductCategory.BASE.equals(input.getLastActiveProduct().getCategory());
- }
- }).orNull();
+ final SubscriptionBase baseSubscription = findBaseSubscription(allSubscriptionsForBundle);
final List<BlockingState> bundleBlockingStates = Objects.firstNonNull(blockingStatesPerBundle.get(bundleId), ImmutableList.<BlockingState>of());
if (entitlementsPerBundle.get(bundleId) == null) {
@@ -230,13 +224,7 @@ public class EventsStreamBuilder {
subscription = subscriptionInternalApi.getSubscriptionFromId(entitlementId, internalTenantContext);
bundle = subscriptionInternalApi.getBundleFromId(subscription.getBundleId(), internalTenantContext);
allSubscriptionsForBundle = subscriptionInternalApi.getSubscriptionsForBundle(subscription.getBundleId(), null, internalTenantContext);
- baseSubscription = Iterables.<SubscriptionBase>tryFind(allSubscriptionsForBundle,
- new Predicate<SubscriptionBase>() {
- @Override
- public boolean apply(final SubscriptionBase input) {
- return ProductCategory.BASE.equals(input.getLastActiveProduct().getCategory());
- }
- }).orNull(); // null for standalone subscriptions
+ baseSubscription = findBaseSubscription(allSubscriptionsForBundle);
} catch (SubscriptionBaseApiException e) {
throw new EntitlementApiException(e);
}
@@ -341,4 +329,18 @@ public class EventsStreamBuilder {
internalTenantContext,
clock.getUTCNow());
}
+
+ private SubscriptionBase findBaseSubscription(final Iterable<SubscriptionBase> subscriptions) {
+ return Iterables.<SubscriptionBase>tryFind(subscriptions,
+ new Predicate<SubscriptionBase>() {
+ @Override
+ public boolean apply(final SubscriptionBase input) {
+ final List<SubscriptionBaseTransition> allTransitions = input.getAllTransitions();
+ return !allTransitions.isEmpty() &&
+ allTransitions.get(0).getNextPlan() != null &&
+ allTransitions.get(0).getNextPlan().getProduct() != null &&
+ ProductCategory.BASE.equals(allTransitions.get(0).getNextPlan().getProduct().getCategory());
+ }
+ }).orNull(); // null for standalone subscriptions
+ }
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java
index 5036c03..24e5eb3 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java
@@ -309,11 +309,12 @@ public class SubscriptionJson extends JsonBase {
public SubscriptionJson(final Subscription subscription, @Nullable final AccountAuditLogs accountAuditLogs) {
super(toAuditLogJson(accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForSubscription(subscription.getId())));
this.startDate = subscription.getEffectiveStartDate();
- this.productName = subscription.getLastActiveProduct().getName();
- this.productCategory = subscription.getLastActiveProductCategory().name();
- this.billingPeriod = subscription.getLastActivePlan().getRecurringBillingPeriod().toString();
- this.phaseType = subscription.getLastActivePhase().getPhaseType().toString();
- this.priceList = subscription.getLastActivePriceList().getName();
+ // last* fields can be null if the subscription starts in the future
+ this.productName = subscription.getLastActiveProduct() == null ? null : subscription.getLastActiveProduct().getName();
+ this.productCategory = subscription.getLastActiveProductCategory() == null ? null : subscription.getLastActiveProductCategory().name();
+ this.billingPeriod = subscription.getLastActivePlan() == null ? null : subscription.getLastActivePlan().getRecurringBillingPeriod().toString();
+ this.phaseType = subscription.getLastActivePhase() == null ? null : subscription.getLastActivePhase().getPhaseType().toString();
+ this.priceList = subscription.getLastActivePriceList() == null ? null : subscription.getLastActivePriceList().getName();
this.state = subscription.getState().name();
this.sourceType = subscription.getSourceType().name();
this.cancelledDate = subscription.getEffectiveEndDate();
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java b/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
index d59430a..99bc820 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
@@ -320,6 +320,7 @@ public class OverdueStateApplicator {
new Predicate<Entitlement>() {
@Override
public boolean apply(final Entitlement entitlement) {
+ // Note: this would miss add-ons created in the future. We should expose a new API to do something similar to EventsStreamBuilder#findBaseSubscription
return !ProductCategory.ADD_ON.equals(entitlement.getLastActiveProductCategory());
}
});
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 c0844da..9688a17 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
@@ -267,7 +267,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
return data.getPreviousPlan().getProduct();
} else {
final Plan currentPlan = getCurrentPlan();
- // currentPlan can be null when playing with the clock
+ // currentPlan can be null when playing with the clock (subscription created in the future)
return currentPlan == null ? null : currentPlan.getProduct();
}
}
@@ -289,7 +289,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
return data.getPreviousPlan().getProduct().getCategory();
} else {
final Plan currentPlan = getCurrentPlan();
- // currentPlan can be null when playing with the clock
+ // currentPlan can be null when playing with the clock (subscription created in the future)
return currentPlan == null ? null : currentPlan.getProduct().getCategory();
}
}
@@ -321,7 +321,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
return data.getPreviousPlan().getRecurringBillingPeriod();
} else {
final Plan currentPlan = getCurrentPlan();
- // currentPlan can be null when playing with the clock
+ // currentPlan can be null when playing with the clock (subscription created in the future)
return currentPlan.getRecurringBillingPeriod();
}
}