killbill-aplcache
Changes
entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java 549(+549 -0)
Details
diff --git a/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBaseTransitionType.java b/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBaseTransitionType.java
index 1c907e4..4fb318e 100644
--- a/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBaseTransitionType.java
+++ b/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBaseTransitionType.java
@@ -57,11 +57,11 @@ public enum SubscriptionBaseTransitionType {
*/
PHASE,
/**
- * Generated by the system to mark the start of blocked billing overdue state
+ * Generated by the system to mark the start of blocked billing overdue state. This is not on disk but computed by junction to create the billing events.
*/
START_BILLING_DISABLED,
/**
- * Generated by the system to mark the end of blocked billing overdue state
+ * Generated by the system to mark the end of blocked billing overdue state. This is not on disk but computed by junction to create the billing events.
*/
END_BILLING_DISABLED
}
diff --git a/api/src/main/java/com/ning/billing/subscription/api/user/SubscriptionBaseTransition.java b/api/src/main/java/com/ning/billing/subscription/api/user/SubscriptionBaseTransition.java
index 01acb68..58c5596 100644
--- a/api/src/main/java/com/ning/billing/subscription/api/user/SubscriptionBaseTransition.java
+++ b/api/src/main/java/com/ning/billing/subscription/api/user/SubscriptionBaseTransition.java
@@ -28,6 +28,8 @@ import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
public interface SubscriptionBaseTransition {
+ public UUID getId();
+
public UUID getSubscriptionId();
public UUID getBundleId();
@@ -61,4 +63,6 @@ public interface SubscriptionBaseTransition {
public DateTime getEffectiveTransitionTime();
public SubscriptionBaseTransitionType getTransitionType();
+
+ public DateTime getCreatedDate();
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
index 51776f1..3167484 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
@@ -1,15 +1,18 @@
package com.ning.billing.entitlement.api;
import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Collection;
import java.util.List;
-import java.util.Map;
import java.util.UUID;
+import javax.annotation.Nullable;
import javax.inject.Inject;
+import org.joda.time.DateTimeZone;
+
import com.ning.billing.ErrorCode;
import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.clock.Clock;
import com.ning.billing.entitlement.block.BlockingChecker;
import com.ning.billing.entitlement.dao.BlockingStateDao;
@@ -23,9 +26,10 @@ import com.ning.billing.util.svcapi.subscription.SubscriptionBaseInternalApi;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Multimaps;
public class DefaultSubscriptionApi implements SubscriptionApi {
@@ -36,11 +40,13 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
private final EntitlementDateHelper dateHelper;
private final Clock clock;
private final InternalCallContextFactory internalCallContextFactory;
+ private final AccountInternalApi accountApi;
@Inject
public DefaultSubscriptionApi(final SubscriptionBaseInternalApi subscriptionInternalApi, final EntitlementApi entitlementApi, final BlockingChecker checker, final BlockingStateDao blockingStateDao, final AccountInternalApi accountApi, final Clock clock, final InternalCallContextFactory internalCallContextFactory) {
this.subscriptionInternalApi = subscriptionInternalApi;
this.entitlementApi = entitlementApi;
+ this.accountApi = accountApi;
this.checker = checker;
this.blockingStateDao = blockingStateDao;
this.dateHelper = new EntitlementDateHelper(accountApi, clock);;
@@ -68,11 +74,13 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
if (entitlements.isEmpty()) {
throw new SubscriptionApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_ID, bundleId);
}
- return getSubscriptionBundleFromEntitlement(bundleId, entitlements, context);
+ return getSubscriptionBundleFromEntitlements(bundleId, entitlements, context);
} catch (EntitlementApiException e) {
throw new SubscriptionApiException(e);
} catch (SubscriptionBaseApiException e) {
throw new SubscriptionApiException(e);
+ } catch (AccountApiException e) {
+ throw new SubscriptionApiException(e);
}
}
@@ -86,11 +94,13 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
if (entitlements.isEmpty()) {
throw new SubscriptionApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_KEY, externalKey);
}
- return getSubscriptionBundleFromEntitlement(entitlements.get(0).getBundleId(), entitlements, context);
+ return getSubscriptionBundleFromEntitlements(entitlements.get(0).getBundleId(), entitlements, context);
} catch (EntitlementApiException e) {
throw new SubscriptionApiException(e);
} catch (SubscriptionBaseApiException e) {
throw new SubscriptionApiException(e);
+ } catch (AccountApiException e) {
+ throw new SubscriptionApiException(e);
}
}
@@ -125,7 +135,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>(perBundleEntitlements.keySet().size());
for (UUID bundleId : perBundleEntitlements.keySet()) {
final List<Entitlement> e = perBundleEntitlements.get(bundleId);
- final SubscriptionBundle b = getSubscriptionBundleFromEntitlement(bundleId, e, context);
+ final SubscriptionBundle b = getSubscriptionBundleFromEntitlements(bundleId, e, context);
result.add(b);
}
return result;
@@ -133,6 +143,8 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
throw new SubscriptionApiException(e);
} catch (SubscriptionBaseApiException e) {
throw new SubscriptionApiException(e);
+ } catch (AccountApiException e) {
+ throw new SubscriptionApiException(e);
}
}
@@ -143,7 +155,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
return result;
}
- private SubscriptionBundle getSubscriptionBundleFromEntitlement(final UUID bundleId, final List<Entitlement> entitlements, final TenantContext context) throws SubscriptionBaseApiException {
+ private SubscriptionBundle getSubscriptionBundleFromEntitlements(final UUID bundleId, final List<Entitlement> entitlements, final TenantContext context) throws SubscriptionBaseApiException, AccountApiException {
final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(context);
final SubscriptionBaseBundle baseBundle = subscriptionInternalApi.getBundleFromId(bundleId, internalTenantContext);
final List<Subscription> subscriptions = new ArrayList<Subscription>();
@@ -153,8 +165,19 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
return fromEntitlement(input, internalTenantContext);
}
}));
- // STEPH_ENT account timeline
- final DefaultSubscriptionBundle bundle = new DefaultSubscriptionBundle(bundleId, baseBundle.getAccountId(), baseBundle.getExternalKey(), subscriptions, null, baseBundle.getCreatedDate(), baseBundle.getUpdatedDate());
+
+ final Account account = accountApi.getAccountById(baseBundle.getAccountId(), internalTenantContext);
+ final DateTimeZone accountTimeZone = account.getTimeZone();
+
+ final List<BlockingState> allBlockingStates = new ArrayList<BlockingState>();
+ allBlockingStates.addAll(blockingStateDao.getBlockingAll(account.getId(), internalTenantContext));
+ allBlockingStates.addAll(blockingStateDao.getBlockingAll(bundleId, internalTenantContext));
+ for (Entitlement cur : entitlements) {
+ allBlockingStates.addAll(blockingStateDao.getBlockingAll(cur.getId(), internalTenantContext));
+ }
+
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, account.getId(), bundleId, baseBundle.getExternalKey(), entitlements, allBlockingStates);
+ final DefaultSubscriptionBundle bundle = new DefaultSubscriptionBundle(bundleId, baseBundle.getAccountId(), baseBundle.getExternalKey(), subscriptions, timeline, baseBundle.getCreatedDate(), baseBundle.getUpdatedDate());
return bundle;
}
}
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
new file mode 100644
index 0000000..f78a0ef
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
@@ -0,0 +1,549 @@
+package com.ning.billing.entitlement.api;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+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 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;
+import com.ning.billing.entitlement.DefaultEntitlementService;
+import com.ning.billing.subscription.api.SubscriptionBase;
+import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
+import com.ning.billing.subscription.api.user.SubscriptionBaseTransition;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+
+public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTimeline {
+
+ private final List<SubscriptionEvent> events;
+ private final UUID accountId;
+ private final UUID bundleId;
+ private final String externalKey;
+
+
+ public DefaultSubscriptionBundleTimeline(final DateTimeZone accountTimeZone, final UUID accountId, final UUID bundleId, final String externalKey, final List<Entitlement> entitlements, List<BlockingState> allBlockingStates) {
+ this.accountId = accountId;
+ this.bundleId = bundleId;
+ this.externalKey = externalKey;
+ this.events = computeEvents(entitlements, allBlockingStates, accountTimeZone);
+ }
+
+ //
+ // Compute all events based on blocking states events and base subscription events
+ // Note that:
+ // - base subscription events are already ordered for each Entitlement and so when we reorder at the bundle level we try not to break that initial ordering
+ // - 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 List<Entitlement> entitlements, 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
+ public UUID apply(final Entitlement input) {
+ return input.getId();
+ }
+ }));
+
+ // Compute base events across all entitlements
+ final LinkedList<SubscriptionEvent> result = computeSubscriptionBaseEvents(entitlements, accountTimeZone);
+
+ // Order allBlockingStates events by effectiveDate, createdDate, uuid, service, serviceState
+ Collections.sort(allBlockingStates, new Comparator<BlockingState>() {
+ @Override
+ public int compare(final BlockingState o1, final BlockingState o2) {
+ final int effectivedComp = o1.getEffectiveDate().compareTo(o2.getEffectiveDate());
+ if (effectivedComp != 0) {
+ return effectivedComp;
+ }
+ final int createdDateComp = o1.getCreatedDate().compareTo(o2.getCreatedDate());
+ if (createdDateComp != 0) {
+ return createdDateComp;
+ }
+ final int uuidComp = o1.getId().compareTo(o2.getId());
+ if (uuidComp != 0) {
+ return uuidComp;
+ }
+ // Same effectiveDate, createdDate and for the same object, we sort first by serviceName and then serviceState
+ final int serviceNameComp = o1.getService().compareTo(o2.getService());
+ if (serviceNameComp != 0) {
+ return serviceNameComp;
+ }
+ final int serviceStateComp = o1.getStateName().compareTo(o2.getStateName());
+ if (serviceStateComp != 0) {
+ return serviceStateComp;
+ }
+ // Underministic-- not sure that will ever happen.
+ return 0;
+ }
+ });
+
+ for (BlockingState bs : allBlockingStates) {
+ final LocalDate bsEffectiveDate = new LocalDate(bs.getEffectiveDate(), accountTimeZone);
+
+ // In the beginning there was nothing...
+ final Map<UUID, Boolean> isBlockedBillingMap = new HashMap<UUID, Boolean>();
+ final Map<UUID, Boolean> isBlockedEntitlementMap = new HashMap<UUID, Boolean>();
+ for (UUID uuid : allEntitlementUUIDs) {
+ isBlockedBillingMap.put(uuid, Boolean.TRUE);
+ isBlockedEntitlementMap.put(uuid, Boolean.TRUE);
+ }
+
+ final List<SubscriptionEvent> newEvents = new ArrayList<SubscriptionEvent>();
+ int index = insertFromBlockingEvent(accountTimeZone, allEntitlementUUIDs, result, bs, bsEffectiveDate, isBlockedBillingMap, isBlockedEntitlementMap, newEvents);
+ result.addAll(index, newEvents);
+ }
+ return result;
+ }
+
+ private int insertFromBlockingEvent(final DateTimeZone accountTimeZone, final Set<UUID> allEntitlementUUIDs, final LinkedList<SubscriptionEvent> result, final BlockingState bs, final LocalDate bsEffectiveDate, final Map<UUID, Boolean> blockedBillingMap, final Map<UUID, Boolean> blockedEntitlementMap, final List<SubscriptionEvent> newEvents) {
+ int index = -1;
+ final Iterator<SubscriptionEvent> it = result.iterator();
+ while (it.hasNext()) {
+ final DefaultSubscriptionEvent cur = (DefaultSubscriptionEvent) it.next();
+ index++;
+
+ switch (cur.getSubscriptionEventType()) {
+ case START_ENTITLEMENT:
+ blockedEntitlementMap.put(cur.getEntitlementId(), Boolean.FALSE);
+ break;
+ case START_BILLING:
+ blockedBillingMap.put(cur.getEntitlementId(), Boolean.FALSE);
+ break;
+ case PAUSE_ENTITLEMENT:
+ case STOP_ENTITLEMENT:
+ blockedEntitlementMap.put(cur.getEntitlementId(), Boolean.TRUE);
+ break;
+ case PAUSE_BILLING:
+ case STOP_BILLING:
+ blockedBillingMap.put(cur.getEntitlementId(), Boolean.TRUE);
+ break;
+ }
+
+ final int compEffectiveDate = bsEffectiveDate.compareTo(cur.getEffectiveDate());
+ if (compEffectiveDate < 0 ||
+ (compEffectiveDate == 0 && bs.getCreatedDate().compareTo(cur.getCreatedDate()) <= 0)) {
+ continue;
+ }
+
+ final DefaultSubscriptionEvent next = it.hasNext() ? (DefaultSubscriptionEvent) it.next() : null;
+
+ final List<UUID> targetEntitlementIds = bs.getType() == BlockingStateType.SUBSCRIPTION ? ImmutableList.<UUID>of(bs.getId()) :
+ ImmutableList.<UUID>copyOf(allEntitlementUUIDs);
+ for (UUID target : targetEntitlementIds) {
+
+ final Boolean isResumeEntitlement = (blockedEntitlementMap.get(bs.getId()) && !bs.isBlockEntitlement());
+ final Boolean isPauseEntitlement = (!blockedEntitlementMap.get(bs.getId()) && bs.isBlockEntitlement());
+ final Boolean isResumeBilling = (blockedBillingMap.get(bs.getId()) && !bs.isBlockBilling());
+ final Boolean isPauseBilling = (!blockedBillingMap.get(bs.getId()) && bs.isBlockBilling());
+ final Boolean isServiceStateChange = !(isResumeEntitlement || isPauseEntitlement || isResumeBilling || isPauseBilling);
+
+ if (isResumeEntitlement) {
+ newEvents.add(toSubscriptionEvent(cur, next, target, bs, SubscriptionEventType.RESUME_ENTITLEMENT, accountTimeZone));
+ } else if (isPauseEntitlement) {
+ newEvents.add(toSubscriptionEvent(cur, next, target, bs, SubscriptionEventType.PAUSE_ENTITLEMENT, accountTimeZone));
+ }
+ if (isResumeBilling) {
+ newEvents.add(toSubscriptionEvent(cur, next, target, bs, SubscriptionEventType.RESUME_BILLING, accountTimeZone));
+ } else if (isPauseBilling) {
+ newEvents.add(toSubscriptionEvent(cur, next, target, bs, SubscriptionEventType.PAUSE_BILLING, accountTimeZone));
+ }
+ if (isServiceStateChange) {
+ newEvents.add(toSubscriptionEvent(cur, next, target, bs, SubscriptionEventType.SERVICE_STATE_CHANGE, accountTimeZone));
+ }
+ }
+ break;
+ }
+ return index;
+ }
+
+
+ private LinkedList<SubscriptionEvent> computeSubscriptionBaseEvents(final List<Entitlement> entitlements, final DateTimeZone accountTimeZone) {
+
+ final LinkedList<SubscriptionEvent> result = new LinkedList<SubscriptionEvent>();
+ for (Entitlement cur : entitlements) {
+ final SubscriptionBase base = ((DefaultEntitlement) cur).getSubscriptionBase();
+ final List<SubscriptionBaseTransition> baseTransitions = base.getAllTransitions();
+ for (SubscriptionBaseTransition tr : baseTransitions) {
+ final SubscriptionEventType eventType = toEventType(tr.getTransitionType());
+ if (eventType == null) {
+ continue;
+ }
+ final SubscriptionEvent event = toSubscriptionEvent(tr, eventType, accountTimeZone);
+ insertSubscriptionEvent(event, result);
+ if (tr.getTransitionType() == SubscriptionBaseTransitionType.CREATE ||
+ tr.getTransitionType() == SubscriptionBaseTransitionType.TRANSFER) {
+ final SubscriptionEvent billingEvent = toSubscriptionEvent(tr, SubscriptionEventType.START_BILLING, accountTimeZone);
+ insertSubscriptionEvent(event, result);
+ }
+ }
+ }
+ sanitizeForBaseRecreateEvents(result);
+ return result;
+ }
+
+
+ //
+ // Old version of code would use CANCEL/ RE_CREATE to simulate PAUSE_BILLING/RESUME_BILLING
+ //
+ private void sanitizeForBaseRecreateEvents(final LinkedList<SubscriptionEvent> input) {
+
+ final Set<UUID> guiltyEntitlementIds = new TreeSet<UUID>();
+ ListIterator<SubscriptionEvent> it = input.listIterator(input.size() - 1);
+ while (it.hasPrevious()) {
+ final SubscriptionEvent cur = it.previous();
+ if (cur.getSubscriptionEventType() == SubscriptionEventType.RESUME_BILLING) {
+ guiltyEntitlementIds.add(cur.getId());
+ continue;
+ }
+ if (cur.getSubscriptionEventType() == SubscriptionEventType.STOP_BILLING &&
+ guiltyEntitlementIds.contains(cur.getId())) {
+ guiltyEntitlementIds.remove(cur.getId());
+ final SubscriptionEvent correctedEvent = new DefaultSubscriptionEvent((DefaultSubscriptionEvent) cur, SubscriptionEventType.PAUSE_BILLING);
+ it.set(correctedEvent);
+ }
+ }
+ }
+
+
+ private void insertSubscriptionEvent(final SubscriptionEvent event, final LinkedList<SubscriptionEvent> result) {
+ int index = 0;
+ for (SubscriptionEvent cur : result) {
+ int compEffectiveDate = event.getEffectiveDate().compareTo(cur.getEffectiveDate());
+ if (compEffectiveDate < 0) {
+ // EffectiveDate is less than cur -> insert here
+ break;
+ } else if (compEffectiveDate == 0) {
+
+ int compCreatedDate = ((DefaultSubscriptionEvent) event).getCreatedDate().compareTo(((DefaultSubscriptionEvent) cur).getCreatedDate());
+ if (compCreatedDate < 0) {
+ // Same EffectiveDate but CreatedDate is less than cur -> insert here
+ break;
+ } else if (compCreatedDate == 0) {
+ int compUUID = event.getId().compareTo(cur.getId());
+ if (compUUID < 0) {
+ // Same EffectiveDate and CreatedDate but order by ID
+ break;
+ } else if (compUUID == 0) {
+ if (event.getSubscriptionEventType().ordinal() < cur.getSubscriptionEventType().ordinal()) {
+ // Same EffectiveDate, CreatedDate and ID, but event type is lower -- as described in enum
+ break;
+ } else {
+ // If we are here : Same EffectiveDate, CreatedDate and ID, but event type is greater (or equal),
+ // then we look for next entry and restart the process
+ index++;
+ }
+ }
+ }
+ }
+ }
+ 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) {
+ return new DefaultSubscriptionEvent(in.getId(),
+ entitlementId,
+ new LocalDate(in.getEffectiveDate(), accountTimeZone),
+ new LocalDate(in.getCreatedDate(), accountTimeZone),
+ eventType,
+ in.isBlockEntitlement(),
+ in.isBlockBilling(),
+ in.getService(),
+ in.getStateName(),
+ prev.getNextProduct(),
+ prev.getNextPlan(),
+ prev.getNextPhase(),
+ prev.getNextPriceList(),
+ prev.getNextBillingPeriod(),
+ next.getPrevProduct(),
+ next.getPrevPlan(),
+ next.getPrevPhase(),
+ next.getPrevPriceList(),
+ next.getPrevBillingPeriod(),
+ in.getCreatedDate());
+ }
+
+ private SubscriptionEvent toSubscriptionEvent(final SubscriptionBaseTransition in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone) {
+ return new DefaultSubscriptionEvent(in.getId(),
+ in.getSubscriptionId(),
+ new LocalDate(in.getEffectiveTransitionTime(), accountTimeZone),
+ new LocalDate(in.getRequestedTransitionTime(), accountTimeZone),
+ eventType,
+ false,
+ false,
+ DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ eventType.toString(),
+ (in.getPreviousPlan() != null ? in.getPreviousPlan().getProduct() : null),
+ in.getPreviousPlan(),
+ in.getPreviousPhase(),
+ in.getPreviousPriceList(),
+ (in.getPreviousPlan() != null ? in.getPreviousPlan().getBillingPeriod() : null),
+ (in.getNextPlan() != null ? in.getNextPlan().getProduct() : null),
+ in.getNextPlan(),
+ in.getNextPhase(),
+ in.getNextPriceList(),
+ (in.getNextPlan() != null ? in.getNextPlan().getBillingPeriod() : null),
+ in.getCreatedDate());
+ }
+
+ private SubscriptionEventType toEventType(final SubscriptionBaseTransitionType in) {
+ switch (in) {
+ case CREATE:
+ return SubscriptionEventType.START_ENTITLEMENT;
+ case MIGRATE_ENTITLEMENT:
+ return SubscriptionEventType.START_ENTITLEMENT;
+ case TRANSFER:
+ return SubscriptionEventType.START_ENTITLEMENT;
+ case MIGRATE_BILLING:
+ return SubscriptionEventType.START_BILLING;
+ case CHANGE:
+ return SubscriptionEventType.CHANGE;
+ case CANCEL:
+ return SubscriptionEventType.STOP_BILLING;
+ case PHASE:
+ return SubscriptionEventType.PHASE;
+ // STEPH This is the old way of pausing billing; not used any longer, but kept for compatibility reason
+ case RE_CREATE:
+ return SubscriptionEventType.RESUME_BILLING;
+ /*
+ * Those can be ignore:
+ */
+ // Marker event
+ case UNCANCEL:
+ // Junction billing events-- that info is part of blocking states, we will get outside of subscription base
+ case START_BILLING_DISABLED:
+ case END_BILLING_DISABLED:
+ default:
+ return null;
+ }
+ }
+
+
+ @Override
+ public UUID getAccountId() {
+ return accountId;
+ }
+
+ @Override
+ public UUID getBundleId() {
+ return bundleId;
+ }
+
+ @Override
+ public String getExternalKey() {
+ return externalKey;
+ }
+
+ @Override
+ public List<SubscriptionEvent> getSubscriptionEvents() {
+ return events;
+ }
+
+ private static final class DefaultSubscriptionEvent implements SubscriptionEvent {
+
+ private final UUID id;
+ private final UUID entitlementId;
+ private final LocalDate effectiveDate;
+ private final LocalDate 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 DefaultSubscriptionEvent(final UUID id,
+ final UUID entitlementId,
+ final LocalDate effectiveDate,
+ final LocalDate 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) {
+ this.id = id;
+ this.entitlementId = entitlementId;
+ this.effectiveDate = effectiveDate;
+ this.requestedDate = requestedDate;
+ this.eventType = eventType;
+ isBlockingEntitlement = blockingEntitlement;
+ 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;
+ }
+
+ private DefaultSubscriptionEvent(DefaultSubscriptionEvent copy, SubscriptionEventType newEventType) {
+ this(copy.getId(),
+ copy.getEntitlementId(),
+ copy.getEffectiveDate(),
+ copy.getRequestedDate(),
+ 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());
+ }
+
+
+ @Override
+ public UUID getId() {
+ return id;
+ }
+
+ @Override
+ public UUID getEntitlementId() {
+ return entitlementId;
+ }
+
+ @Override
+ public LocalDate getEffectiveDate() {
+ return effectiveDate;
+ }
+
+ @Override
+ public LocalDate getRequestedDate() {
+ return requestedDate;
+ }
+
+ @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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/subscription/src/main/java/com/ning/billing/subscription/api/user/DefaultSubscriptionBase.java b/subscription/src/main/java/com/ning/billing/subscription/api/user/DefaultSubscriptionBase.java
index 4329d22..3a2b71d 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/api/user/DefaultSubscriptionBase.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/api/user/DefaultSubscriptionBase.java
@@ -635,7 +635,9 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
previousPriceList,
nextEventId, nextCreatedDate,
nextState, nextPlan, nextPhase,
- nextPriceList, cur.getTotalOrdering(), nextUserToken,
+ nextPriceList, cur.getTotalOrdering(),
+ cur.getCreatedDate(),
+ nextUserToken,
isFromDisk);
transitions.add(transition);
diff --git a/subscription/src/main/java/com/ning/billing/subscription/api/user/SubscriptionBaseTransitionData.java b/subscription/src/main/java/com/ning/billing/subscription/api/user/SubscriptionBaseTransitionData.java
index 7216940..685fbec 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/api/user/SubscriptionBaseTransitionData.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/api/user/SubscriptionBaseTransitionData.java
@@ -53,6 +53,7 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
private final Boolean isFromDisk;
private final Integer remainingEventsForUserOperation;
private final UUID userToken;
+ private final DateTime createdDate;
public SubscriptionBaseTransitionData(final UUID eventId,
final UUID subscriptionId,
@@ -74,6 +75,7 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
final PlanPhase nextPhase,
final PriceList nextPriceList,
final Long totalOrdering,
+ final DateTime createdDate,
final UUID userToken,
final Boolean isFromDisk) {
this.eventId = eventId;
@@ -98,6 +100,7 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
this.nextEventCreatedDate = nextEventCreatedDate;
this.isFromDisk = isFromDisk;
this.userToken = userToken;
+ this.createdDate = createdDate;
this.remainingEventsForUserOperation = 0;
}
@@ -131,8 +134,10 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
this.isFromDisk = input.isFromDisk();
this.userToken = input.getUserToken();
this.remainingEventsForUserOperation = remainingEventsForUserOperation;
+ this.createdDate = input.getCreatedDate();
}
+ @Override
public UUID getId() {
return eventId;
}
@@ -230,6 +235,10 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
throw new SubscriptionBaseError("Unexpected event type " + eventType);
}
}
+ @Override
+ public DateTime getCreatedDate() {
+ return createdDate;
+ }
@Override
public DateTime getRequestedTransitionTime() {