killbill-aplcache

Details

diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
index 88f0d08..8006a39 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
@@ -129,6 +129,7 @@ public class PlanAligner  {
             // If we never had any Plan change, borrow the logics for createPlan alignment
             case MIGRATE_ENTITLEMENT:
             case CREATE:
+            case RE_CREATE:                
                 List<TimedPhase> timedPhases = getTimedPhaseOnCreate(subscription.getStartDate(),
                         subscription.getBundleStartDate(),
                         lastPlanTransition.getNextPlan(),
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultEntitlementRepairApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultEntitlementRepairApi.java
index 547cc93..e7cd999 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultEntitlementRepairApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultEntitlementRepairApi.java
@@ -56,6 +56,14 @@ public class DefaultEntitlementRepairApi implements EntitlementRepairApi {
     private final SubscriptionFactory factory;
     private final RepairEntitlementLifecycleDao repairDao;
 
+    
+    private enum RepairType  {
+        BASE_REPAIR,
+        ADD_ON_REPAIR,
+        STANDALONE_REPAIR
+    }
+
+    
     @Inject
     public DefaultEntitlementRepairApi(@Named(EntitlementModule.REPAIR_NAMED) final SubscriptionFactory factory,
             @Named(EntitlementModule.REPAIR_NAMED) final RepairEntitlementLifecycleDao repairDao, final EntitlementDao dao) {
@@ -82,6 +90,7 @@ public class DefaultEntitlementRepairApi implements EntitlementRepairApi {
         return createGetBundleRepair(bundleId, viewId, repairs);
     }
     
+    
 
     @Override
     public BundleRepair repairBundle(final BundleRepair input, final boolean dryRun, final CallContext context)
@@ -116,6 +125,7 @@ public class DefaultEntitlementRepairApi implements EntitlementRepairApi {
             List<SubscriptionDataRepair> inRepair =  new LinkedList<SubscriptionDataRepair>();
             for (Subscription cur : subscriptions) {
 
+                //
                 SubscriptionRepair curRepair = findAndCreateSubscriptionRepair(cur.getId(), input.getSubscriptions());
                 if (curRepair != null) {
                     SubscriptionDataRepair curInputRepair = ((SubscriptionDataRepair) cur);
@@ -166,10 +176,29 @@ public class DefaultEntitlementRepairApi implements EntitlementRepairApi {
                 }
             }
             
-            // This is a pure AO repair
-            if (baseSubscriptionRepair == null && subscriptions.get(0).getCategory() == ProductCategory.BASE) {
-                SubscriptionDataRepair baseSubscription =  (SubscriptionDataRepair) subscriptions.get(0);
-                baseSubscriptionRepair = createSubscriptionDataRepair(baseSubscription, baseSubscription.getBundleStartDate(), baseSubscription.getStartDate(), baseSubscription.getEvents());
+            final RepairType repairType = getRepairType(subscriptions.get(0), (baseSubscriptionRepair != null));
+            switch(repairType) {
+                case BASE_REPAIR:
+                   // We need to add any existing addon that are not in the input repair list
+                    for (Subscription cur : subscriptions) {
+                        if (cur.getCategory() == ProductCategory.ADD_ON && !inRepair.contains(cur)) {
+                            SubscriptionDataRepair curOutputRepair = createSubscriptionDataRepair((SubscriptionDataRepair) cur, newBundleStartDate, null, ((SubscriptionDataRepair) cur).getEvents());
+                            repairDao.initializeRepair(curOutputRepair.getId(), ((SubscriptionDataRepair) cur).getEvents());
+                            inRepair.add(curOutputRepair);
+                            addOnSubscriptionInRepair.add(curOutputRepair);
+                        }
+                    }
+                    
+                    break;
+                case ADD_ON_REPAIR:
+                    // We need to set the baseSubscription as it is useful to calculate addon validity
+                    SubscriptionDataRepair baseSubscription =  (SubscriptionDataRepair) subscriptions.get(0);
+                    baseSubscriptionRepair = createSubscriptionDataRepair(baseSubscription, baseSubscription.getBundleStartDate(), baseSubscription.getStartDate(), baseSubscription.getEvents());
+                    break;
+                case STANDALONE_REPAIR:
+                default:
+                    break;
+                    
             }
 
             validateBasePlanRecreate(isBasePlanRecreate, subscriptions, input.getSubscriptions());
@@ -199,6 +228,13 @@ public class DefaultEntitlementRepairApi implements EntitlementRepairApi {
         }
     }
 
+    private RepairType getRepairType(final Subscription firstSubscription, final boolean gotBaseSubscription) {
+        if (firstSubscription.getCategory() == ProductCategory.BASE) {
+            return gotBaseSubscription ? RepairType.BASE_REPAIR : RepairType.ADD_ON_REPAIR;
+        } else {
+            return RepairType.STANDALONE_REPAIR;
+        }
+    }
     
     private void validateBasePlanRecreate(boolean isBasePlanRecreate, List<Subscription> subscriptions, List<SubscriptionRepair> input) 
         throws EntitlementRepairException  {
@@ -240,11 +276,11 @@ public class DefaultEntitlementRepairApi implements EntitlementRepairApi {
     throws EntitlementRepairException {
         if (lastBPRemainingTime != null &&
                 firstNewEvent.getRequestedDate().isBefore(lastBPRemainingTime)) {
-            throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NEW_EVENT_BEFORE_LAST_BP_REMAINING, firstNewEvent.getPlanPhaseSpecifier().toString(), data.getId());
+            throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NEW_EVENT_BEFORE_LAST_BP_REMAINING, firstNewEvent.getSubscriptionTransitionType(), data.getId());
         }
         if (lastRemainingTime != null &&
                 firstNewEvent.getRequestedDate().isBefore(lastRemainingTime)) {
-            throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NEW_EVENT_BEFORE_LAST_AO_REMAINING, firstNewEvent.getPlanPhaseSpecifier().toString(), data.getId());
+            throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NEW_EVENT_BEFORE_LAST_AO_REMAINING, firstNewEvent.getSubscriptionTransitionType(), data.getId());
         }
 
     }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultSubscriptionRepair.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultSubscriptionRepair.java
index efae0b5..297825c 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultSubscriptionRepair.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultSubscriptionRepair.java
@@ -41,13 +41,22 @@ public class DefaultSubscriptionRepair implements SubscriptionRepair  {
     private final List<NewEvent> newEvents;
     private final List<DeletedEvent> deletedEvents;    
     
+    public DefaultSubscriptionRepair(final UUID id) {
+        this.id = id;
+        this.existingEvents = Collections.<SubscriptionRepair.ExistingEvent>emptyList();
+        this.deletedEvents =  Collections.<SubscriptionRepair.DeletedEvent>emptyList();
+        this.newEvents = Collections.<SubscriptionRepair.NewEvent>emptyList();
+    }
     
     public DefaultSubscriptionRepair(SubscriptionRepair input) {
         this.id = input.getId();
-        this.existingEvents = (input.getExistingEvents() != null) ? new ArrayList<SubscriptionRepair.ExistingEvent>(input.getExistingEvents()) : Collections.<SubscriptionRepair.ExistingEvent>emptyList();
+        this.existingEvents = (input.getExistingEvents() != null) ? new ArrayList<SubscriptionRepair.ExistingEvent>(input.getExistingEvents()) : 
+            Collections.<SubscriptionRepair.ExistingEvent>emptyList();
         sortExistingEvent(this.existingEvents);
-        this.deletedEvents = (input.getDeletedEvents() != null) ? new ArrayList<SubscriptionRepair.DeletedEvent>(input.getDeletedEvents()) : Collections.<SubscriptionRepair.DeletedEvent>emptyList();
-        this.newEvents = (input.getNewEvents() != null) ? new ArrayList<SubscriptionRepair.NewEvent>(input.getNewEvents()) : null;
+        this.deletedEvents = (input.getDeletedEvents() != null) ? new ArrayList<SubscriptionRepair.DeletedEvent>(input.getDeletedEvents()) : 
+            Collections.<SubscriptionRepair.DeletedEvent>emptyList();
+        this.newEvents = (input.getNewEvents() != null) ? new ArrayList<SubscriptionRepair.NewEvent>(input.getNewEvents()) : 
+            Collections.<SubscriptionRepair.NewEvent>emptyList();
         sortNewEvent(this.newEvents);
     }
     
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionFactory.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionFactory.java
index a76599e..01a9328 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionFactory.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionFactory.java
@@ -27,6 +27,7 @@ import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
 import com.ning.billing.entitlement.engine.addon.AddonUtils;
+import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.glue.EntitlementModule;
 import com.ning.billing.util.clock.Clock;
@@ -34,17 +35,21 @@ import com.ning.billing.util.clock.Clock;
 public class RepairSubscriptionFactory extends DefaultSubscriptionFactory implements SubscriptionFactory {
 
     private final AddonUtils addonUtils;
+    private final EntitlementDao repairDao;
     
     @Inject
-    public RepairSubscriptionFactory(@Named(EntitlementModule.REPAIR_NAMED) SubscriptionApiService apiService, Clock clock, CatalogService catalogService, AddonUtils addonUtils) {
+    public RepairSubscriptionFactory(@Named(EntitlementModule.REPAIR_NAMED) SubscriptionApiService apiService,
+            @Named(EntitlementModule.REPAIR_NAMED) EntitlementDao dao,
+            Clock clock, CatalogService catalogService, AddonUtils addonUtils) {
         super(apiService, clock, catalogService);
         this.addonUtils = addonUtils;
+        this.repairDao = dao;
     }
      
     @Override
     public SubscriptionData createSubscription(SubscriptionBuilder builder,
             List<EntitlementEvent> events) {
-        SubscriptionData subscription = new SubscriptionDataRepair(builder, events, apiService, clock, addonUtils);
+        SubscriptionData subscription = new SubscriptionDataRepair(builder, events, apiService, repairDao, clock, addonUtils, catalogService);
         subscription.rebuildTransitions(events, catalogService.getFullCatalog());
         return subscription;
     }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionDataRepair.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionDataRepair.java
index b983b55..809c23c 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionDataRepair.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionDataRepair.java
@@ -21,12 +21,14 @@ import java.util.LinkedList;
 import java.util.List;
 
 import org.joda.time.DateTime;
+import org.omg.CORBA.Request;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.Catalog;
 import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.CatalogService;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.Product;
@@ -36,7 +38,11 @@ import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
 import com.ning.billing.entitlement.engine.addon.AddonUtils;
+import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
+import com.ning.billing.entitlement.events.user.ApiEventBuilder;
+import com.ning.billing.entitlement.events.user.ApiEventCancel;
 
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.clock.Clock;
@@ -45,7 +51,9 @@ public class SubscriptionDataRepair extends SubscriptionData {
 
     private final AddonUtils addonUtils;
     private final Clock clock;
-
+    private final EntitlementDao repairDao;
+    private final CatalogService catalogService;
+    
     private final List<EntitlementEvent> initialEvents;
 
     // Low level events are ONLY used for Repair APIs
@@ -53,17 +61,33 @@ public class SubscriptionDataRepair extends SubscriptionData {
 
 
     public SubscriptionDataRepair(SubscriptionBuilder builder, List<EntitlementEvent> initialEvents, SubscriptionApiService apiService,
-            Clock clock, AddonUtils addonUtils) {
+            EntitlementDao dao, Clock clock, AddonUtils addonUtils, CatalogService catalogService) {
         super(builder, apiService, clock);
+        this.repairDao = dao;
         this.addonUtils = addonUtils;
         this.clock = clock;
+        this.catalogService = catalogService;
         this.initialEvents = initialEvents;
     }
 
+    
+    DateTime getLastUserEventEffectiveDate() {
+        DateTime res = null;
+        for (EntitlementEvent cur : events) {
+            if (cur.getActiveVersion() != getActiveVersion()) {
+                break;
+            }
+            if (cur.getType() == EventType.PHASE) {
+                continue;
+            }
+            res = cur.getEffectiveDate();
+        }
+        return res;
+    }
+
     public void addNewRepairEvent(final DefaultNewEvent input, final SubscriptionDataRepair baseSubscription, final List<SubscriptionDataRepair> addonSubscriptions, final CallContext context)
     throws EntitlementRepairException {
 
-
         try {
             final PlanPhaseSpecifier spec = input.getPlanPhaseSpecifier();
             switch(input.getSubscriptionTransitionType()) {
@@ -75,16 +99,17 @@ public class SubscriptionDataRepair extends SubscriptionData {
             case CHANGE:
                 changePlan(spec.getProductName(), spec.getBillingPeriod(), spec.getPriceListName(), input.getRequestedDate(), context);
                 checkAddonRights(baseSubscription);
+                trickleDownBPEffectForAddon(addonSubscriptions, getLastUserEventEffectiveDate(), context);
                 break;
             case CANCEL:
                 cancel(input.getRequestedDate(), false, context);
+                trickleDownBPEffectForAddon(addonSubscriptions, getLastUserEventEffectiveDate(), context);
                 break;
             case PHASE:
                 break;
             default:
                 throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_UNKNOWN_TYPE, input.getSubscriptionTransitionType(), id);
             }
-            trickleDownBPEffectForAddon(addonSubscriptions, input.getRequestedDate(), context);
             
         } catch (EntitlementUserApiException e) {
             throw new EntitlementRepairException(e);
@@ -94,7 +119,7 @@ public class SubscriptionDataRepair extends SubscriptionData {
     }
 
 
-    private void trickleDownBPEffectForAddon(final List<SubscriptionDataRepair> addonSubscriptions, final DateTime requestedDate, final CallContext context)
+    private void trickleDownBPEffectForAddon(final List<SubscriptionDataRepair> addonSubscriptions, final DateTime effectiveDate, final CallContext context)
      throws EntitlementUserApiException {
 
         if (category != ProductCategory.BASE) {
@@ -117,7 +142,16 @@ public class SubscriptionDataRepair extends SubscriptionData {
                     addonUtils.isAddonIncluded(baseProduct, addonCurrentPlan) ||
                     ! addonUtils.isAddonAvailable(baseProduct, addonCurrentPlan)) {
 
-                cur.cancel(requestedDate, false, context);
+                EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
+                .setSubscriptionId(cur.getId())
+                .setActiveVersion(cur.getActiveVersion())
+                .setProcessedDate(now)
+                .setEffectiveDate(effectiveDate)
+                .setRequestedDate(now)
+                .setUserToken(context.getUserToken())
+                .setFromDisk(true));
+                repairDao.cancelSubscription(cur.getId(), cancelEvent, context, 0);
+                cur.rebuildTransitions(repairDao.getEventsForSubscription(cur.getId()), catalogService.getFullCatalog());
             }
         }
     }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
index 5e9f53b..9bfaa70 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
@@ -239,6 +239,32 @@ public class SubscriptionData extends ExtendedEntityBase implements
         return paidThroughDate;
     }
 
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result
+                + ((id == null) ? 0 : id.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        SubscriptionData other = (SubscriptionData) obj;
+        if (id == null) {
+            if (other.id != null)
+                return false;
+        } else if (!id.equals(other.id))
+            return false;
+        return true;
+    }
+
     public List<SubscriptionEventTransition> getBillingTransitions() {
 
         if (transitions == null) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
index f22b2a3..453020c 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
@@ -148,9 +148,6 @@ public class Engine implements EventListener, EntitlementService {
                     final CallContext context = factory.createCallContext("SubscriptionEventQueue", CallOrigin.INTERNAL, UserType.SYSTEM, userToken);
                     processEventReady(event, key.getSeqId(), context);
                 }
-                
-    
-
             },
             new NotificationConfig() {
                 @Override
@@ -214,6 +211,11 @@ public class Engine implements EventListener, EntitlementService {
             log.warn("Failed to retrieve subscription for id %s", event.getSubscriptionId());
             return;
         }
+        if (subscription.getActiveVersion() > event.getActiveVersion()) {
+            // Skip repaired events
+            return;
+        }
+        
         //
         // Do any internal processing on that event before we send the event to the bus
         //
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
index 85c203a..0281894 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
@@ -608,13 +608,18 @@ public class EntitlementSqlDao implements EntitlementDao {
                     TransactionStatus status) throws Exception {
 
                 EventSqlDao transEventDao = transactional.become(EventSqlDao.class);
-                for (SubscriptionDataRepair cur : inRepair) {
+                for (final SubscriptionDataRepair cur : inRepair) {
                     transactional.updateForRepair(cur.getId().toString(), cur.getActiveVersion(), cur.getStartDate().toDate(), cur.getBundleStartDate().toDate(), context);
                     for (EntitlementEvent event : cur.getInitialEvents()) {
-                        transEventDao.updateVersion(event.getId().toString(), cur.getActiveVersion(), context);
+                        transEventDao.updateVersion(event.getId().toString(), event.getActiveVersion(), context);
                     }
                     for (EntitlementEvent event : cur.getNewEvents()) {
                         transEventDao.insertEvent(event, context);
+                        if (event.getEffectiveDate().isAfter(clock.getUTCNow())) {
+                            recordFutureNotificationFromTransaction(transactional,
+                                    event.getEffectiveDate(),
+                                    new EntitlementNotificationKey(event.getId()));
+                        }
                     }
                 }
                 return null;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java
index dfb81d2..098dc4b 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java
@@ -22,6 +22,7 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeSet;
 import java.util.UUID;
 
 import com.ning.billing.entitlement.api.SubscriptionFactory;
@@ -42,23 +43,24 @@ public class RepairEntitlementDao implements EntitlementDao, RepairEntitlementLi
     
     private final static class SubscriptionRepairEvent {
         
-        private final List<EntitlementEvent> events;
+        private final Set<EntitlementEvent> events;
         
         public SubscriptionRepairEvent(List<EntitlementEvent> initialEvents) {
-            events = new LinkedList<EntitlementEvent>();
-            if (initialEvents != null) {
-                events.addAll(initialEvents);
-            }
-        }
-        public List<EntitlementEvent> getEvents() {
-            Collections.sort(events, new Comparator<EntitlementEvent>() {
+            events = new TreeSet<EntitlementEvent>(new Comparator<EntitlementEvent>() {
                 @Override
                 public int compare(EntitlementEvent o1, EntitlementEvent o2) {
                     return o1.compareTo(o2);
                 }
             });
+            if (initialEvents != null) {
+                events.addAll(initialEvents);
+            }
+        }
+        
+        public Set<EntitlementEvent> getEvents() {
             return events;
         }
+        
         public void addEvents(List<EntitlementEvent> newEvents) {
             events.addAll(newEvents);
         }
@@ -79,7 +81,7 @@ public class RepairEntitlementDao implements EntitlementDao, RepairEntitlementLi
     @Override
     public List<EntitlementEvent> getEventsForSubscription(UUID subscriptionId) {
         SubscriptionRepairEvent target =  getRepairSubscriptionEvents(subscriptionId);
-        return target.getEvents();
+        return new LinkedList<EntitlementEvent>(target.getEvents());
     }
 
     @Override
@@ -97,7 +99,17 @@ public class RepairEntitlementDao implements EntitlementDao, RepairEntitlementLi
     @Override
     public void cancelSubscription(UUID subscriptionId,
             EntitlementEvent cancelEvent, CallContext context, int cancelSeq) {
-        addEvents(subscriptionId, Collections.singletonList(cancelEvent));        
+        long activeVersion = cancelEvent.getActiveVersion();
+        addEvents(subscriptionId, Collections.singletonList(cancelEvent));
+        SubscriptionRepairEvent target =  getRepairSubscriptionEvents(subscriptionId);
+        boolean foundCancelEvent = false;
+        for (EntitlementEvent cur : target.getEvents()) {
+            if (cur.getId().equals(cancelEvent.getId())) {
+                foundCancelEvent = true;
+            } else if (foundCancelEvent) { 
+                cur.setActiveVersion(activeVersion - 1);
+            }
+        }
     }
 
     
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairBP.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairBP.java
index 96b42c1..fe25958 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairBP.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairBP.java
@@ -43,13 +43,10 @@ import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.SubscriptionTransitionType;
-import com.ning.billing.entitlement.api.TestApiBase;
 import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
 import com.ning.billing.entitlement.api.repair.SubscriptionRepair.DeletedEvent;
 import com.ning.billing.entitlement.api.repair.SubscriptionRepair.ExistingEvent;
 import com.ning.billing.entitlement.api.repair.SubscriptionRepair.NewEvent;
-import com.ning.billing.entitlement.api.repair.TestApiBaseRepair.TestWithException;
-import com.ning.billing.entitlement.api.repair.TestApiBaseRepair.TestWithExceptionCallback;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
@@ -243,7 +240,29 @@ public class TestRepairBP extends TestApiBaseRepair {
         expected.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, newBaseProduct, PhaseType.EVERGREEN,
                     ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, restartDate.plusDays(30)));
 
-        testBPRepairCreate(true, startDate, clockShift, baseProduct, newBaseProduct, expected);
+        UUID baseSubscriptionId = testBPRepairCreate(true, startDate, clockShift, baseProduct, newBaseProduct, expected);
+        
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        clock.addDeltaFromReality(getDurationDay(32));
+        assertTrue(testListener.isCompleted(5000));
+        
+        // CHECK WHAT"S GOING ON AFTER WE MOVE CLOCK-- FUTURE MOTIFICATION SHOULD KICK IN
+        SubscriptionData subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscriptionId);
+        
+        assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+        assertEquals(subscription.getBundleId(), bundle.getId());
+        assertEquals(subscription.getStartDate(), restartDate);
+        assertEquals(subscription.getBundleStartDate(), restartDate);        
+
+        Plan currentPlan = subscription.getCurrentPlan();
+        assertNotNull(currentPlan);
+        assertEquals(currentPlan.getProduct().getName(), newBaseProduct);
+        assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+        assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.MONTHLY);
+
+        PlanPhase currentPhase = subscription.getCurrentPhase();
+        assertNotNull(currentPhase);
+        assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
     }
 
     
@@ -267,7 +286,7 @@ public class TestRepairBP extends TestApiBaseRepair {
     }
     
     
-    private void testBPRepairCreate(boolean inTrial, DateTime startDate, int clockShift, 
+    private UUID testBPRepairCreate(boolean inTrial, DateTime startDate, int clockShift, 
             String baseProduct, String newBaseProduct, List<ExistingEvent> expectedEvents) throws Exception {
 
         // CREATE BP
@@ -367,6 +386,8 @@ public class TestRepairBP extends TestApiBaseRepair {
         currentPhase = realRunBaseSubscription.getCurrentPhase();
         assertNotNull(currentPhase);
         assertEquals(currentPhase.getPhaseType(), PhaseType.TRIAL);
+        
+        return baseSubscription.getId();
     }
 
     @Test(groups={"slow"})
@@ -387,7 +408,28 @@ public class TestRepairBP extends TestApiBaseRepair {
         expected.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, newBaseProduct, PhaseType.EVERGREEN,
                     ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, startDate.plusDays(30)));
 
-        testBPRepairAddChange(true, startDate, clockShift, baseProduct, newBaseProduct, expected, 3);
+        UUID baseSubscriptionId = testBPRepairAddChange(true, startDate, clockShift, baseProduct, newBaseProduct, expected, 3);
+        
+        // CHECK WHAT"S GOING ON AFTER WE MOVE CLOCK-- FUTURE MOTIFICATION SHOULD KICK IN
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        clock.addDeltaFromReality(getDurationDay(32));
+        assertTrue(testListener.isCompleted(5000));
+        SubscriptionData subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscriptionId);
+        
+        assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+        assertEquals(subscription.getBundleId(), bundle.getId());
+        assertEquals(subscription.getStartDate(), startDate);
+        assertEquals(subscription.getBundleStartDate(), startDate);        
+
+        Plan currentPlan = subscription.getCurrentPlan();
+        assertNotNull(currentPlan);
+        assertEquals(currentPlan.getProduct().getName(), newBaseProduct);
+        assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+        assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.MONTHLY);
+
+        PlanPhase currentPhase = subscription.getCurrentPhase();
+        assertNotNull(currentPhase);
+        assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
     }
 
     @Test(groups={"slow"})
@@ -411,7 +453,7 @@ public class TestRepairBP extends TestApiBaseRepair {
     }
     
 
-    private void testBPRepairAddChange(boolean inTrial, DateTime startDate, int clockShift, 
+    private UUID testBPRepairAddChange(boolean inTrial, DateTime startDate, int clockShift, 
             String baseProduct, String newBaseProduct, List<ExistingEvent> expectedEvents, int expectedTransitions) throws Exception {
 
         // CREATE BP
@@ -515,8 +557,76 @@ public class TestRepairBP extends TestApiBaseRepair {
         } else {
             assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
         }
+        return baseSubscription.getId();
     }
     
+    @Test(groups={"slow"})
+    public void testRepairWithFurureCancelEvent() throws Exception {
+      
+        DateTime startDate = clock.getUTCNow();
+        
+        // CREATE BP
+        Subscription baseSubscription = createSubscription("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, startDate);
+
+        // MOVE CLOCK -- OUT OF TRIAL
+        testListener.pushExpectedEvent(NextEvent.PHASE);                
+        clock.setDeltaFromReality(getDurationDay(35), 0);
+        assertTrue(testListener.isCompleted(5000));
+        
+        // SET CTD to BASE SUBSCRIPTION SP CANCEL OCCURS EOT
+        DateTime newChargedThroughDate = baseSubscription.getStartDate().plusDays(30).plusMonths(1);
+        billingApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate, context);
+        baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+
+        
+        DateTime requestedChange = clock.getUTCNow();
+        baseSubscription.changePlan("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, requestedChange, context);
+        
+        
+        // CHECK CHANGE DID NOT OCCUR YET
+        Plan currentPlan = baseSubscription.getCurrentPlan();
+        assertNotNull(currentPlan);
+        assertEquals(currentPlan.getProduct().getName(), "Shotgun");
+        assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+        assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.MONTHLY);
+        
+        
+        DateTime repairTime = clock.getUTCNow().minusDays(1);
+        BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+        sortEventsOnBundle(bundleRepair);
+        
+        PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+
+        NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, repairTime, spec);
+        List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+        des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(2).getEventId()));
+
+        SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
+        
+        // SKIP DRY RUN AND DO REPAIR...
+        BundleRepair bRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+        
+        boolean dryRun = false;
+        repairApi.repairBundle(bRepair, dryRun, context);
+     
+        baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+        
+        assertEquals(((SubscriptionData) baseSubscription).getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+        assertEquals(baseSubscription.getBundleId(), bundle.getId());
+        assertEquals(baseSubscription.getStartDate(), baseSubscription.getStartDate());
+
+        currentPlan = baseSubscription.getCurrentPlan();
+        assertNotNull(currentPlan);
+        assertEquals(currentPlan.getProduct().getName(), "Assault-Rifle");
+        assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+        assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.MONTHLY);
+
+        PlanPhase currentPhase = baseSubscription.getCurrentPhase();
+        assertNotNull(currentPhase);
+        assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
+    }
+    
+    
     // Needs real SQL backend to be tested properly
     @Test(groups={"slow"})
     public void testENT_REPAIR_VIEW_CHANGED_newEvent() throws Exception {
@@ -544,12 +654,11 @@ public class TestRepairBP extends TestApiBaseRepair {
 
                 BundleRepair bRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
 
-
                 testListener.pushExpectedEvent(NextEvent.CHANGE);
                 DateTime changeTime = clock.getUTCNow();
                 baseSubscription.changePlan("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, changeTime, context);
                 assertTrue(testListener.isCompleted(5000));
-
+                                
                 repairApi.repairBundle(bRepair, true, context);
             }
         }, ErrorCode.ENT_REPAIR_VIEW_CHANGED);
@@ -570,7 +679,6 @@ public class TestRepairBP extends TestApiBaseRepair {
             @Override
             public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
 
-                /*
                 BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
                 sortEventsOnBundle(bundleRepair);
                 PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
@@ -582,14 +690,11 @@ public class TestRepairBP extends TestApiBaseRepair {
 
                 BundleRepair bRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
 
-
-                testListener.pushExpectedEvent(NextEvent.CHANGE);
-                DateTime changeTime = clock.getUTCNow();
-                baseSubscription.changePlan("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, changeTime, context);
-                assertTrue(testListener.isCompleted(5000));
+                DateTime newChargedThroughDate = baseSubscription.getStartDate().plusDays(30).plusMonths(1);
+                billingApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate, context);
+                entitlementApi.getSubscriptionFromId(baseSubscription.getId());
 
                 repairApi.repairBundle(bRepair, true, context);
-                */
             }
         }, ErrorCode.ENT_REPAIR_VIEW_CHANGED);
     }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithAO.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithAO.java
index ac28307..d238a6a 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithAO.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithAO.java
@@ -18,15 +18,12 @@ package com.ning.billing.entitlement.api.repair;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.UUID;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
 import org.joda.time.DateTime;
-import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import com.google.inject.Guice;
@@ -60,16 +57,409 @@ public class TestRepairWithAO extends TestApiBaseRepair {
     @Test(groups={"slow"})
     public void testRepairChangeBPWithAddonIncluded() throws Exception {
         
+        String baseProduct = "Shotgun";
+        BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+        String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+        // CREATE BP
+        SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+        // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
+        Duration someTimeLater = getDurationDay(3);
+        clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+
+        SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+        
+        SubscriptionData aoSubscription2 = createSubscription("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+
+        // MOVE CLOCK A LITTLE BIT MORE -- STILL IN TRIAL
+        clock.addDeltaFromReality(someTimeLater);
+
+        BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+        sortEventsOnBundle(bundleRepair);
+        
+        // Quick check
+        SubscriptionRepair bpRepair = getSubscriptionRepair(baseSubscription.getId(), bundleRepair);
+        assertEquals(bpRepair.getExistingEvents().size(), 2);
+        
+        SubscriptionRepair aoRepair = getSubscriptionRepair(aoSubscription.getId(), bundleRepair);
+        assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+        SubscriptionRepair aoRepair2 = getSubscriptionRepair(aoSubscription2.getId(), bundleRepair);
+        assertEquals(aoRepair2.getExistingEvents().size(), 2);
+
+        DateTime bpChangeDate = clock.getUTCNow().minusDays(1);
+        
+        List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+        des.add(createDeletedEvent(bpRepair.getExistingEvents().get(1).getEventId()));        
+        
+        PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL);
+        NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, bpChangeDate, spec);
+        
+        bpRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
+        
+        bundleRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(bpRepair));
+        
+        boolean dryRun = true;
+        BundleRepair dryRunBundleRepair = repairApi.repairBundle(bundleRepair, dryRun, context);
+                
+        aoRepair = getSubscriptionRepair(aoSubscription.getId(), dryRunBundleRepair);
+        assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+        aoRepair2 = getSubscriptionRepair(aoSubscription2.getId(), dryRunBundleRepair);
+        assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+        bpRepair = getSubscriptionRepair(baseSubscription.getId(), dryRunBundleRepair);
+        assertEquals(bpRepair.getExistingEvents().size(), 3);        
+        
+        // Check expected for AO
+        List<ExistingEvent> expectedAO = new LinkedList<SubscriptionRepair.ExistingEvent>();
+        expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Telescopic-Scope", PhaseType.DISCOUNT,
+                ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoSubscription.getStartDate()));
+        expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.CANCEL, null, null,
+                ProductCategory.ADD_ON, null, null, bpChangeDate));
+        int index = 0;
+        for (ExistingEvent e : expectedAO) {
+           validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));           
+        }
+
+        List<ExistingEvent> expectedAO2 = new LinkedList<SubscriptionRepair.ExistingEvent>();
+        expectedAO2.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Laser-Scope", PhaseType.DISCOUNT,
+                ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoSubscription2.getStartDate()));
+        expectedAO2.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, "Laser-Scope", PhaseType.EVERGREEN,
+                ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoSubscription2.getStartDate().plusMonths(1)));
+        index = 0;
+        for (ExistingEvent e : expectedAO2) {
+           validateExistingEventForAssertion(e, aoRepair2.getExistingEvents().get(index++));           
+        }
+        
+        // Check expected for BP        
+        List<ExistingEvent> expectedBP = new LinkedList<SubscriptionRepair.ExistingEvent>();
+        expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Shotgun", PhaseType.TRIAL,
+                ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.NO_BILLING_PERIOD, baseSubscription.getStartDate()));
+        expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.CHANGE, "Assault-Rifle", PhaseType.TRIAL,
+                ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.NO_BILLING_PERIOD, bpChangeDate));
+        expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, "Assault-Rifle", PhaseType.EVERGREEN,
+                ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, baseSubscription.getStartDate().plusDays(30)));
+        index = 0;
+        for (ExistingEvent e : expectedBP) {
+           validateExistingEventForAssertion(e, bpRepair.getExistingEvents().get(index++));           
+        }
+
+        
+        SubscriptionData newAoSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+        assertEquals(newAoSubscription.getState(), SubscriptionState.ACTIVE);
+        assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+        assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+
+        SubscriptionData newAoSubscription2 = (SubscriptionData)  entitlementApi.getSubscriptionFromId(aoSubscription2.getId());
+        assertEquals(newAoSubscription2.getState(), SubscriptionState.ACTIVE);
+        assertEquals(newAoSubscription2.getAllTransitions().size(), 2);
+        assertEquals(newAoSubscription2.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+
+        
+        SubscriptionData newBaseSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+        assertEquals(newBaseSubscription.getState(), SubscriptionState.ACTIVE);
+        assertEquals(newBaseSubscription.getAllTransitions().size(), 2);
+        assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+        
+        dryRun = false;
+        BundleRepair realRunBundleRepair = repairApi.repairBundle(bundleRepair, dryRun, context);
+        
+        
+        aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
+        assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+        bpRepair = getSubscriptionRepair(baseSubscription.getId(), realRunBundleRepair);
+        assertEquals(bpRepair.getExistingEvents().size(), 3);        
+        
+        index = 0;
+        for (ExistingEvent e : expectedAO) {
+           validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));           
+        }
+        
+        index = 0;
+        for (ExistingEvent e : expectedAO2) {
+           validateExistingEventForAssertion(e, aoRepair2.getExistingEvents().get(index++));           
+        }
+
+        index = 0;
+        for (ExistingEvent e : expectedBP) {
+           validateExistingEventForAssertion(e, bpRepair.getExistingEvents().get(index++));           
+        }
+
+        newAoSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+        assertEquals(newAoSubscription.getState(), SubscriptionState.CANCELLED);
+        assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+        assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+            
+        newAoSubscription2 = (SubscriptionData)  entitlementApi.getSubscriptionFromId(aoSubscription2.getId());
+        assertEquals(newAoSubscription2.getState(), SubscriptionState.ACTIVE);
+        assertEquals(newAoSubscription2.getAllTransitions().size(), 2);
+        assertEquals(newAoSubscription2.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+
+        
+        newBaseSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+        assertEquals(newBaseSubscription.getState(), SubscriptionState.ACTIVE);
+        assertEquals(newBaseSubscription.getAllTransitions().size(), 3);
+        assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
     }
 
     @Test(groups={"slow"})
     public void testRepairChangeBPWithAddonNonAvailable() throws Exception {
         
+        String baseProduct = "Shotgun";
+        BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+        String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+        // CREATE BP
+        SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+        // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
+        Duration someTimeLater = getDurationDay(3);
+        clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+
+        SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+        
+        // MOVE CLOCK A LITTLE BIT MORE -- AFTER TRIAL
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        someTimeLater = getDurationDay(32);
+        clock.addDeltaFromReality(someTimeLater);
+        assertTrue(testListener.isCompleted(7000));        
+
+        BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+        sortEventsOnBundle(bundleRepair);
+        
+        // Quick check
+        SubscriptionRepair bpRepair = getSubscriptionRepair(baseSubscription.getId(), bundleRepair);
+        assertEquals(bpRepair.getExistingEvents().size(), 2);
+        
+        SubscriptionRepair aoRepair = getSubscriptionRepair(aoSubscription.getId(), bundleRepair);
+        assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+        DateTime bpChangeDate = clock.getUTCNow().minusDays(1);
+        
+        PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+        NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, bpChangeDate, spec);
+        
+        bpRepair = createSubscriptionReapir(baseSubscription.getId(), Collections.<SubscriptionRepair.DeletedEvent>emptyList(), Collections.singletonList(ne));
+        
+        bundleRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(bpRepair));
+        
+        boolean dryRun = true;
+        BundleRepair dryRunBundleRepair = repairApi.repairBundle(bundleRepair, dryRun, context);
+                
+        aoRepair = getSubscriptionRepair(aoSubscription.getId(), dryRunBundleRepair);
+        assertEquals(aoRepair.getExistingEvents().size(), 3);
+
+        bpRepair = getSubscriptionRepair(baseSubscription.getId(), dryRunBundleRepair);
+        assertEquals(bpRepair.getExistingEvents().size(), 3);        
+        
+        // Check expected for AO
+        List<ExistingEvent> expectedAO = new LinkedList<SubscriptionRepair.ExistingEvent>();
+        expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Telescopic-Scope", PhaseType.DISCOUNT,
+                ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoSubscription.getStartDate()));
+        expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Telescopic-Scope", PhaseType.EVERGREEN,
+                ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, baseSubscription.getStartDate().plusMonths(1)));
+        expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.CANCEL, null, null,
+                ProductCategory.ADD_ON, null, null, bpChangeDate));
+        int index = 0;
+        for (ExistingEvent e : expectedAO) {
+           validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));           
+        }
+
+        // Check expected for BP        
+        List<ExistingEvent> expectedBP = new LinkedList<SubscriptionRepair.ExistingEvent>();
+        expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Shotgun", PhaseType.TRIAL,
+                ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.NO_BILLING_PERIOD, baseSubscription.getStartDate()));
+        expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, "Shotgun", PhaseType.EVERGREEN,
+                ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, baseSubscription.getStartDate().plusDays(30)));
+        expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.CHANGE, "Pistol", PhaseType.EVERGREEN,
+                ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, bpChangeDate));
+        index = 0;
+        for (ExistingEvent e : expectedBP) {
+           validateExistingEventForAssertion(e, bpRepair.getExistingEvents().get(index++));           
+        }
+        
+        SubscriptionData newAoSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+        assertEquals(newAoSubscription.getState(), SubscriptionState.ACTIVE);
+        assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+        assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+
+        SubscriptionData newBaseSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+        assertEquals(newBaseSubscription.getState(), SubscriptionState.ACTIVE);
+        assertEquals(newBaseSubscription.getAllTransitions().size(), 2);
+        assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+        
+        dryRun = false;
+        BundleRepair realRunBundleRepair = repairApi.repairBundle(bundleRepair, dryRun, context);
+        
+        aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
+        assertEquals(aoRepair.getExistingEvents().size(), 3);
+
+        bpRepair = getSubscriptionRepair(baseSubscription.getId(), realRunBundleRepair);
+        assertEquals(bpRepair.getExistingEvents().size(), 3);        
+        
+        index = 0;
+        for (ExistingEvent e : expectedAO) {
+           validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));           
+        }
+        
+        index = 0;
+        for (ExistingEvent e : expectedBP) {
+           validateExistingEventForAssertion(e, bpRepair.getExistingEvents().get(index++));           
+        }
+
+        newAoSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+        assertEquals(newAoSubscription.getState(), SubscriptionState.CANCELLED);
+        assertEquals(newAoSubscription.getAllTransitions().size(), 3);
+        assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+            
+        newBaseSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+        assertEquals(newBaseSubscription.getState(), SubscriptionState.ACTIVE);
+        assertEquals(newBaseSubscription.getAllTransitions().size(), 3);
+        assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
     }
 
     @Test(groups={"slow"})
-    public void testRepairCancelBPWithAddons() throws Exception {
+    public void testRepairCancelBP_EOT_WithAddons() throws Exception {
+        
+        String baseProduct = "Shotgun";
+        BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+        String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+        // CREATE BP
+        SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+        // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
+        Duration someTimeLater = getDurationDay(3);
+        clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+
+        SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+        
+        // MOVE CLOCK A LITTLE BIT MORE -- AFTER TRIAL
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        someTimeLater = getDurationDay(40);
+        clock.addDeltaFromReality(someTimeLater);
+        assertTrue(testListener.isCompleted(7000));
+        
+        // SET CTD to BASE SUBSCRIPTION SP CANCEL OCCURS EOT
+        DateTime newChargedThroughDate = baseSubscription.getStartDate().plusDays(30).plusMonths(1);
+        billingApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate, context);
+        baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+
+        BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+        sortEventsOnBundle(bundleRepair);
+        
+        // Quick check
+        SubscriptionRepair bpRepair = getSubscriptionRepair(baseSubscription.getId(), bundleRepair);
+        assertEquals(bpRepair.getExistingEvents().size(), 2);
+        
+        SubscriptionRepair aoRepair = getSubscriptionRepair(aoSubscription.getId(), bundleRepair);
+        assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+        DateTime bpCancelDate = clock.getUTCNow().minusDays(1);
+        NewEvent ne = createNewEvent(SubscriptionTransitionType.CANCEL, bpCancelDate, null);
+        bpRepair = createSubscriptionReapir(baseSubscription.getId(), Collections.<SubscriptionRepair.DeletedEvent>emptyList(), Collections.singletonList(ne));
+        bundleRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(bpRepair));
+        
+        boolean dryRun = true;
+        BundleRepair dryRunBundleRepair = repairApi.repairBundle(bundleRepair, dryRun, context);
+                
+        aoRepair = getSubscriptionRepair(aoSubscription.getId(), dryRunBundleRepair);
+        assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+        bpRepair = getSubscriptionRepair(baseSubscription.getId(), dryRunBundleRepair);
+        assertEquals(bpRepair.getExistingEvents().size(), 3);        
         
+        // Check expected for AO
+        List<ExistingEvent> expectedAO = new LinkedList<SubscriptionRepair.ExistingEvent>();
+        expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Telescopic-Scope", PhaseType.DISCOUNT,
+                ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoSubscription.getStartDate()));
+        expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, "Telescopic-Scope", PhaseType.EVERGREEN,
+                ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, baseSubscription.getStartDate().plusMonths(1)));
+        /*
+        expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.CANCEL, null, null,
+                ProductCategory.ADD_ON, null, null, newChargedThroughDate));
+                */
+        int index = 0;
+        for (ExistingEvent e : expectedAO) {
+           validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));           
+        }
+
+        // Check expected for BP        
+        List<ExistingEvent> expectedBP = new LinkedList<SubscriptionRepair.ExistingEvent>();
+        expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Shotgun", PhaseType.TRIAL,
+                ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.NO_BILLING_PERIOD, baseSubscription.getStartDate()));
+        expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, "Shotgun", PhaseType.EVERGREEN,
+                ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, baseSubscription.getStartDate().plusDays(30)));
+        expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.CANCEL, null, null,
+                ProductCategory.BASE, null, null, newChargedThroughDate));
+        index = 0;
+        for (ExistingEvent e : expectedBP) {
+           validateExistingEventForAssertion(e, bpRepair.getExistingEvents().get(index++));           
+        }
+        
+        SubscriptionData newAoSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+        assertEquals(newAoSubscription.getState(), SubscriptionState.ACTIVE);
+        assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+        assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+
+        SubscriptionData newBaseSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+        assertEquals(newBaseSubscription.getState(), SubscriptionState.ACTIVE);
+        assertEquals(newBaseSubscription.getAllTransitions().size(), 2);
+        assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+        
+        dryRun = false;
+        BundleRepair realRunBundleRepair = repairApi.repairBundle(bundleRepair, dryRun, context);
+        
+        aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
+        assertEquals(aoRepair.getExistingEvents().size(), 3);
+
+        bpRepair = getSubscriptionRepair(baseSubscription.getId(), realRunBundleRepair);
+        assertEquals(bpRepair.getExistingEvents().size(), 3);        
+        
+        // STEPH BUG -- WE ONLY SEE THAT AFTER WE HIT DISK.
+        expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.CANCEL, null, null,
+                ProductCategory.ADD_ON, null, null, newChargedThroughDate));
+        index = 0;
+        for (ExistingEvent e : expectedAO) {
+           validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));           
+        }
+        
+        index = 0;
+        for (ExistingEvent e : expectedBP) {
+           validateExistingEventForAssertion(e, bpRepair.getExistingEvents().get(index++));           
+        }
+
+        newAoSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+        assertEquals(newAoSubscription.getState(), SubscriptionState.ACTIVE);
+        assertEquals(newAoSubscription.getAllTransitions().size(), 3);
+        assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+            
+        newBaseSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+        assertEquals(newBaseSubscription.getState(), SubscriptionState.ACTIVE);
+        assertEquals(newBaseSubscription.getAllTransitions().size(), 3);
+        assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+        
+        // MOVE CLOCK AFTER CANCEL DATE
+        testListener.pushExpectedEvent(NextEvent.CANCEL);
+        testListener.pushExpectedEvent(NextEvent.CANCEL);
+        someTimeLater = getDurationDay(32);
+        clock.addDeltaFromReality(someTimeLater);
+        assertTrue(testListener.isCompleted(7000));
+
+        newAoSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+        assertEquals(newAoSubscription.getState(), SubscriptionState.CANCELLED);
+        assertEquals(newAoSubscription.getAllTransitions().size(), 3);
+        assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+            
+        newBaseSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+        assertEquals(newBaseSubscription.getState(), SubscriptionState.CANCELLED);
+        assertEquals(newBaseSubscription.getAllTransitions().size(), 3);
+        assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
     }
 
     
@@ -126,7 +516,7 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Telescopic-Scope", PhaseType.DISCOUNT,
                 ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoSubscription.getStartDate()));
         expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CANCEL, null, null,
-                    ProductCategory.ADD_ON, null, null, aoCancelDate));
+                ProductCategory.ADD_ON, null, null, aoCancelDate));
         int index = 0;
         for (ExistingEvent e : expected) {
            validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));           
@@ -249,7 +639,7 @@ public class TestRepairWithAO extends TestApiBaseRepair {
     //
     // We are doing repair for multi-phase tiered-addon with different alignment:
     // Telescopic-Scope -> Laser-Scope
-    // Tiered ADON logix
+    // Tiered ADON logic
     // . Both multi phase
     // . Telescopic-Scope (bundle align) and Laser-Scope is Subscription align
     //
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithError.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithError.java
index 87f3cea..4e67a53 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithError.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithError.java
@@ -43,6 +43,7 @@ import com.ning.billing.entitlement.api.repair.SubscriptionRepair.DeletedEvent;
 import com.ning.billing.entitlement.api.repair.SubscriptionRepair.NewEvent;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.glue.MockEngineModuleMemory;
 
@@ -248,43 +249,180 @@ public class TestRepairWithError extends TestApiBaseRepair {
         }, ErrorCode.ENT_REPAIR_AO_CREATE_BEFORE_BP_START);
     }
     
-    @Test(groups={"fast"}, enabled=false)
+    @Test(groups={"fast"})
     public void testENT_REPAIR_NEW_EVENT_BEFORE_LAST_AO_REMAINING() throws Exception {
         test.withException(new TestWithExceptionCallback() {
             @Override
-            public void doTest() throws EntitlementRepairException {
+            public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
                 
-            }
-        }, ErrorCode.ENT_REPAIR_NEW_EVENT_BEFORE_LAST_AO_REMAINING);
-    }
 
-    @Test(groups={"fast"}, enabled=false)
-    public void testENT_REPAIR_MISSING_AO_DELETE_EVENT() throws Exception {
-        test.withException(new TestWithExceptionCallback() {
-            @Override
-            public void doTest() throws EntitlementRepairException {
+                // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
+                Duration someTimeLater = getDurationDay(3);
+                clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+
+                SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+
+                // MOVE CLOCK A LITTLE BIT MORE -- STILL IN TRIAL
+                clock.addDeltaFromReality(someTimeLater);
+
+                BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+                sortEventsOnBundle(bundleRepair);
+                
+                // Quick check
+                SubscriptionRepair bpRepair = getSubscriptionRepair(baseSubscription.getId(), bundleRepair);
+                assertEquals(bpRepair.getExistingEvents().size(), 2);
+                
+                SubscriptionRepair aoRepair = getSubscriptionRepair(aoSubscription.getId(), bundleRepair);
+                assertEquals(aoRepair.getExistingEvents().size(), 2);
+                
+
+                List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+                //des.add(createDeletedEvent(aoRepair.getExistingEvents().get(1).getEventId()));        
+                DateTime aoCancelDate = aoSubscription.getStartDate().plusDays(10);
                 
+                NewEvent ne = createNewEvent(SubscriptionTransitionType.CANCEL, aoCancelDate, null);
+                
+                SubscriptionRepair saoRepair = createSubscriptionReapir(aoSubscription.getId(), des, Collections.singletonList(ne));
+                
+                bundleRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(saoRepair));
+                
+                boolean dryRun = true;
+                repairApi.repairBundle(bundleRepair, dryRun, context);
             }
-        }, ErrorCode.ENT_REPAIR_MISSING_AO_DELETE_EVENT);
+        }, ErrorCode.ENT_REPAIR_NEW_EVENT_BEFORE_LAST_AO_REMAINING);
     }
 
-    @Test(groups={"fast"}, enabled=false)
+
+    @Test(groups={"fast"})
     public void testENT_REPAIR_BP_RECREATE_MISSING_AO() throws Exception {
         test.withException(new TestWithExceptionCallback() {
             @Override
-            public void doTest() throws EntitlementRepairException {
+            public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
+
+              //testListener.pushExpectedEvent(NextEvent.PHASE);
+
+                clock.setDeltaFromReality(getDurationDay(5), 0);
+                //assertTrue(testListener.isCompleted(5000));
+
+                SubscriptionData aoSubscription = createSubscription("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+                
+                BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+                sortEventsOnBundle(bundleRepair);
+                
+                DateTime newCreateTime = baseSubscription.getStartDate().plusDays(3);
+
+                PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL);
+
+                NewEvent ne = createNewEvent(SubscriptionTransitionType.CREATE, newCreateTime, spec);
+                List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+                des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(0).getEventId()));
+                des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(1).getEventId()));
+
+                SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
+                
+                // FIRST ISSUE DRY RUN
+                BundleRepair bRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
                 
+                boolean dryRun = true;
+                repairApi.repairBundle(bRepair, dryRun, context);
             }
         }, ErrorCode.ENT_REPAIR_BP_RECREATE_MISSING_AO);
     }
     
+    //
+    // CAN'T seem to trigger such case easily, other errors trigger before...
+    //
     @Test(groups={"fast"}, enabled=false)
     public void testENT_REPAIR_BP_RECREATE_MISSING_AO_CREATE() throws Exception {
         test.withException(new TestWithExceptionCallback() {
             @Override
-            public void doTest() throws EntitlementRepairException {
+            public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
+                /*
+              //testListener.pushExpectedEvent(NextEvent.PHASE);
+
+                clock.setDeltaFromReality(getDurationDay(5), 0);
+                //assertTrue(testListener.isCompleted(5000));
+
+                SubscriptionData aoSubscription = createSubscription("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
                 
+                BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+                sortEventsOnBundle(bundleRepair);
+                
+                DateTime newCreateTime = baseSubscription.getStartDate().plusDays(3);
+
+                PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL);
+
+                NewEvent ne = createNewEvent(SubscriptionTransitionType.CREATE, newCreateTime, spec);
+                List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+                des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(0).getEventId()));
+                des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(1).getEventId()));
+
+                SubscriptionRepair bpRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
+                
+                ne = createNewEvent(SubscriptionTransitionType.CANCEL, clock.getUTCNow().minusDays(1),  null);
+                SubscriptionRepair aoRepair = createSubscriptionReapir(aoSubscription.getId(), Collections.<SubscriptionRepair.DeletedEvent>emptyList(), Collections.singletonList(ne));
+                
+                
+                List<SubscriptionRepair> allRepairs = new LinkedList<SubscriptionRepair>();
+                allRepairs.add(bpRepair);
+                allRepairs.add(aoRepair);
+                bundleRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), allRepairs);
+                // FIRST ISSUE DRY RUN
+                BundleRepair bRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), allRepairs);
+                
+                boolean dryRun = true;
+                repairApi.repairBundle(bRepair, dryRun, context);
+                */
             }
         }, ErrorCode.ENT_REPAIR_BP_RECREATE_MISSING_AO_CREATE);
     }
+    
+    @Test(groups={"fast"}, enabled=false)
+    public void testENT_REPAIR_MISSING_AO_DELETE_EVENT() throws Exception {
+        test.withException(new TestWithExceptionCallback() {
+            @Override
+            public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
+
+                
+                /*
+                // MOVE CLOCK -- JUST BEFORE END OF TRIAL
+                clock.setDeltaFromReality(getDurationDay(29), 0);
+                
+                SubscriptionData aoSubscription = createSubscription("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+                
+                // MOVE CLOCK -- RIGHT OUT OF TRIAL
+                testListener.pushExpectedEvent(NextEvent.PHASE);                
+                clock.addDeltaFromReality(getDurationDay(5));
+                assertTrue(testListener.isCompleted(5000));
+
+                DateTime requestedChange = clock.getUTCNow();
+                baseSubscription.changePlan("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, requestedChange, context);
+
+                DateTime reapairTime = clock.getUTCNow().minusDays(1);
+
+                BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+                sortEventsOnBundle(bundleRepair);
+                
+                SubscriptionRepair bpRepair = getSubscriptionRepair(baseSubscription.getId(), bundleRepair);
+                SubscriptionRepair aoRepair = getSubscriptionRepair(aoSubscription.getId(), bundleRepair);
+
+                List<DeletedEvent> bpdes = new LinkedList<SubscriptionRepair.DeletedEvent>();
+                bpdes.add(createDeletedEvent(bpRepair.getExistingEvents().get(2).getEventId()));    
+                bpRepair = createSubscriptionReapir(baseSubscription.getId(), bpdes, Collections.<NewEvent>emptyList());
+                
+                NewEvent ne = createNewEvent(SubscriptionTransitionType.CANCEL, reapairTime, null);
+                aoRepair = createSubscriptionReapir(aoSubscription.getId(), Collections.<SubscriptionRepair.DeletedEvent>emptyList(), Collections.singletonList(ne));
+                
+                List<SubscriptionRepair> allRepairs = new LinkedList<SubscriptionRepair>();
+                allRepairs.add(bpRepair);
+                allRepairs.add(aoRepair);
+                bundleRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), allRepairs);
+                
+                boolean dryRun = false;
+                repairApi.repairBundle(bundleRepair, dryRun, context);
+                */
+                }
+        }, ErrorCode.ENT_REPAIR_MISSING_AO_DELETE_EVENT);
+    }
+
 }