killbill-aplcache
Changes
entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java 451(+96 -355)
Details
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
index 54b1a92..df82f4d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
@@ -25,15 +25,15 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
-import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
+import javax.annotation.Nullable;
+
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
-import org.joda.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -95,7 +95,6 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
// - blocking state events occur at various level (account, bundle and subscription) so for higher level, we need to dispatch that on each subscription.
//
private List<SubscriptionEvent> computeEvents(final Collection<Entitlement> entitlements, final List<BlockingState> allBlockingStates, final DateTimeZone accountTimeZone) {
-
// Extract ids for all entitlement in the list
final Set<UUID> allEntitlementUUIDs = new TreeSet(Collections2.transform(entitlements, new Function<Entitlement, UUID>() {
@Override
@@ -152,10 +151,33 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
final int index = insertFromBlockingEvent(accountTimeZone, allEntitlementUUIDs, result, bs, bs.getEffectiveDate(), newEvents);
insertAfterIndex(result, newEvents, index);
}
+
reOrderSubscriptionEventsOnSameDateByType(result);
+
+ removeOverlappingSubscriptionEvents(result);
+
return result;
}
+ // Make sure the argument supports the remove operation - hence expect a LinkedList, not a List
+ private void removeOverlappingSubscriptionEvents(final LinkedList<SubscriptionEvent> events) {
+ final Iterator<SubscriptionEvent> iterator = events.iterator();
+ final Map<String, DefaultSubscriptionEvent> prevPerService = new HashMap<String, DefaultSubscriptionEvent>();
+ while (iterator.hasNext()) {
+ final DefaultSubscriptionEvent current = (DefaultSubscriptionEvent) iterator.next();
+ final DefaultSubscriptionEvent prev = prevPerService.get(current.getServiceName());
+ if (prev != null) {
+ if (current.overlaps(prev)) {
+ iterator.remove();
+ } else {
+ prevPerService.put(current.getServiceName(), current);
+ }
+ } else {
+ prevPerService.put(current.getServiceName(), current);
+ }
+ }
+ }
+
//
// All events have been inserted and should be at the right place, except that we want to ensure that events for a given subscription,
// and for a given time are ordered by SubscriptionEventType.
@@ -280,7 +302,6 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
// For each target compute the new events that should be inserted in the stream
for (final UUID target : targetEntitlementIds) {
-
final SubscriptionEvent[] prevNext = findPrevNext(result, target, curInsertion);
final TargetState curTargetState = targetStates.get(target);
@@ -339,36 +360,8 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
}
}
}
- sanitizeForBaseRecreateEvents(result);
- return result;
- }
- //
- // Old version of code would use CANCEL/RE_CREATE to simulate PAUSE_BILLING/RESUME_BILLING
- // (Relies on the assumption that there is no blocking_state event matching that CACNEL event so:
- // 1. The STOP_BILLING (coming from the row CANCEL event) should be transformed into a PAUSE_BILLING
- // 2. We also add a PAUSE_ENTITLEMENT at the same time as the PAUSE_BILLING
- //
- private void sanitizeForBaseRecreateEvents(final LinkedList<SubscriptionEvent> input) {
- final Collection<UUID> guiltyEntitlementIds = new TreeSet<UUID>();
- final ListIterator<SubscriptionEvent> it = input.listIterator(input.size());
- while (it.hasPrevious()) {
- final SubscriptionEvent cur = it.previous();
- if (cur.getSubscriptionEventType() == SubscriptionEventType.RESUME_BILLING) {
- guiltyEntitlementIds.add(cur.getEntitlementId());
- continue;
- }
- if (cur.getSubscriptionEventType() == SubscriptionEventType.STOP_BILLING &&
- guiltyEntitlementIds.contains(cur.getEntitlementId())) {
- guiltyEntitlementIds.remove(cur.getEntitlementId());
- final SubscriptionEvent correctedBillingEvent = new DefaultSubscriptionEvent((DefaultSubscriptionEvent) cur, SubscriptionEventType.PAUSE_BILLING);
- it.set(correctedBillingEvent);
-
- // Old versions of the code won't have an associated event in blocking_states - we need to add one on the fly
- final SubscriptionEvent correctedEntitlementEvent = new DefaultSubscriptionEvent((DefaultSubscriptionEvent) cur, SubscriptionEventType.PAUSE_ENTITLEMENT);
- it.add(correctedEntitlementEvent);
- }
- }
+ return result;
}
private void insertSubscriptionEvent(final SubscriptionEvent event, final List<SubscriptionEvent> result) {
@@ -405,7 +398,66 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
result.add(index, event);
}
- private SubscriptionEvent toSubscriptionEvent(final SubscriptionEvent prev, final SubscriptionEvent next, final UUID entitlementId, final BlockingState in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone) {
+ private SubscriptionEvent toSubscriptionEvent(@Nullable final SubscriptionEvent prev, @Nullable final SubscriptionEvent next,
+ final UUID entitlementId, final BlockingState in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone) {
+ final Product prevProduct;
+ final Plan prevPlan;
+ final PlanPhase prevPlanPhase;
+ final PriceList prevPriceList;
+ final BillingPeriod prevBillingPeriod;
+ // Enforce prev = null for start events
+ if (prev == null || SubscriptionEventType.START_ENTITLEMENT.equals(eventType) || SubscriptionEventType.START_BILLING.equals(eventType)) {
+ prevProduct = null;
+ prevPlan = null;
+ prevPlanPhase = null;
+ prevPriceList = null;
+ prevBillingPeriod = null;
+ } else {
+ // We look for the next for the 'prev' meaning we we are headed to, but if this is null -- for example on cancellation we get the prev which gives the correct state.
+ prevProduct = (prev.getNextProduct() != null ? prev.getNextProduct() : prev.getPrevProduct());
+ prevPlan = (prev.getNextPlan() != null ? prev.getNextPlan() : prev.getPrevPlan());
+ prevPlanPhase = (prev.getNextPhase() != null ? prev.getNextPhase() : prev.getPrevPhase());
+ prevPriceList = (prev.getNextPriceList() != null ? prev.getNextPriceList() : prev.getPrevPriceList());
+ prevBillingPeriod = (prev.getNextBillingPeriod() != null ? prev.getNextBillingPeriod() : prev.getPrevBillingPeriod());
+ }
+
+ final Product nextProduct;
+ final Plan nextPlan;
+ final PlanPhase nextPlanPhase;
+ final PriceList nextPriceList;
+ final BillingPeriod nextBillingPeriod;
+ if (SubscriptionEventType.PAUSE_ENTITLEMENT.equals(eventType) || SubscriptionEventType.PAUSE_BILLING.equals(eventType) ||
+ SubscriptionEventType.RESUME_ENTITLEMENT.equals(eventType) || SubscriptionEventType.RESUME_BILLING.equals(eventType) ||
+ (SubscriptionEventType.SERVICE_STATE_CHANGE.equals(eventType) && (prev == null || (!SubscriptionEventType.STOP_ENTITLEMENT.equals(prev.getSubscriptionEventType()) && !SubscriptionEventType.STOP_BILLING.equals(prev.getSubscriptionEventType()))))) {
+ // Enforce next = prev for pause/resume events as well as service changes
+ nextProduct = prevProduct;
+ nextPlan = prevPlan;
+ nextPlanPhase = prevPlanPhase;
+ nextPriceList = prevPriceList;
+ nextBillingPeriod = prevBillingPeriod;
+ } else if (next == null) {
+ // Enforce next = null for stop events
+ if (prev == null || SubscriptionEventType.STOP_ENTITLEMENT.equals(eventType) || SubscriptionEventType.STOP_BILLING.equals(eventType)) {
+ nextProduct = null;
+ nextPlan = null;
+ nextPlanPhase = null;
+ nextPriceList = null;
+ nextBillingPeriod = null;
+ } else {
+ nextProduct = prev.getNextProduct();
+ nextPlan = prev.getNextPlan();
+ nextPlanPhase = prev.getNextPhase();
+ nextPriceList = prev.getNextPriceList();
+ nextBillingPeriod = prev.getNextBillingPeriod();
+ }
+ } else {
+ nextProduct = next.getNextProduct();
+ nextPlan = next.getNextPlan();
+ nextPlanPhase = next.getNextPhase();
+ nextPriceList = next.getNextPriceList();
+ nextBillingPeriod = next.getNextBillingPeriod();
+ }
+
return new DefaultSubscriptionEvent(in.getId(),
entitlementId,
in.getEffectiveDate(),
@@ -415,17 +467,16 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
in.isBlockBilling(),
in.getService(),
in.getStateName(),
- // We look for the next for the 'prev' meaning we we are headed to, but if this is null -- for example on cancellation we get the prev which gives the correct state.
- prev != null ? (prev.getNextProduct() != null ? prev.getNextProduct() : prev.getPrevProduct()) : null,
- prev != null ? (prev.getNextPlan() != null ? prev.getNextPlan() : prev.getPrevPlan()) : null,
- prev != null ? (prev.getNextPhase() != null ? prev.getNextPhase() : prev.getPrevPhase()) : null,
- prev != null ? (prev.getNextPriceList() != null ? prev.getNextPriceList() : prev.getPrevPriceList()) : null,
- prev != null ? (prev.getNextBillingPeriod() != null ? prev.getNextBillingPeriod() : prev.getPrevBillingPeriod()) : null,
- next != null ? next.getPrevProduct() : null,
- next != null ? next.getPrevPlan() : null,
- next != null ? next.getPrevPhase() : null,
- next != null ? next.getPrevPriceList() : null,
- next != null ? next.getPrevBillingPeriod() : null,
+ prevProduct,
+ prevPlan,
+ prevPlanPhase,
+ prevPriceList,
+ prevBillingPeriod,
+ nextProduct,
+ nextPlan,
+ nextPlanPhase,
+ nextPriceList,
+ nextBillingPeriod,
in.getCreatedDate(),
accountTimeZone);
}
@@ -487,11 +538,6 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
return ImmutableList.<SubscriptionEventType>of(SubscriptionEventType.STOP_BILLING);
case PHASE:
return ImmutableList.<SubscriptionEventType>of(SubscriptionEventType.PHASE);
- // This is the old way of pausing billing; not used any longer, but kept for compatibility reason. We return both RESUME_ENTITLEMENT and RESUME_BILLING
- // and will rely on the sanitizeForBaseRecreateEvents method to transform the STOP_BILLING (coming from CANCEL) into the correct events.
- //
- case RE_CREATE:
- return ImmutableList.<SubscriptionEventType>of(SubscriptionEventType.RESUME_ENTITLEMENT, SubscriptionEventType.RESUME_BILLING);
/*
* Those can be ignored:
*/
@@ -572,7 +618,6 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
// From the current state of that subscription, compute the effect of the new state based on the incoming blockingState event
//
private List<SubscriptionEventType> addStateAndReturnEventTypes(final BlockingState bs) {
-
// Turn off isBlockedEntitlement and isBlockedBilling if there was not start event
final BlockingState fixedBlockingState = new DefaultBlockingState(bs.getBlockedId(),
bs.getType(),
@@ -630,308 +675,4 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
return aggrBefore;
}
}
-
- protected static final class DefaultSubscriptionEvent implements SubscriptionEvent {
-
- private final UUID id;
- private final UUID entitlementId;
- private final DateTime effectiveDate;
- private final DateTime requestedDate;
- private final SubscriptionEventType eventType;
- private final boolean isBlockingEntitlement;
- private final boolean isBlockingBilling;
- private final String serviceName;
- private final String serviceStateName;
- private final Product prevProduct;
- private final Plan prevPlan;
- private final PlanPhase prevPlanPhase;
- private final PriceList prevPriceList;
- private final BillingPeriod prevBillingPeriod;
- private final Product nextProduct;
- private final Plan nextPlan;
- private final PlanPhase nextPlanPhase;
- private final PriceList nextPriceList;
- private final BillingPeriod nextBillingPeriod;
- private final DateTime createdDate;
- private final DateTimeZone accountTimeZone;
-
- public DefaultSubscriptionEvent(final UUID id,
- final UUID entitlementId,
- final DateTime effectiveDate,
- final DateTime requestedDate,
- final SubscriptionEventType eventType,
- final boolean blockingEntitlement,
- final boolean blockingBilling,
- final String serviceName,
- final String serviceStateName,
- final Product prevProduct,
- final Plan prevPlan,
- final PlanPhase prevPlanPhase,
- final PriceList prevPriceList,
- final BillingPeriod prevBillingPeriod,
- final Product nextProduct,
- final Plan nextPlan,
- final PlanPhase nextPlanPhase,
- final PriceList nextPriceList,
- final BillingPeriod nextBillingPeriod,
- final DateTime createDate,
- final DateTimeZone accountTimeZone) {
- this.id = id;
- this.entitlementId = entitlementId;
- this.effectiveDate = effectiveDate;
- this.requestedDate = requestedDate;
- this.eventType = eventType;
- this.isBlockingEntitlement = blockingEntitlement;
- this.isBlockingBilling = blockingBilling;
- this.serviceName = serviceName;
- this.serviceStateName = serviceStateName;
- this.prevProduct = prevProduct;
- this.prevPlan = prevPlan;
- this.prevPlanPhase = prevPlanPhase;
- this.prevPriceList = prevPriceList;
- this.prevBillingPeriod = prevBillingPeriod;
- this.nextProduct = nextProduct;
- this.nextPlan = nextPlan;
- this.nextPlanPhase = nextPlanPhase;
- this.nextPriceList = nextPriceList;
- this.nextBillingPeriod = nextBillingPeriod;
- this.createdDate = createDate;
- this.accountTimeZone = accountTimeZone;
- }
-
- private DefaultSubscriptionEvent(final DefaultSubscriptionEvent copy, final SubscriptionEventType newEventType) {
- this(copy.getId(),
- copy.getEntitlementId(),
- copy.getEffectiveDateTime(),
- copy.getRequestedDateTime(),
- newEventType,
- copy.isBlockedEntitlement(),
- copy.isBlockedBilling(),
- copy.getServiceName(),
- copy.getServiceStateName(),
- copy.getPrevProduct(),
- copy.getPrevPlan(),
- copy.getPrevPhase(),
- copy.getPrevPriceList(),
- copy.getPrevBillingPeriod(),
- copy.getNextProduct(),
- copy.getNextPlan(),
- copy.getNextPhase(),
- copy.getNextPriceList(),
- copy.getNextBillingPeriod(),
- copy.getCreatedDate(),
- copy.getAccountTimeZone());
- }
-
- public DateTimeZone getAccountTimeZone() {
- return accountTimeZone;
- }
-
- public DateTime getEffectiveDateTime() {
- return effectiveDate;
- }
-
- public DateTime getRequestedDateTime() {
- return requestedDate;
- }
-
- @Override
- public UUID getId() {
- return id;
- }
-
- @Override
- public UUID getEntitlementId() {
- return entitlementId;
- }
-
- @Override
- public LocalDate getEffectiveDate() {
- return effectiveDate != null ? new LocalDate(effectiveDate, accountTimeZone) : null;
- }
-
- @Override
- public LocalDate getRequestedDate() {
- return requestedDate != null ? new LocalDate(requestedDate, accountTimeZone) : null;
- }
-
- @Override
- public SubscriptionEventType getSubscriptionEventType() {
- return eventType;
- }
-
- @Override
- public boolean isBlockedBilling() {
- return isBlockingBilling;
- }
-
- @Override
- public boolean isBlockedEntitlement() {
- return isBlockingEntitlement;
- }
-
- @Override
- public String getServiceName() {
- return serviceName;
- }
-
- @Override
- public String getServiceStateName() {
- return serviceStateName;
- }
-
- @Override
- public Product getPrevProduct() {
- return prevProduct;
- }
-
- @Override
- public Plan getPrevPlan() {
- return prevPlan;
- }
-
- @Override
- public PlanPhase getPrevPhase() {
- return prevPlanPhase;
- }
-
- @Override
- public PriceList getPrevPriceList() {
- return prevPriceList;
- }
-
- @Override
- public BillingPeriod getPrevBillingPeriod() {
- return prevBillingPeriod;
- }
-
- @Override
- public Product getNextProduct() {
- return nextProduct;
- }
-
- @Override
- public Plan getNextPlan() {
- return nextPlan;
- }
-
- @Override
- public PlanPhase getNextPhase() {
- return nextPlanPhase;
- }
-
- @Override
- public PriceList getNextPriceList() {
- return nextPriceList;
- }
-
- @Override
- public BillingPeriod getNextBillingPeriod() {
- return nextBillingPeriod;
- }
-
- public DateTime getCreatedDate() {
- return createdDate;
- }
-
- @Override
- public boolean equals(final Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- final DefaultSubscriptionEvent that = (DefaultSubscriptionEvent) o;
-
- if (isBlockingBilling != that.isBlockingBilling) {
- return false;
- }
- if (isBlockingEntitlement != that.isBlockingEntitlement) {
- return false;
- }
- if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
- return false;
- }
- if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
- return false;
- }
- if (entitlementId != null ? !entitlementId.equals(that.entitlementId) : that.entitlementId != null) {
- return false;
- }
- if (eventType != that.eventType) {
- return false;
- }
- if (id != null ? !id.equals(that.id) : that.id != null) {
- return false;
- }
- if (nextBillingPeriod != that.nextBillingPeriod) {
- return false;
- }
- if (nextPlan != null ? !nextPlan.equals(that.nextPlan) : that.nextPlan != null) {
- return false;
- }
- if (nextPlanPhase != null ? !nextPlanPhase.equals(that.nextPlanPhase) : that.nextPlanPhase != null) {
- return false;
- }
- if (nextPriceList != null ? !nextPriceList.equals(that.nextPriceList) : that.nextPriceList != null) {
- return false;
- }
- if (nextProduct != null ? !nextProduct.equals(that.nextProduct) : that.nextProduct != null) {
- return false;
- }
- if (prevBillingPeriod != that.prevBillingPeriod) {
- return false;
- }
- if (prevPlan != null ? !prevPlan.equals(that.prevPlan) : that.prevPlan != null) {
- return false;
- }
- if (prevPlanPhase != null ? !prevPlanPhase.equals(that.prevPlanPhase) : that.prevPlanPhase != null) {
- return false;
- }
- if (prevPriceList != null ? !prevPriceList.equals(that.prevPriceList) : that.prevPriceList != null) {
- return false;
- }
- if (prevProduct != null ? !prevProduct.equals(that.prevProduct) : that.prevProduct != null) {
- return false;
- }
- if (requestedDate != null ? !requestedDate.equals(that.requestedDate) : that.requestedDate != null) {
- return false;
- }
- if (serviceName != null ? !serviceName.equals(that.serviceName) : that.serviceName != null) {
- return false;
- }
- if (serviceStateName != null ? !serviceStateName.equals(that.serviceStateName) : that.serviceStateName != null) {
- return false;
- }
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = id != null ? id.hashCode() : 0;
- result = 31 * result + (entitlementId != null ? entitlementId.hashCode() : 0);
- result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
- result = 31 * result + (requestedDate != null ? requestedDate.hashCode() : 0);
- result = 31 * result + (eventType != null ? eventType.hashCode() : 0);
- result = 31 * result + (isBlockingEntitlement ? 1 : 0);
- result = 31 * result + (isBlockingBilling ? 1 : 0);
- result = 31 * result + (serviceName != null ? serviceName.hashCode() : 0);
- result = 31 * result + (serviceStateName != null ? serviceStateName.hashCode() : 0);
- result = 31 * result + (prevProduct != null ? prevProduct.hashCode() : 0);
- result = 31 * result + (prevPlan != null ? prevPlan.hashCode() : 0);
- result = 31 * result + (prevPlanPhase != null ? prevPlanPhase.hashCode() : 0);
- result = 31 * result + (prevPriceList != null ? prevPriceList.hashCode() : 0);
- result = 31 * result + (prevBillingPeriod != null ? prevBillingPeriod.hashCode() : 0);
- result = 31 * result + (nextProduct != null ? nextProduct.hashCode() : 0);
- result = 31 * result + (nextPlan != null ? nextPlan.hashCode() : 0);
- result = 31 * result + (nextPlanPhase != null ? nextPlanPhase.hashCode() : 0);
- result = 31 * result + (nextPriceList != null ? nextPriceList.hashCode() : 0);
- result = 31 * result + (nextBillingPeriod != null ? nextBillingPeriod.hashCode() : 0);
- result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
- return result;
- }
- }
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionEvent.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionEvent.java
new file mode 100644
index 0000000..fbd7ab6
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionEvent.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.entitlement.api;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.catalog.api.PriceList;
+import com.ning.billing.catalog.api.Product;
+
+public class DefaultSubscriptionEvent implements SubscriptionEvent {
+
+ private final UUID id;
+ private final UUID entitlementId;
+ private final DateTime effectiveDate;
+ private final DateTime requestedDate;
+ private final SubscriptionEventType eventType;
+ private final boolean isBlockingEntitlement;
+ private final boolean isBlockingBilling;
+ private final String serviceName;
+ private final String serviceStateName;
+ private final Product prevProduct;
+ private final Plan prevPlan;
+ private final PlanPhase prevPlanPhase;
+ private final PriceList prevPriceList;
+ private final BillingPeriod prevBillingPeriod;
+ private final Product nextProduct;
+ private final Plan nextPlan;
+ private final PlanPhase nextPlanPhase;
+ private final PriceList nextPriceList;
+ private final BillingPeriod nextBillingPeriod;
+ private final DateTime createdDate;
+ private final DateTimeZone accountTimeZone;
+
+ public DefaultSubscriptionEvent(final UUID id,
+ final UUID entitlementId,
+ final DateTime effectiveDate,
+ final DateTime requestedDate,
+ final SubscriptionEventType eventType,
+ final boolean blockingEntitlement,
+ final boolean blockingBilling,
+ final String serviceName,
+ final String serviceStateName,
+ final Product prevProduct,
+ final Plan prevPlan,
+ final PlanPhase prevPlanPhase,
+ final PriceList prevPriceList,
+ final BillingPeriod prevBillingPeriod,
+ final Product nextProduct,
+ final Plan nextPlan,
+ final PlanPhase nextPlanPhase,
+ final PriceList nextPriceList,
+ final BillingPeriod nextBillingPeriod,
+ final DateTime createDate,
+ final DateTimeZone accountTimeZone) {
+ this.id = id;
+ this.entitlementId = entitlementId;
+ this.effectiveDate = effectiveDate;
+ this.requestedDate = requestedDate;
+ this.eventType = eventType;
+ this.isBlockingEntitlement = blockingEntitlement;
+ this.isBlockingBilling = blockingBilling;
+ this.serviceName = serviceName;
+ this.serviceStateName = serviceStateName;
+ this.prevProduct = prevProduct;
+ this.prevPlan = prevPlan;
+ this.prevPlanPhase = prevPlanPhase;
+ this.prevPriceList = prevPriceList;
+ this.prevBillingPeriod = prevBillingPeriod;
+ this.nextProduct = nextProduct;
+ this.nextPlan = nextPlan;
+ this.nextPlanPhase = nextPlanPhase;
+ this.nextPriceList = nextPriceList;
+ this.nextBillingPeriod = nextBillingPeriod;
+ this.createdDate = createDate;
+ this.accountTimeZone = accountTimeZone;
+ }
+
+ public DefaultSubscriptionEvent(final DefaultSubscriptionEvent copy, final SubscriptionEventType newEventType) {
+ this(copy.getId(),
+ copy.getEntitlementId(),
+ copy.getEffectiveDateTime(),
+ copy.getRequestedDateTime(),
+ newEventType,
+ copy.isBlockedEntitlement(),
+ copy.isBlockedBilling(),
+ copy.getServiceName(),
+ copy.getServiceStateName(),
+ copy.getPrevProduct(),
+ copy.getPrevPlan(),
+ copy.getPrevPhase(),
+ copy.getPrevPriceList(),
+ copy.getPrevBillingPeriod(),
+ copy.getNextProduct(),
+ copy.getNextPlan(),
+ copy.getNextPhase(),
+ copy.getNextPriceList(),
+ copy.getNextBillingPeriod(),
+ copy.getCreatedDate(),
+ copy.getAccountTimeZone());
+ }
+
+ public DateTimeZone getAccountTimeZone() {
+ return accountTimeZone;
+ }
+
+ public DateTime getEffectiveDateTime() {
+ return effectiveDate;
+ }
+
+ public DateTime getRequestedDateTime() {
+ return requestedDate;
+ }
+
+ @Override
+ public UUID getId() {
+ return id;
+ }
+
+ @Override
+ public UUID getEntitlementId() {
+ return entitlementId;
+ }
+
+ @Override
+ public LocalDate getEffectiveDate() {
+ return effectiveDate != null ? new LocalDate(effectiveDate, accountTimeZone) : null;
+ }
+
+ @Override
+ public LocalDate getRequestedDate() {
+ return requestedDate != null ? new LocalDate(requestedDate, accountTimeZone) : null;
+ }
+
+ @Override
+ public SubscriptionEventType getSubscriptionEventType() {
+ return eventType;
+ }
+
+ @Override
+ public boolean isBlockedBilling() {
+ return isBlockingBilling;
+ }
+
+ @Override
+ public boolean isBlockedEntitlement() {
+ return isBlockingEntitlement;
+ }
+
+ @Override
+ public String getServiceName() {
+ return serviceName;
+ }
+
+ @Override
+ public String getServiceStateName() {
+ return serviceStateName;
+ }
+
+ @Override
+ public Product getPrevProduct() {
+ return prevProduct;
+ }
+
+ @Override
+ public Plan getPrevPlan() {
+ return prevPlan;
+ }
+
+ @Override
+ public PlanPhase getPrevPhase() {
+ return prevPlanPhase;
+ }
+
+ @Override
+ public PriceList getPrevPriceList() {
+ return prevPriceList;
+ }
+
+ @Override
+ public BillingPeriod getPrevBillingPeriod() {
+ return prevBillingPeriod;
+ }
+
+ @Override
+ public Product getNextProduct() {
+ return nextProduct;
+ }
+
+ @Override
+ public Plan getNextPlan() {
+ return nextPlan;
+ }
+
+ @Override
+ public PlanPhase getNextPhase() {
+ return nextPlanPhase;
+ }
+
+ @Override
+ public PriceList getNextPriceList() {
+ return nextPriceList;
+ }
+
+ @Override
+ public BillingPeriod getNextBillingPeriod() {
+ return nextBillingPeriod;
+ }
+
+ public DateTime getCreatedDate() {
+ return createdDate;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final DefaultSubscriptionEvent that = (DefaultSubscriptionEvent) o;
+
+ if (isBlockingBilling != that.isBlockingBilling) {
+ return false;
+ }
+ if (isBlockingEntitlement != that.isBlockingEntitlement) {
+ return false;
+ }
+ if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
+ return false;
+ }
+ if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
+ return false;
+ }
+ if (entitlementId != null ? !entitlementId.equals(that.entitlementId) : that.entitlementId != null) {
+ return false;
+ }
+ if (eventType != that.eventType) {
+ return false;
+ }
+ if (id != null ? !id.equals(that.id) : that.id != null) {
+ return false;
+ }
+ if (nextBillingPeriod != that.nextBillingPeriod) {
+ return false;
+ }
+ if (nextPlan != null ? !nextPlan.equals(that.nextPlan) : that.nextPlan != null) {
+ return false;
+ }
+ if (nextPlanPhase != null ? !nextPlanPhase.equals(that.nextPlanPhase) : that.nextPlanPhase != null) {
+ return false;
+ }
+ if (nextPriceList != null ? !nextPriceList.equals(that.nextPriceList) : that.nextPriceList != null) {
+ return false;
+ }
+ if (nextProduct != null ? !nextProduct.equals(that.nextProduct) : that.nextProduct != null) {
+ return false;
+ }
+ if (prevBillingPeriod != that.prevBillingPeriod) {
+ return false;
+ }
+ if (prevPlan != null ? !prevPlan.equals(that.prevPlan) : that.prevPlan != null) {
+ return false;
+ }
+ if (prevPlanPhase != null ? !prevPlanPhase.equals(that.prevPlanPhase) : that.prevPlanPhase != null) {
+ return false;
+ }
+ if (prevPriceList != null ? !prevPriceList.equals(that.prevPriceList) : that.prevPriceList != null) {
+ return false;
+ }
+ if (prevProduct != null ? !prevProduct.equals(that.prevProduct) : that.prevProduct != null) {
+ return false;
+ }
+ if (requestedDate != null ? !requestedDate.equals(that.requestedDate) : that.requestedDate != null) {
+ return false;
+ }
+ if (serviceName != null ? !serviceName.equals(that.serviceName) : that.serviceName != null) {
+ return false;
+ }
+ if (serviceStateName != null ? !serviceStateName.equals(that.serviceStateName) : that.serviceStateName != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean overlaps(final DefaultSubscriptionEvent that) {
+ if (this == that) {
+ return true;
+ }
+ if (that == null || getClass() != that.getClass()) {
+ return false;
+ }
+
+ if (isBlockingBilling != that.isBlockingBilling) {
+ return false;
+ }
+ if (isBlockingEntitlement != that.isBlockingEntitlement) {
+ return false;
+ }
+ if (effectiveDate != null ? effectiveDate.compareTo(that.effectiveDate) < 0 : that.effectiveDate != null) {
+ return false;
+ }
+ if (entitlementId != null ? !entitlementId.equals(that.entitlementId) : that.entitlementId != null) {
+ return false;
+ }
+ if (eventType != that.eventType) {
+ return false;
+ }
+ if (nextBillingPeriod != that.nextBillingPeriod) {
+ return false;
+ }
+ if (nextPlan != null ? !nextPlan.equals(that.nextPlan) : that.nextPlan != null) {
+ return false;
+ }
+ if (nextPlanPhase != null ? !nextPlanPhase.equals(that.nextPlanPhase) : that.nextPlanPhase != null) {
+ return false;
+ }
+ if (nextPriceList != null ? !nextPriceList.equals(that.nextPriceList) : that.nextPriceList != null) {
+ return false;
+ }
+ if (nextProduct != null ? !nextProduct.equals(that.nextProduct) : that.nextProduct != null) {
+ return false;
+ }
+ if (prevBillingPeriod != that.prevBillingPeriod) {
+ return false;
+ }
+ if (prevPlan != null ? !prevPlan.equals(that.prevPlan) : that.prevPlan != null) {
+ return false;
+ }
+ if (prevPlanPhase != null ? !prevPlanPhase.equals(that.prevPlanPhase) : that.prevPlanPhase != null) {
+ return false;
+ }
+ if (prevPriceList != null ? !prevPriceList.equals(that.prevPriceList) : that.prevPriceList != null) {
+ return false;
+ }
+ if (prevProduct != null ? !prevProduct.equals(that.prevProduct) : that.prevProduct != null) {
+ return false;
+ }
+ if (serviceName != null ? !serviceName.equals(that.serviceName) : that.serviceName != null) {
+ return false;
+ }
+ if (serviceStateName != null ? !serviceStateName.equals(that.serviceStateName) : that.serviceStateName != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = id != null ? id.hashCode() : 0;
+ result = 31 * result + (entitlementId != null ? entitlementId.hashCode() : 0);
+ result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
+ result = 31 * result + (requestedDate != null ? requestedDate.hashCode() : 0);
+ result = 31 * result + (eventType != null ? eventType.hashCode() : 0);
+ result = 31 * result + (isBlockingEntitlement ? 1 : 0);
+ result = 31 * result + (isBlockingBilling ? 1 : 0);
+ result = 31 * result + (serviceName != null ? serviceName.hashCode() : 0);
+ result = 31 * result + (serviceStateName != null ? serviceStateName.hashCode() : 0);
+ result = 31 * result + (prevProduct != null ? prevProduct.hashCode() : 0);
+ result = 31 * result + (prevPlan != null ? prevPlan.hashCode() : 0);
+ result = 31 * result + (prevPlanPhase != null ? prevPlanPhase.hashCode() : 0);
+ result = 31 * result + (prevPriceList != null ? prevPriceList.hashCode() : 0);
+ result = 31 * result + (prevBillingPeriod != null ? prevBillingPeriod.hashCode() : 0);
+ result = 31 * result + (nextProduct != null ? nextProduct.hashCode() : 0);
+ result = 31 * result + (nextPlan != null ? nextPlan.hashCode() : 0);
+ result = 31 * result + (nextPlanPhase != null ? nextPlanPhase.hashCode() : 0);
+ result = 31 * result + (nextPriceList != null ? nextPriceList.hashCode() : 0);
+ result = 31 * result + (nextBillingPeriod != null ? nextBillingPeriod.hashCode() : 0);
+ result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
index cf93ba9..aa75652 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
@@ -44,6 +44,7 @@ import com.ning.billing.subscription.events.SubscriptionBaseEvent.EventType;
import com.ning.billing.subscription.events.user.ApiEventType;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteNoDB {
@@ -311,81 +312,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
assertEquals(events.get(2).getSubscriptionEventType(), SubscriptionEventType.PHASE);
assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
+ assertNull(events.get(0).getPrevPhase());
assertEquals(events.get(0).getNextPhase().getName(), "trial");
+ assertNull(events.get(1).getPrevPhase());
assertEquals(events.get(1).getNextPhase().getName(), "trial");
+ assertEquals(events.get(2).getPrevPhase().getName(), "trial");
assertEquals(events.get(2).getNextPhase().getName(), "phase");
- assertEquals(events.get(3).getNextPhase(), null);
- }
-
- @Test(groups = "fast")
- public void testOneEntitlementWithRecreate() throws CatalogApiException {
- clock.setDay(new LocalDate(2013, 1, 1));
-
- final DateTimeZone accountTimeZone = DateTimeZone.UTC;
- final UUID accountId = UUID.randomUUID();
- final UUID bundleId = UUID.randomUUID();
- final String externalKey = "foo";
-
- final UUID entitlementId = UUID.randomUUID();
-
- final List<SubscriptionBaseTransition> allTransitions = new ArrayList<SubscriptionBaseTransition>();
-
- final DateTime requestedDate = new DateTime();
- DateTime effectiveDate = new DateTime(2013, 1, 1, 15, 43, 25, 0, DateTimeZone.UTC);
- final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
- allTransitions.add(tr1);
-
- effectiveDate = effectiveDate.plusDays(30);
- clock.addDays(30);
- final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
- allTransitions.add(tr2);
-
- effectiveDate = effectiveDate.plusDays(15);
- clock.addDays(15);
- final SubscriptionBaseTransition tr3 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CANCEL, requestedDate, effectiveDate, clock.getUTCNow(), "phase", null);
- allTransitions.add(tr3);
-
- effectiveDate = effectiveDate.plusDays(15);
- clock.addDays(15);
- final SubscriptionBaseTransition tr4 = createTransition(entitlementId, EventType.API_USER, ApiEventType.RE_CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "phase");
- allTransitions.add(tr4);
-
- final List<Entitlement> entitlements = new ArrayList<Entitlement>();
- final Entitlement entitlement = createEntitlement(entitlementId, allTransitions);
- entitlements.add(entitlement);
-
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, Collections.<BlockingState>emptyList());
-
- assertEquals(timeline.getAccountId(), accountId);
- assertEquals(timeline.getBundleId(), bundleId);
- assertEquals(timeline.getExternalKey(), externalKey);
-
- final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
- assertEquals(events.size(), 7);
-
- assertEquals(events.get(0).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
- assertEquals(events.get(1).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
- assertEquals(events.get(2).getEffectiveDate().compareTo(new LocalDate(tr2.getEffectiveTransitionTime(), accountTimeZone)), 0);
- assertEquals(events.get(3).getEffectiveDate().compareTo(new LocalDate(tr3.getEffectiveTransitionTime(), accountTimeZone)), 0);
- assertEquals(events.get(4).getEffectiveDate().compareTo(new LocalDate(tr3.getEffectiveTransitionTime(), accountTimeZone)), 0);
- assertEquals(events.get(5).getEffectiveDate().compareTo(new LocalDate(tr4.getEffectiveTransitionTime(), accountTimeZone)), 0);
- assertEquals(events.get(6).getEffectiveDate().compareTo(new LocalDate(tr4.getEffectiveTransitionTime(), accountTimeZone)), 0);
-
- assertEquals(events.get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
- assertEquals(events.get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
- assertEquals(events.get(2).getSubscriptionEventType(), SubscriptionEventType.PHASE);
- assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.PAUSE_ENTITLEMENT);
- assertEquals(events.get(4).getSubscriptionEventType(), SubscriptionEventType.PAUSE_BILLING);
- assertEquals(events.get(5).getSubscriptionEventType(), SubscriptionEventType.RESUME_ENTITLEMENT);
- assertEquals(events.get(6).getSubscriptionEventType(), SubscriptionEventType.RESUME_BILLING);
-
- assertEquals(events.get(0).getNextPhase().getName(), "trial");
- assertEquals(events.get(1).getNextPhase().getName(), "trial");
- assertEquals(events.get(2).getNextPhase().getName(), "phase");
- assertEquals(events.get(3).getNextPhase(), null);
- assertEquals(events.get(4).getNextPhase(), null);
- assertEquals(events.get(5).getNextPhase().getName(), "phase");
- assertEquals(events.get(6).getNextPhase().getName(), "phase");
+ assertEquals(events.get(3).getPrevPhase().getName(), "phase");
+ assertNull(events.get(3).getNextPhase());
}
@Test(groups = "fast")
@@ -455,11 +389,16 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.SERVICE_STATE_CHANGE);
assertEquals(events.get(4).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
+ assertNull(events.get(0).getPrevPhase());
assertEquals(events.get(0).getNextPhase().getName(), "trial");
+ assertNull(events.get(1).getPrevPhase());
assertEquals(events.get(1).getNextPhase().getName(), "trial");
+ assertEquals(events.get(2).getPrevPhase().getName(), "trial");
assertEquals(events.get(2).getNextPhase().getName(), "phase");
+ assertEquals(events.get(3).getPrevPhase().getName(), "phase");
assertEquals(events.get(3).getNextPhase().getName(), "phase");
- assertEquals(events.get(4).getNextPhase(), null);
+ assertEquals(events.get(4).getPrevPhase().getName(), "phase");
+ assertNull(events.get(4).getNextPhase());
}
@Test(groups = "fast")
@@ -526,18 +465,21 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
assertEquals(events.get(4).getSubscriptionEventType(), SubscriptionEventType.STOP_ENTITLEMENT);
assertEquals(events.get(5).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
- assertEquals(events.get(0).getPrevPhase(), null);
+ assertNull(events.get(0).getPrevPhase());
assertEquals(events.get(0).getNextPhase().getName(), "trial");
- assertEquals(events.get(1).getPrevPhase(), null);
+ assertNull(events.get(1).getPrevPhase());
assertEquals(events.get(1).getNextPhase().getName(), "trial");
+
assertEquals(events.get(2).getPrevPhase().getName(), "trial");
assertEquals(events.get(2).getNextPhase().getName(), "phase");
+
assertEquals(events.get(3).getPrevPhase().getName(), "phase");
assertEquals(events.get(3).getNextPhase().getName(), "phase");
+
assertEquals(events.get(4).getPrevPhase().getName(), "phase");
- assertEquals(events.get(4).getNextPhase(), null);
+ assertNull(events.get(4).getNextPhase());
assertEquals(events.get(5).getPrevPhase().getName(), "phase");
- assertEquals(events.get(5).getNextPhase(), null);
+ assertNull(events.get(5).getNextPhase());
}
@Test(groups = "fast")
@@ -626,13 +568,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
assertEquals(events.get(7).getSubscriptionEventType(), SubscriptionEventType.STOP_ENTITLEMENT);
assertEquals(events.get(8).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
- assertEquals(events.get(0).getPrevPhase(), null);
+ assertNull(events.get(0).getPrevPhase());
assertEquals(events.get(0).getNextPhase().getName(), "trial1");
- assertEquals(events.get(1).getPrevPhase(), null);
+ assertNull(events.get(1).getPrevPhase());
assertEquals(events.get(1).getNextPhase().getName(), "trial1");
- assertEquals(events.get(2).getPrevPhase(), null);
+
+ assertNull(events.get(2).getPrevPhase());
assertEquals(events.get(2).getNextPhase().getName(), "phase2");
- assertEquals(events.get(3).getPrevPhase(), null);
+ assertNull(events.get(3).getPrevPhase());
assertEquals(events.get(3).getNextPhase().getName(), "phase2");
assertEquals(events.get(4).getPrevPhase().getName(), "trial1");
@@ -642,17 +585,17 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
assertEquals(events.get(5).getNextPhase().getName(), "phase1");
assertEquals(events.get(6).getPrevPhase().getName(), "phase2");
- assertEquals(events.get(6).getNextPhase(), null);
+ assertEquals(events.get(6).getNextPhase().getName(), "phase2");
assertEquals(events.get(7).getPrevPhase().getName(), "phase1");
- assertEquals(events.get(7).getNextPhase(), null);
+ assertNull(events.get(7).getNextPhase());
assertEquals(events.get(8).getPrevPhase().getName(), "phase1");
- assertEquals(events.get(8).getNextPhase(), null);
+ assertNull(events.get(8).getNextPhase());
}
@Test(groups = "fast")
- public void testWithOverdueOfflineAndClear() throws CatalogApiException {
+ public void testWithOverdueOffline() throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -676,21 +619,11 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
allTransitions.add(tr2);
- effectiveDate = effectiveDate.plusDays(6); // 2013-02-06
- clock.addDays(6);
+ effectiveDate = effectiveDate.plusDays(40); // 2013-03-12
+ clock.addDays(40);
final SubscriptionBaseTransition tr3 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CANCEL, requestedDate, effectiveDate, clock.getUTCNow(), "phase", null);
allTransitions.add(tr3);
- effectiveDate = effectiveDate.plusDays(22);// 2013-02-28
- clock.addDays(22);
- final SubscriptionBaseTransition tr4 = createTransition(entitlementId, EventType.API_USER, ApiEventType.RE_CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "phase");
- allTransitions.add(tr4);
-
- effectiveDate = effectiveDate.plusDays(12); // 2013-03-12
- clock.addDays(12);
- final SubscriptionBaseTransition tr5 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CANCEL, requestedDate, effectiveDate, clock.getUTCNow(), "phase", null);
- allTransitions.add(tr5);
-
final BlockingState bs1 = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.ACCOUNT,
"OFFLINE", "overdue-service",
true, true, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
@@ -703,15 +636,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
blockingStates.add(bs2);
- effectiveDate = effectiveDate.plusDays(12); // 2013-03-24
- clock.addDays(12);
-
- final BlockingState bs3 = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.ACCOUNT,
- "__KILLBILL__CLEAR__OVERDUE__STATE__", "overdue-service",
- false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
-
- blockingStates.add(bs3);
-
final List<Entitlement> entitlements = new ArrayList<Entitlement>();
final Entitlement entitlement = createEntitlement(entitlementId, allTransitions);
entitlements.add(entitlement);
@@ -723,43 +647,117 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
assertEquals(timeline.getExternalKey(), externalKey);
final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
- assertEquals(events.size(), 11);
+ assertEquals(events.size(), 6);
assertEquals(events.get(0).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
assertEquals(events.get(1).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
assertEquals(events.get(2).getEffectiveDate().compareTo(new LocalDate(tr2.getEffectiveTransitionTime(), accountTimeZone)), 0);
assertEquals(events.get(3).getEffectiveDate().compareTo(new LocalDate(tr3.getEffectiveTransitionTime(), accountTimeZone)), 0);
assertEquals(events.get(4).getEffectiveDate().compareTo(new LocalDate(tr3.getEffectiveTransitionTime(), accountTimeZone)), 0);
- assertEquals(events.get(5).getEffectiveDate().compareTo(new LocalDate(tr4.getEffectiveTransitionTime(), accountTimeZone)), 0);
- assertEquals(events.get(6).getEffectiveDate().compareTo(new LocalDate(tr4.getEffectiveTransitionTime(), accountTimeZone)), 0);
- assertEquals(events.get(7).getEffectiveDate().compareTo(new LocalDate(tr5.getEffectiveTransitionTime(), accountTimeZone)), 0);
- assertEquals(events.get(8).getEffectiveDate().compareTo(new LocalDate(tr5.getEffectiveTransitionTime(), accountTimeZone)), 0);
- assertEquals(events.get(9).getEffectiveDate().compareTo(new LocalDate(tr5.getEffectiveTransitionTime(), accountTimeZone)), 0);
- assertEquals(events.get(10).getEffectiveDate().compareTo(new LocalDate(bs3.getEffectiveDate(), accountTimeZone)), 0);
+ assertEquals(events.get(5).getEffectiveDate().compareTo(new LocalDate(bs1.getEffectiveDate(), accountTimeZone)), 0);
assertEquals(events.get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
assertEquals(events.get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
assertEquals(events.get(2).getSubscriptionEventType(), SubscriptionEventType.PHASE);
- assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.PAUSE_ENTITLEMENT);
- assertEquals(events.get(4).getSubscriptionEventType(), SubscriptionEventType.PAUSE_BILLING);
- assertEquals(events.get(5).getSubscriptionEventType(), SubscriptionEventType.RESUME_ENTITLEMENT);
- assertEquals(events.get(6).getSubscriptionEventType(), SubscriptionEventType.RESUME_BILLING);
- assertEquals(events.get(7).getSubscriptionEventType(), SubscriptionEventType.STOP_ENTITLEMENT);
- assertEquals(events.get(8).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
- assertEquals(events.get(9).getSubscriptionEventType(), SubscriptionEventType.SERVICE_STATE_CHANGE);
- assertEquals(events.get(10).getSubscriptionEventType(), SubscriptionEventType.SERVICE_STATE_CHANGE);
+ assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.STOP_ENTITLEMENT);
+ assertEquals(events.get(4).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
+ assertEquals(events.get(5).getSubscriptionEventType(), SubscriptionEventType.SERVICE_STATE_CHANGE);
+ assertNull(events.get(0).getPrevPhase());
assertEquals(events.get(0).getNextPhase().getName(), "trial");
+ assertNull(events.get(1).getPrevPhase());
assertEquals(events.get(1).getNextPhase().getName(), "trial");
+
+ assertEquals(events.get(2).getPrevPhase().getName(), "trial");
assertEquals(events.get(2).getNextPhase().getName(), "phase");
- assertEquals(events.get(3).getNextPhase(), null);
- assertEquals(events.get(4).getNextPhase(), null);
- assertEquals(events.get(5).getNextPhase().getName(), "phase");
- assertEquals(events.get(6).getNextPhase().getName(), "phase");
- assertEquals(events.get(7).getNextPhase(), null);
- assertEquals(events.get(8).getNextPhase(), null);
- assertEquals(events.get(9).getNextPhase(), null);
- assertEquals(events.get(10).getNextPhase(), null);
+
+ assertEquals(events.get(3).getPrevPhase().getName(), "phase");
+ assertNull(events.get(3).getNextPhase());
+ assertEquals(events.get(4).getPrevPhase().getName(), "phase");
+ assertNull(events.get(4).getNextPhase());
+
+ assertEquals(events.get(5).getPrevPhase().getName(), "phase");
+ assertNull(events.get(5).getNextPhase());
+ }
+
+ @Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/134")
+ public void testRemoveOverlappingBlockingStates() throws CatalogApiException {
+ clock.setDay(new LocalDate(2013, 1, 1));
+
+ final DateTimeZone accountTimeZone = DateTimeZone.UTC;
+ final UUID accountId = UUID.randomUUID();
+ final UUID bundleId = UUID.randomUUID();
+ final String externalKey = "foo";
+
+ final UUID entitlementId = UUID.randomUUID();
+
+ final List<SubscriptionBaseTransition> allTransitions = new ArrayList<SubscriptionBaseTransition>();
+ final List<BlockingState> blockingStates = new ArrayList<BlockingState>();
+
+ final DateTime requestedDate = new DateTime();
+ DateTime effectiveDate = new DateTime(2013, 1, 1, 15, 43, 25, 0, DateTimeZone.UTC);
+ final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
+ allTransitions.add(tr1);
+
+ // Overlapping ENT_STATE_BLOCKED - should merge
+ effectiveDate = effectiveDate.plusDays(5);
+ clock.addDays(5);
+ final BlockingState bs1 = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_BLOCKED, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ true, true, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+ blockingStates.add(bs1);
+
+ effectiveDate = effectiveDate.plusDays(1);
+ clock.addDays(1);
+ final BlockingState bs2 = new DefaultBlockingState(UUID.randomUUID(), bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE,
+ DefaultEntitlementApi.ENT_STATE_BLOCKED, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ true, true, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+
+ blockingStates.add(bs2);
+
+ // Overlapping ENT_STATE_CANCELLED - should merge
+ effectiveDate = effectiveDate.plusDays(1);
+ clock.addDays(1);
+ final BlockingState bs3 = new DefaultBlockingState(UUID.randomUUID(), accountId, BlockingStateType.ACCOUNT,
+ DefaultEntitlementApi.ENT_STATE_CANCELLED, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ true, true, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+
+ blockingStates.add(bs3);
+ final BlockingState bs4 = new DefaultBlockingState(UUID.randomUUID(), bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE,
+ DefaultEntitlementApi.ENT_STATE_CANCELLED, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ true, true, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+
+ blockingStates.add(bs4);
+
+ final List<Entitlement> entitlements = new ArrayList<Entitlement>();
+ final Entitlement entitlement = createEntitlement(entitlementId, allTransitions);
+ entitlements.add(entitlement);
+
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, blockingStates);
+
+ final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
+ assertEquals(events.size(), 4);
+
+ assertEquals(events.get(0).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
+ assertEquals(events.get(1).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
+ assertEquals(events.get(2).getEffectiveDate().compareTo(new LocalDate(bs1.getEffectiveDate(), accountTimeZone)), 0);
+ assertEquals(events.get(3).getEffectiveDate().compareTo(new LocalDate(bs3.getEffectiveDate(), accountTimeZone)), 0);
+
+ assertEquals(events.get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
+ assertEquals(events.get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
+ assertEquals(events.get(2).getSubscriptionEventType(), SubscriptionEventType.PAUSE_ENTITLEMENT);
+ assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.STOP_ENTITLEMENT);
+
+ assertNull(events.get(0).getPrevPhase());
+ assertEquals(events.get(0).getNextPhase().getName(), "trial");
+ assertNull(events.get(1).getPrevPhase());
+ assertEquals(events.get(1).getNextPhase().getName(), "trial");
+
+ assertEquals(events.get(2).getPrevPhase().getName(), "trial");
+ assertEquals(events.get(2).getNextPhase().getName(), "trial");
+
+ assertEquals(events.get(3).getPrevPhase().getName(), "trial");
+ assertNull(events.get(3).getNextPhase());
}
private Entitlement createEntitlement(final UUID entitlementId, final List<SubscriptionBaseTransition> allTransitions) {
@@ -781,25 +779,53 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final String prevPhaseName,
final String nextPhaseName
) throws CatalogApiException {
-
- final PlanPhase prevPhase = prevPhaseName != null ? Mockito.mock(PlanPhase.class) : null;
- if (prevPhase != null) {
+ final PlanPhase prevPhase;
+ final Plan prevPlan;
+ final Product prevProduct;
+ final PriceList prevPriceList;
+ if (prevPhaseName == null) {
+ prevPhase = null;
+ prevPlan = null;
+ prevProduct = null;
+ prevPriceList = null;
+ } else {
+ prevPhase = Mockito.mock(PlanPhase.class);
Mockito.when(prevPhase.getName()).thenReturn(prevPhaseName);
+
+ prevProduct = Mockito.mock(Product.class);
+ Mockito.when(prevProduct.getName()).thenReturn("product");
+
+ prevPlan = Mockito.mock(Plan.class);
+ Mockito.when(prevPlan.getName()).thenReturn("plan");
+ Mockito.when(prevPlan.getProduct()).thenReturn(prevProduct);
+
+ prevPriceList = Mockito.mock(PriceList.class);
+ Mockito.when(prevPriceList.getName()).thenReturn("pricelist");
}
- final PlanPhase nextPhase = nextPhaseName != null ? Mockito.mock(PlanPhase.class) : null;
- if (nextPhase != null) {
+ final PlanPhase nextPhase;
+ final Plan nextPlan;
+ final Product nextProduct;
+ final PriceList nextPriceList;
+ if (nextPhaseName == null) {
+ nextPhase = null;
+ nextPlan = null;
+ nextProduct = null;
+ nextPriceList = null;
+ } else {
+ nextPhase = Mockito.mock(PlanPhase.class);
Mockito.when(nextPhase.getName()).thenReturn(nextPhaseName);
- }
- final Plan plan = Mockito.mock(Plan.class);
- Mockito.when(plan.getName()).thenReturn("plan");
+ nextProduct = Mockito.mock(Product.class);
+ Mockito.when(nextProduct.getName()).thenReturn("product");
- final Product product = Mockito.mock(Product.class);
- Mockito.when(product.getName()).thenReturn("product");
+ nextPlan = Mockito.mock(Plan.class);
+ Mockito.when(nextPlan.getName()).thenReturn("plan");
+ Mockito.when(nextPlan.getProduct()).thenReturn(nextProduct);
- final PriceList priceList = Mockito.mock(PriceList.class);
- Mockito.when(priceList.getName()).thenReturn("pricelist");
+ nextPriceList = Mockito.mock(PriceList.class);
+ Mockito.when(nextPriceList.getName()).thenReturn("pricelist");
+ }
return new SubscriptionBaseTransitionData(UUID.randomUUID(),
entitlementId,
@@ -811,15 +837,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
null,
null,
null,
- plan,
+ prevPlan,
prevPhase,
- priceList,
+ prevPriceList,
null,
null,
null,
- plan,
+ nextPlan,
nextPhase,
- priceList,
+ nextPriceList,
1L,
createdDate,
UUID.randomUUID(),