killbill-aplcache

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 796bd34..b9ddef6 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
@@ -98,78 +98,91 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
         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);
+            int index = insertFromBlockingEvent(accountTimeZone, allEntitlementUUIDs, result, bs, bsEffectiveDate, 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) {
+    private int insertFromBlockingEvent(final DateTimeZone accountTimeZone, final Set<UUID> allEntitlementUUIDs, final LinkedList<SubscriptionEvent> result, final BlockingState bs, final LocalDate bsEffectiveDate, final List<SubscriptionEvent> newEvents) {
+
+
+        // In the beginning there was nothing...
+        final Map<UUID, Boolean> blockedEntitlementMap = new HashMap<UUID, Boolean>();
+        final Map<UUID, Boolean> blockedBillingMap = new HashMap<UUID, Boolean>();
+        for (UUID uuid : allEntitlementUUIDs) {
+            blockedEntitlementMap.put(uuid, Boolean.TRUE);
+            blockedBillingMap.put(uuid, Boolean.TRUE);
+        }
+
         int index = -1;
         final Iterator<SubscriptionEvent> it = result.iterator();
+        DefaultSubscriptionEvent cur = null;
         while (it.hasNext()) {
-            final DefaultSubscriptionEvent cur = (DefaultSubscriptionEvent) it.next();
+            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 boolean shouldContinue = (compEffectiveDate > 0 ||
+                                            (compEffectiveDate == 0 && bs.getCreatedDate().compareTo(cur.getCreatedDate()) >= 0));
+
+            if (shouldContinue) {
+                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;
+                }
+            } else {
+                break;
             }
+        }
 
-            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 DefaultSubscriptionEvent next = it.hasNext() ? (DefaultSubscriptionEvent) it.next() : null;
 
-                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);
+        final List<UUID> targetEntitlementIds = bs.getType() == BlockingStateType.SUBSCRIPTION ? ImmutableList.<UUID>of(bs.getBlockedId()) :
+                                                ImmutableList.<UUID>copyOf(allEntitlementUUIDs);
+        for (UUID target : targetEntitlementIds) {
 
-                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));
-                }
+            // If the blocking state is ENT_STATE_CANCELLED there is nothing else to look at, just insert the event
+            if (bs.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CANCELLED)) {
+                newEvents.add(toSubscriptionEvent(cur, next, target, bs, SubscriptionEventType.STOP_ENTITLEMENT, accountTimeZone));
+                continue;
+            }
+
+            // If not, figure out from the existing state, what that new event should be
+            final Boolean isResumeEntitlement = (blockedEntitlementMap.get(target) && !bs.isBlockEntitlement());
+            final Boolean isPauseEntitlement = (!blockedEntitlementMap.get(target) && bs.isBlockEntitlement());
+            final Boolean isResumeBilling = (blockedBillingMap.get(target) && !bs.isBlockBilling());
+            final Boolean isPauseBilling = (!blockedBillingMap.get(target) && 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;
     }
@@ -242,10 +255,10 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
                         // 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;
-                         }
+                        if (event.getSubscriptionEventType().ordinal() < cur.getSubscriptionEventType().ordinal()) {
+                            // Same EffectiveDate, CreatedDate and ID, but event type is lower -- as described in enum
+                            break;
+                        }
                     }
                 }
             }
@@ -264,16 +277,16 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
                                             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(),
+                                            prev != null ? prev.getNextProduct() : null,
+                                            prev != null ? prev.getNextPlan() : null,
+                                            prev != null ? prev.getNextPhase() : null,
+                                            prev != null ? prev.getNextPriceList() : null,
+                                            prev != null ? prev.getNextBillingPeriod() : 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,
                                             in.getCreatedDate());
     }
 
@@ -377,7 +390,6 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
         private final DateTime createdDate;
 
 
-
         private DefaultSubscriptionEvent(final UUID id,
                                          final UUID entitlementId,
                                          final LocalDate effectiveDate,
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 2c35cd8..1c7370c 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
@@ -19,6 +19,7 @@ import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.catalog.api.PriceList;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.Product;
+import com.ning.billing.entitlement.DefaultEntitlementService;
 import com.ning.billing.entitlement.EntitlementTestSuiteNoDB;
 import com.ning.billing.entitlement.api.SubscriptionBundleTimeline.SubscriptionEvent;
 import com.ning.billing.entitlement.api.SubscriptionBundleTimeline.SubscriptionEventType;
@@ -27,6 +28,7 @@ import com.ning.billing.subscription.api.user.SubscriptionBaseTransition;
 import com.ning.billing.subscription.api.user.SubscriptionBaseTransitionData;
 import com.ning.billing.subscription.events.SubscriptionBaseEvent.EventType;
 import com.ning.billing.subscription.events.user.ApiEventType;
+import com.ning.billing.util.svcapi.junction.DefaultBlockingState;
 
 import static org.testng.Assert.assertEquals;
 
@@ -41,7 +43,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
     }
 
     @Test(groups = "fast")
-    public void testSimple() throws CatalogApiException {
+    public void testOneEntitlementNoBlockingStates() throws CatalogApiException {
 
         clock.setDay(new LocalDate(2013, 1, 1));
 
@@ -96,6 +98,81 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
     }
 
+
+
+    @Test(groups = "fast")
+    public void testOneEntitlementWithBlockingStatesSubscription() 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());
+        allTransitions.add(tr1);
+
+        effectiveDate = effectiveDate.plusDays(30);
+        clock.addDays(30);
+        final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow());
+        allTransitions.add(tr2);
+
+        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(15);
+        clock.addDays(15);
+        final SubscriptionBaseTransition tr3 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CANCEL, requestedDate, effectiveDate, clock.getUTCNow());
+        allTransitions.add(tr3);
+        final BlockingState bs2 = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                           DefaultEntitlementApi.ENT_STATE_CANCELLED, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                           true, true, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+
+        blockingStates.add(bs2);
+
+        final List<Entitlement> entitlements = new ArrayList<Entitlement>();
+        final Entitlement entitlement = createEntitlement(entitlementId, allTransitions);
+        entitlements.add(entitlement);
+
+
+
+
+        final DefaultSubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, blockingStates);
+
+        List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
+        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(bs1.getEffectiveDate(), accountTimeZone)), 0);
+        assertEquals(events.get(4).getEffectiveDate().compareTo(new LocalDate(tr3.getEffectiveTransitionTime(), accountTimeZone)), 0);
+        assertEquals(events.get(5).getEffectiveDate().compareTo(new LocalDate(bs2.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.STOP_ENTITLEMENT);
+        assertEquals(events.get(5).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
+    }
+
+
+
+
     private DefaultEntitlement createEntitlement(final UUID entitlementId, final List<SubscriptionBaseTransition> allTransitions) {
 
         final DefaultEntitlement result = Mockito.mock(DefaultEntitlement.class);