killbill-aplcache

Details

diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index 6ab0eea..da22812 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -63,16 +63,17 @@ public enum ErrorCode {
     ENT_REPAIR_INVALID_DELETE_SET(1091, "Event %s is not deleted for subscription %s but prior events were"),
     ENT_REPAIR_NON_EXISTENT_DELETE_EVENT(1092, "Event %s does not exist for subscription %s"),    
     ENT_REPAIR_MISSING_AO_DELETE_EVENT(1093, "Event %s should be in deleted set for subscription %s because BP events got deleted earlier"),
-    ENT_REPAIR_NEW_AO_EVENT_BEFORE_BP(1094, "New event %s for subscription %s is before last remaining event for BP"),
-    ENT_REPAIR_INVALID_NEW_AO_EVENT(1095, "New event %s for subscription %s is before last remaining event"),
+    ENT_REPAIR_NEW_EVENT_BEFORE_LAST_BP_REMAINING(1094, "New event %s for subscription %s is before last remaining event for BP"),
+    ENT_REPAIR_NEW_EVENT_BEFORE_LAST_AO_REMAINING(1095, "New event %s for subscription %s is before last remaining event"),
     ENT_REPAIR_UNKNOWN_TYPE(1096, "Unknown new event type %s for subscription %s"),
     ENT_REPAIR_UNKNOWN_BUNDLE(1097, "Unknown bundle %s"), 
     ENT_REPAIR_UNKNOWN_SUBSCRIPTION(1098, "Unknown subscription %s"),     
     ENT_REPAIR_NO_ACTIVE_SUBSCRIPTIONS(1099, "No active subscriptions on bundle %s"),         
     ENT_REPAIR_VIEW_CHANGED(1100, "View for bundle %s has changed from %s to %s"),             
     ENT_REPAIR_SUB_RECREATE_NOT_EMPTY(1101, "Subscription %s with recreation for bundle %s should specify all existing events to be deleted"),    
-    ENT_REPAIR_BP_RECREATE_MISSING_AO(1102, "BP recreation for bundle %s implies repair all subscriptions"),    
-    ENT_REPAIR_BP_RECREATE_MISSING_AO_CREATE(1103, "BP recreation for bundle %s implies that all AO should be start also with a CREATE"),        
+    ENT_REPAIR_SUB_EMPTY(1102, "Subscription %s with recreation for bundle %s should specify all existing events to be deleted"),    
+    ENT_REPAIR_BP_RECREATE_MISSING_AO(1103, "BP recreation for bundle %s implies repair all subscriptions"),    
+    ENT_REPAIR_BP_RECREATE_MISSING_AO_CREATE(1104, "BP recreation for bundle %s implies that all AO should be start also with a CREATE"),        
     
     /*
     *
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 a982a26..d8315f7 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
@@ -212,9 +212,11 @@ public class PlanAligner  {
         Catalog catalog = catalogService.getFullCatalog();
         ProductCategory currentCategory = currentPlan.getProduct().getCategory();
         // STEPH tiered ADDON not implemented yet
+        /*
         if (currentCategory != ProductCategory.BASE) {
             throw new EntitlementError(String.format("Only implemented changePlan for BasePlan"));
         }
+        */
 
         PlanPhaseSpecifier fromPlanPhaseSpecifier = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
                 currentCategory,
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 23e55dc..6f2f3bb 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
@@ -15,6 +15,7 @@
  */
 package com.ning.billing.entitlement.api.repair;
 
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -117,64 +118,62 @@ public class DefaultEntitlementRepairApi implements EntitlementRepairApi {
 
                 SubscriptionRepair curRepair = findAndCreateSubscriptionRepair(cur.getId(), input.getSubscriptions());
                 if (curRepair != null) {
-                    SubscriptionDataRepair curData = ((SubscriptionDataRepair) cur);
-                    List<EntitlementEvent> remaining = getRemainingEventsAndValidateDeletedEvents(curData, firstDeletedBPEventTime, curRepair.getDeletedEvents());
-
-                    
+                    SubscriptionDataRepair curInputRepair = ((SubscriptionDataRepair) cur);
+                    final List<EntitlementEvent> remaining = getRemainingEventsAndValidateDeletedEvents(curInputRepair, firstDeletedBPEventTime, curRepair.getDeletedEvents());
                     
-                    boolean isPlanRecreate = (curRepair.getNewEvents().size() > 0 
+                    final boolean isPlanRecreate = (curRepair.getNewEvents().size() > 0 
                             && (curRepair.getNewEvents().get(0).getSubscriptionTransitionType() == SubscriptionTransitionType.CREATE 
                                     || curRepair.getNewEvents().get(0).getSubscriptionTransitionType() == SubscriptionTransitionType.RE_CREATE));
                     
-                    DateTime newSubscriptionStartDate = isPlanRecreate ? curRepair.getNewEvents().get(0).getRequestedDate() : null;
+                    final DateTime newSubscriptionStartDate = isPlanRecreate ? curRepair.getNewEvents().get(0).getRequestedDate() : null;
                     
                     if (isPlanRecreate && remaining.size() != 0) {
                         throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_SUB_RECREATE_NOT_EMPTY, cur.getId(), cur.getBundleId());
                     }
                     
+                    if (!isPlanRecreate && remaining.size() == 0) {
+                        throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_SUB_EMPTY, cur.getId(), cur.getBundleId());
+                    }
+                    
                     if (cur.getCategory() == ProductCategory.BASE) {
 
                         int bpTransitionSize =((SubscriptionData) cur).getAllTransitions().size();
-                        lastRemainingBPEventTime = (remaining.size() > 0) ? curData.getAllTransitions().get(remaining.size() - 1).getEffectiveTransitionTime() : null;
-                        firstDeletedBPEventTime =  (remaining.size() < bpTransitionSize) ? curData.getAllTransitions().get(remaining.size()).getEffectiveTransitionTime() : null;
+                        lastRemainingBPEventTime = (remaining.size() > 0) ? curInputRepair.getAllTransitions().get(remaining.size() - 1).getEffectiveTransitionTime() : null;
+                        firstDeletedBPEventTime =  (remaining.size() < bpTransitionSize) ? curInputRepair.getAllTransitions().get(remaining.size()).getEffectiveTransitionTime() : null;
 
                         isBasePlanRecreate = isPlanRecreate;
                         newBundleStartDate = newSubscriptionStartDate;
                     }
 
                     if (curRepair.getNewEvents().size() > 0) {
-                        DateTime lastRemainingEventTime = (remaining.size() == 0) ? null : curData.getAllTransitions().get(remaining.size() - 1).getEffectiveTransitionTime();
-                        validateFirstNewEvent(curData, curRepair.getNewEvents().get(0), lastRemainingBPEventTime, lastRemainingEventTime);
+                        DateTime lastRemainingEventTime = (remaining.size() == 0) ? null : curInputRepair.getAllTransitions().get(remaining.size() - 1).getEffectiveTransitionTime();
+                        validateFirstNewEvent(curInputRepair, curRepair.getNewEvents().get(0), lastRemainingBPEventTime, lastRemainingEventTime);
                     }
 
 
-                    SubscriptionDataRepair sRepair = createSubscriptionDataRepair(curData, newBundleStartDate, newSubscriptionStartDate, remaining);
-                    repairDao.initializeRepair(curData.getId(), remaining);
-                    inRepair.add(sRepair);
-                    if (sRepair.getCategory() == ProductCategory.ADD_ON) {
-                        addOnSubscriptionInRepair.add(sRepair);
-                    } else if (sRepair.getCategory() == ProductCategory.BASE) {
-                        baseSubscriptionRepair = sRepair;
+                    SubscriptionDataRepair curOutputRepair = createSubscriptionDataRepair(curInputRepair, newBundleStartDate, newSubscriptionStartDate, remaining);
+                    repairDao.initializeRepair(curInputRepair.getId(), remaining);
+                    inRepair.add(curOutputRepair);
+                    if (curOutputRepair.getCategory() == ProductCategory.ADD_ON) {
+                        addOnSubscriptionInRepair.add(curOutputRepair);
+                    } else if (curOutputRepair.getCategory() == ProductCategory.BASE) {
+                        baseSubscriptionRepair = curOutputRepair;
                     }
                 }
             }
+            
+            // 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());
+            }
 
             validateBasePlanRecreate(isBasePlanRecreate, subscriptions, input.getSubscriptions());
             validateInputSubscriptionsKnown(subscriptions, input.getSubscriptions());
 
-            TreeSet<NewEvent> newEventSet = new TreeSet<SubscriptionRepair.NewEvent>(new Comparator<NewEvent>() {
-                @Override
-                public int compare(NewEvent o1, NewEvent o2) {
-                    return o1.getRequestedDate().compareTo(o2.getRequestedDate());
-                }
-            });
-            for (SubscriptionRepair cur : input.getSubscriptions()) {
-                for (NewEvent e : cur.getNewEvents()) {
-                    newEventSet.add(new DefaultNewEvent(cur.getId(), e.getPlanPhaseSpecifier(), e.getRequestedDate(), e.getSubscriptionTransitionType()));    
-                }
-            }
 
-            Iterator<NewEvent> it = newEventSet.iterator();
+            Collection<NewEvent> newEvents = createOrderedNewEventInput(input.getSubscriptions());
+            Iterator<NewEvent> it = newEvents.iterator();
             while (it.hasNext()) {
                 DefaultNewEvent cur = (DefaultNewEvent) it.next();
                 SubscriptionDataRepair curDataRepair = findSubscriptionDataRepair(cur.getSubscriptionId(), inRepair);
@@ -237,14 +236,30 @@ public class DefaultEntitlementRepairApi implements EntitlementRepairApi {
     throws EntitlementRepairException {
         if (lastBPRemainingTime != null &&
                 firstNewEvent.getRequestedDate().isBefore(lastBPRemainingTime)) {
-            throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NEW_AO_EVENT_BEFORE_BP, firstNewEvent.getPlanPhaseSpecifier().toString(), data.getId());
+            throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NEW_EVENT_BEFORE_LAST_BP_REMAINING, firstNewEvent.getPlanPhaseSpecifier().toString(), data.getId());
         }
         if (lastRemainingTime != null &&
                 firstNewEvent.getRequestedDate().isBefore(lastRemainingTime)) {
-            throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_INVALID_NEW_AO_EVENT, firstNewEvent.getPlanPhaseSpecifier().toString(), data.getId());
+            throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NEW_EVENT_BEFORE_LAST_AO_REMAINING, firstNewEvent.getPlanPhaseSpecifier().toString(), data.getId());
         }
 
     }
+    
+    private Collection<NewEvent> createOrderedNewEventInput(List<SubscriptionRepair> subscriptionsReapir) {
+        TreeSet<NewEvent> newEventSet = new TreeSet<SubscriptionRepair.NewEvent>(new Comparator<NewEvent>() {
+            @Override
+            public int compare(NewEvent o1, NewEvent o2) {
+                return o1.getRequestedDate().compareTo(o2.getRequestedDate());
+            }
+        });
+        for (SubscriptionRepair cur : subscriptionsReapir) {
+            for (NewEvent e : cur.getNewEvents()) {
+                newEventSet.add(new DefaultNewEvent(cur.getId(), e.getPlanPhaseSpecifier(), e.getRequestedDate(), e.getSubscriptionTransitionType()));    
+            }
+        }
+        return newEventSet;
+    }
+
 
     private List<EntitlementEvent> getRemainingEventsAndValidateDeletedEvents(final SubscriptionDataRepair data, final DateTime firstBPDeletedTime,
             final List<SubscriptionRepair.DeletedEvent> deletedEvents) 
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 1df8ee2..b983b55 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
@@ -70,9 +70,11 @@ public class SubscriptionDataRepair extends SubscriptionData {
             case CREATE:
             case RE_CREATE:
                 recreate(spec, input.getRequestedDate(), context);
+                checkAddonRights(baseSubscription);
                 break;
             case CHANGE:
                 changePlan(spec.getProductName(), spec.getBillingPeriod(), spec.getPriceListName(), input.getRequestedDate(), context);
+                checkAddonRights(baseSubscription);
                 break;
             case CANCEL:
                 cancel(input.getRequestedDate(), false, context);
@@ -82,9 +84,7 @@ public class SubscriptionDataRepair extends SubscriptionData {
             default:
                 throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_UNKNOWN_TYPE, input.getSubscriptionTransitionType(), id);
             }
-            
             trickleDownBPEffectForAddon(addonSubscriptions, input.getRequestedDate(), context);
-            checkAddonRights(baseSubscription);
             
         } catch (EntitlementUserApiException e) {
             throw new EntitlementRepairException(e);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestApiBaseRepair.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestApiBaseRepair.java
new file mode 100644
index 0000000..9c6244d
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestApiBaseRepair.java
@@ -0,0 +1,195 @@
+/* 
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.testng.Assert;
+
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+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.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.user.EntitlementUserApiException;
+
+
+public abstract class TestApiBaseRepair extends TestApiBase {
+
+
+    public interface TestWithExceptionCallback {
+        public void doTest() throws EntitlementRepairException, EntitlementUserApiException;
+    }
+    
+    public static class TestWithException {
+        public void withException(TestWithExceptionCallback callback, ErrorCode code) throws Exception {
+            try {
+                callback.doTest();
+                Assert.fail("Failed to catch exception " + code);
+            } catch (EntitlementRepairException e) {
+                assertEquals(e.getCode(), code.getCode());
+            }
+        }
+    }
+
+    
+    protected SubscriptionRepair createSubscriptionReapir(final UUID id, final List<DeletedEvent> deletedEvents, final List<NewEvent> newEvents) {
+        return new SubscriptionRepair() {
+            @Override
+            public UUID getId() {
+                return id;
+            }
+            @Override
+            public List<NewEvent> getNewEvents() {
+                return newEvents;
+            }
+            @Override
+            public List<ExistingEvent> getExistingEvents() {
+                return null;
+            }
+            @Override
+            public List<DeletedEvent> getDeletedEvents() {
+                return deletedEvents;
+            }
+        };
+    }
+
+    protected BundleRepair createBundleRepair(final UUID bundleId, final String viewId, final List<SubscriptionRepair> subscriptionRepair) {
+        return new BundleRepair() {
+            @Override
+            public String getViewId() {
+                return viewId;
+            }
+            @Override
+            public List<SubscriptionRepair> getSubscriptions() {
+                return subscriptionRepair;
+            }
+            @Override
+            public UUID getBundleId() {
+                return bundleId;
+            }
+        };
+    }
+
+    protected ExistingEvent createExistingEventForAssertion(final SubscriptionTransitionType type, 
+            final String productName, final PhaseType phaseType, final ProductCategory category, final String priceListName, final BillingPeriod billingPeriod,
+            final DateTime effectiveDateTime) {
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, category, billingPeriod, priceListName, phaseType);
+        ExistingEvent ev = new ExistingEvent() {
+            @Override
+            public SubscriptionTransitionType getSubscriptionTransitionType() {
+                return type;
+            }
+             @Override
+            public DateTime getRequestedDate() {
+                 return null;
+            }
+            @Override
+            public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+                return spec;
+            }
+            @Override
+            public UUID getEventId() {
+                return null;
+            }
+            @Override
+            public DateTime getEffectiveDate() {
+                return effectiveDateTime;
+            }
+        };
+        return ev;
+    }
+    
+    protected void validateExistingEventForAssertion(final ExistingEvent expected, final ExistingEvent input) {
+        assertEquals(input.getPlanPhaseSpecifier().getProductName(), expected.getPlanPhaseSpecifier().getProductName());
+        assertEquals(input.getPlanPhaseSpecifier().getPhaseType(), expected.getPlanPhaseSpecifier().getPhaseType());
+        assertEquals(input.getPlanPhaseSpecifier().getProductCategory(), expected.getPlanPhaseSpecifier().getProductCategory());                    
+        assertEquals(input.getPlanPhaseSpecifier().getPriceListName(), expected.getPlanPhaseSpecifier().getPriceListName());                    
+        assertEquals(input.getPlanPhaseSpecifier().getBillingPeriod(), expected.getPlanPhaseSpecifier().getBillingPeriod());
+        assertEquals(input.getEffectiveDate(), expected.getEffectiveDate());        
+    }
+    
+    protected DeletedEvent createDeletedEvent(final UUID eventId) {
+        return new DeletedEvent() {
+            @Override
+            public UUID getEventId() {
+                return eventId;
+            }
+        };
+    }
+
+    protected NewEvent createNewEvent(final SubscriptionTransitionType type, final DateTime requestedDate, final PlanPhaseSpecifier spec) {
+
+        return new NewEvent() {
+            @Override
+            public SubscriptionTransitionType getSubscriptionTransitionType() {
+                return type;
+            }
+            @Override
+            public DateTime getRequestedDate() {
+                return requestedDate;
+            }
+            @Override
+            public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+                return spec;
+            }
+        };
+    }
+
+    protected void sortEventsOnBundle(final BundleRepair bundle) {
+        if (bundle.getSubscriptions() == null) {
+            return;
+        }
+        for (SubscriptionRepair cur : bundle.getSubscriptions()) {
+            if (cur.getExistingEvents() != null) {
+                sortExistingEvent(cur.getExistingEvents());
+            }
+            if (cur.getNewEvents() != null) {
+                sortNewEvent(cur.getNewEvents());
+            }
+        }
+    }
+
+    protected void sortExistingEvent(final List<ExistingEvent> events) {
+        Collections.sort(events, new Comparator<ExistingEvent>() {
+            @Override
+            public int compare(ExistingEvent arg0, ExistingEvent arg1) {
+                return arg0.getEffectiveDate().compareTo(arg1.getEffectiveDate());
+            }
+        });
+    }
+    protected void sortNewEvent(final List<NewEvent> events) {
+        Collections.sort(events, new Comparator<NewEvent>() {
+            @Override
+            public int compare(NewEvent arg0, NewEvent arg1) {
+                return arg0.getRequestedDate().compareTo(arg1.getRequestedDate());
+            }
+        });
+    }
+
+}
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
new file mode 100644
index 0000000..24955be
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithAO.java
@@ -0,0 +1,341 @@
+/* 
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api.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;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Duration;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+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.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.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionEvents;
+import com.ning.billing.entitlement.glue.MockEngineModuleSql;
+
+public class TestRepairWithAO extends TestApiBaseRepair {
+
+    
+
+    @Override
+    public Injector getInjector() {
+        return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
+    }
+
+    @Test(groups={"slow"})
+    public void testRepairCancelAO() 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 -- 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(1);
+        
+        NewEvent ne = createNewEvent(SubscriptionTransitionType.CANCEL, aoCancelDate, null);
+        
+        SubscriptionRepair saoRepair = createSubscriptionReapir(aoSubscription.getId(), des, Collections.singletonList(ne));
+        
+        BundleRepair bRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(saoRepair));
+        
+        boolean dryRun = true;
+        BundleRepair dryRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+        
+        aoRepair = getSubscriptionRepair(aoSubscription.getId(), dryRunBundleRepair);
+        assertEquals(aoRepair.getExistingEvents().size(), 2);
+        
+        
+        List<ExistingEvent> expected = new LinkedList<SubscriptionRepair.ExistingEvent>();
+        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));
+        int index = 0;
+        for (ExistingEvent e : expected) {
+           validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));           
+        }
+        SubscriptionData newAoSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+        assertEquals(newAoSubscription.getState(), SubscriptionState.ACTIVE);
+        assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+        
+        dryRun = false;
+        BundleRepair realRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+        
+        aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
+        assertEquals(aoRepair.getExistingEvents().size(), 2);
+        index = 0;
+        for (ExistingEvent e : expected) {
+           validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));           
+        }
+        
+        newAoSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+        assertEquals(newAoSubscription.getState(), SubscriptionState.CANCELLED);
+        assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+    }
+    
+    
+    @Test(groups={"slow"})
+    public void testRepairRecreateAO() 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 -- 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(0).getEventId()));        
+        des.add(createDeletedEvent(aoRepair.getExistingEvents().get(1).getEventId()));
+
+        DateTime aoRecreateDate = aoSubscription.getStartDate().plusDays(1);
+        PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.DISCOUNT);
+        NewEvent ne = createNewEvent(SubscriptionTransitionType.CREATE, aoRecreateDate, spec);
+        
+        SubscriptionRepair saoRepair = createSubscriptionReapir(aoSubscription.getId(), des, Collections.singletonList(ne));
+        
+        BundleRepair bRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(saoRepair));
+        
+        boolean dryRun = true;
+        BundleRepair dryRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+        
+        aoRepair = getSubscriptionRepair(aoSubscription.getId(), dryRunBundleRepair);
+        assertEquals(aoRepair.getExistingEvents().size(), 2);
+        
+        
+        List<ExistingEvent> expected = new LinkedList<SubscriptionRepair.ExistingEvent>();
+        expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Telescopic-Scope", PhaseType.DISCOUNT,
+                ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoRecreateDate));
+        expected.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, "Telescopic-Scope", PhaseType.EVERGREEN,
+                    ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, baseSubscription.getStartDate().plusMonths(1) /* Bundle align */));
+        int index = 0;
+        for (ExistingEvent e : expected) {
+           validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));           
+        }
+        SubscriptionData newAoSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+        assertEquals(newAoSubscription.getState(), SubscriptionState.ACTIVE);
+        assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+        assertEquals(newAoSubscription.getStartDate(), aoSubscription.getStartDate());
+        assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);        
+        
+        // NOW COMMIT
+        dryRun = false;
+        BundleRepair realRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+        
+        aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
+        assertEquals(aoRepair.getExistingEvents().size(), 2);
+        index = 0;
+        for (ExistingEvent e : expected) {
+           validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));           
+        }
+        
+        newAoSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+        assertEquals(newAoSubscription.getState(), SubscriptionState.ACTIVE);
+        assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+        assertEquals(newAoSubscription.getStartDate(), aoRecreateDate);
+        assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);        
+
+    }
+    
+    // Fasten your seatbelt here:
+    //
+    // We are doing repair for multi-phase tiered-addon with different alignment:
+    // Telescopic-Scope -> Laser-Scope
+    // Tiered ADON logix
+    // . Both multi phase
+    // . Telescopic-Scope (bundle align) and Laser-Scope is Subscription align
+    //
+    @Test(groups={"slow"})
+    public void testRepairChangeAOOK() 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 -- 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 aoChangeDate = aoSubscription.getStartDate().plusDays(1);
+        PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Laser-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL);
+        NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, aoChangeDate, spec);
+        
+        SubscriptionRepair saoRepair = createSubscriptionReapir(aoSubscription.getId(), des, Collections.singletonList(ne));
+        
+        BundleRepair bRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(saoRepair));
+        
+        boolean dryRun = true;
+        BundleRepair dryRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+        
+        aoRepair = getSubscriptionRepair(aoSubscription.getId(), dryRunBundleRepair);
+        assertEquals(aoRepair.getExistingEvents().size(), 3);
+        
+        
+        List<ExistingEvent> expected = new LinkedList<SubscriptionRepair.ExistingEvent>();
+        expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Telescopic-Scope", PhaseType.DISCOUNT,
+                ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoSubscription.getStartDate()));
+        expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CHANGE, "Laser-Scope", PhaseType.DISCOUNT,
+                ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoChangeDate));
+        expected.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, "Laser-Scope", PhaseType.EVERGREEN,
+                ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY,
+                aoSubscription.getStartDate().plusMonths(1) /* Subscription alignment */));
+                
+        int index = 0;
+        for (ExistingEvent e : expected) {
+           validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));           
+        }
+        SubscriptionData newAoSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+        assertEquals(newAoSubscription.getState(), SubscriptionState.ACTIVE);
+        assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+        
+        // AND NOW COMMIT
+        dryRun = false;
+        BundleRepair realRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+        
+        aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
+        assertEquals(aoRepair.getExistingEvents().size(), 3);
+        index = 0;
+        for (ExistingEvent e : expected) {
+           validateExistingEventForAssertion(e, aoRepair.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);
+        assertEquals(newAoSubscription.getBundleId(), bundle.getId());
+        assertEquals(newAoSubscription.getStartDate(), aoSubscription.getStartDate());
+
+        Plan currentPlan = newAoSubscription.getCurrentPlan();
+        assertNotNull(currentPlan);
+        assertEquals(currentPlan.getProduct().getName(), "Laser-Scope");
+        assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.ADD_ON);
+        assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.MONTHLY);
+
+        PlanPhase currentPhase = newAoSubscription.getCurrentPhase();
+        assertNotNull(currentPhase);
+        assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
+        
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        someTimeLater = getDurationDay(60);
+        clock.addDeltaFromReality(someTimeLater);
+        assertTrue(testListener.isCompleted(5000));
+        
+        newAoSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+        currentPhase = newAoSubscription.getCurrentPhase();
+        assertNotNull(currentPhase);
+        assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
+    }
+    
+    
+    private SubscriptionRepair getSubscriptionRepair(final UUID id, final BundleRepair bundleRepair) {
+        for (SubscriptionRepair cur : bundleRepair.getSubscriptions()) {
+            if (cur.getId().equals(id)) {
+                return cur;
+            }
+        }
+        Assert.fail("Failed to find SubscriptionReapir " + id);
+        return null;
+    }
+}
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
new file mode 100644
index 0000000..385cb56
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithError.java
@@ -0,0 +1,245 @@
+/* 
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Duration;
+import com.ning.billing.catalog.api.PhaseType;
+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.ApiTestListener.NextEvent;
+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.glue.MockEngineModuleMemory;
+
+public class TestRepairWithError extends TestApiBaseRepair {
+
+    private static final String baseProduct = "Shotgun";
+    private TestWithException test;
+    private Subscription baseSubscription;
+    private DateTime startDate;
+    @Override
+    public Injector getInjector() {
+        return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleMemory());
+    }
+
+
+    @BeforeMethod(groups={"fast"})
+    public void beforeMethod() throws Exception {
+        test = new TestWithException();
+        startDate = clock.getUTCNow();
+        baseSubscription = createSubscription(baseProduct, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, startDate);
+        testListener.reset();
+        clock.resetDeltaFromReality();
+    }
+  
+    @Test(groups={"fast"})
+    public void testENT_REPAIR_NEW_EVENT_BEFORE_LAST_BP_REMAINING() throws Exception {
+        test.withException(new TestWithExceptionCallback() {
+            @Override
+            public void doTest() throws EntitlementRepairException {
+
+                // MOVE AFTER TRIAL
+                testListener.pushExpectedEvent(NextEvent.PHASE);
+                Duration durationShift = getDurationDay(40);
+                clock.setDeltaFromReality(durationShift, 0);
+                assertTrue(testListener.isCompleted(5000));
+                
+                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, baseSubscription.getStartDate().plusDays(10), spec);
+                
+                SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), Collections.<DeletedEvent>emptyList(), Collections.singletonList(ne));
+                
+                BundleRepair bRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+
+                repairApi.repairBundle(bRepair, true, context);
+            }
+        }, ErrorCode.ENT_REPAIR_NEW_EVENT_BEFORE_LAST_BP_REMAINING);
+    }
+    
+    @Test(groups={"fast"})
+    public void testENT_REPAIR_INVALID_DELETE_SET() throws Exception {
+        test.withException(new TestWithExceptionCallback() {
+            @Override
+            public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
+
+                Duration durationShift = getDurationDay(3);
+                clock.setDeltaFromReality(durationShift, 0);
+                
+                testListener.pushExpectedEvent(NextEvent.CHANGE);
+                DateTime changeTime = clock.getUTCNow();
+                baseSubscription.changePlan("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, changeTime, context);
+                assertTrue(testListener.isCompleted(5000));
+                
+                // MOVE AFTER TRIAL
+                testListener.pushExpectedEvent(NextEvent.PHASE);
+                durationShift = getDurationDay(40);
+                clock.addDeltaFromReality(durationShift);
+                assertTrue(testListener.isCompleted(5000));
+                
+                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, baseSubscription.getStartDate().plusDays(10), spec);
+                DeletedEvent de = createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(1).getEventId());                
+
+                SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), Collections.singletonList(de), Collections.singletonList(ne));
+                BundleRepair bRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+
+                repairApi.repairBundle(bRepair, true, context);
+            }
+        }, ErrorCode.ENT_REPAIR_INVALID_DELETE_SET);
+    }
+
+    @Test(groups={"fast"})
+    public void testENT_REPAIR_NON_EXISTENT_DELETE_EVENT() throws Exception {
+        test.withException(new TestWithExceptionCallback() {
+            @Override
+            public void doTest() throws EntitlementRepairException {
+                
+                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, baseSubscription.getStartDate().plusDays(10), spec);
+                DeletedEvent de = createDeletedEvent(UUID.randomUUID());
+                SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), Collections.singletonList(de), Collections.singletonList(ne));
+                
+                BundleRepair bRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+
+                repairApi.repairBundle(bRepair, true, context);
+            }
+        }, ErrorCode.ENT_REPAIR_NON_EXISTENT_DELETE_EVENT);
+    }
+    
+    @Test(groups={"fast"})
+    public void testENT_REPAIR_SUB_RECREATE_NOT_EMPTY() throws Exception {
+        test.withException(new TestWithExceptionCallback() {
+            @Override
+            public void doTest() throws EntitlementRepairException {
+                
+                // MOVE AFTER TRIAL
+                   testListener.pushExpectedEvent(NextEvent.PHASE);
+                   Duration durationShift = getDurationDay(40);
+                   clock.setDeltaFromReality(durationShift, 0);
+                   assertTrue(testListener.isCompleted(5000));
+                   
+                   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.CREATE, baseSubscription.getStartDate().plusDays(10), spec);
+                   List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+                   des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(1).getEventId()));                
+                   SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
+                   
+                   BundleRepair bRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+
+                   repairApi.repairBundle(bRepair, true, context);
+                
+            }
+        }, ErrorCode.ENT_REPAIR_SUB_RECREATE_NOT_EMPTY);
+    }
+
+    @Test(groups={"fast"})
+    public void testENT_REPAIR_SUB_EMPTY() throws Exception {
+        test.withException(new TestWithExceptionCallback() {
+            @Override
+            public void doTest() throws EntitlementRepairException {
+                
+             // MOVE AFTER TRIAL
+                testListener.pushExpectedEvent(NextEvent.PHASE);
+                Duration durationShift = getDurationDay(40);
+                clock.setDeltaFromReality(durationShift, 0);
+                assertTrue(testListener.isCompleted(5000));
+                
+                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, baseSubscription.getStartDate().plusDays(10), 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));
+                
+                BundleRepair bRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+
+                repairApi.repairBundle(bRepair, true, context);
+            }
+        }, ErrorCode.ENT_REPAIR_SUB_EMPTY);
+    }
+    
+    @Test(groups={"fast"}, enabled=false)
+    public void testENT_REPAIR_NEW_EVENT_BEFORE_LAST_AO_REMAINING() throws Exception {
+        test.withException(new TestWithExceptionCallback() {
+            @Override
+            public void doTest() throws EntitlementRepairException {
+                
+            }
+        }, 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 {
+                
+            }
+        }, ErrorCode.ENT_REPAIR_MISSING_AO_DELETE_EVENT);
+    }
+
+  
+    @Test(groups={"fast"}, enabled=false)
+    public void testENT_REPAIR_BP_RECREATE_MISSING_AO() throws Exception {
+        test.withException(new TestWithExceptionCallback() {
+            @Override
+            public void doTest() throws EntitlementRepairException {
+                
+            }
+        }, ErrorCode.ENT_REPAIR_BP_RECREATE_MISSING_AO);
+    }
+    
+    @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 {
+                
+            }
+        }, ErrorCode.ENT_REPAIR_BP_RECREATE_MISSING_AO_CREATE);
+    }
+}
diff --git a/entitlement/src/test/resources/testInput.xml b/entitlement/src/test/resources/testInput.xml
index 8a97d57..bdc73db 100644
--- a/entitlement/src/test/resources/testInput.xml
+++ b/entitlement/src/test/resources/testInput.xml
@@ -1,41 +1,21 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!--
-  ~ Copyright 2010-2011 Ning, Inc.
-  ~
-  ~ Ning licenses this file to you under the Apache License, version 2.0
-  ~ (the "License"); you may not use this file except in compliance with the
-  ~ License.  You may obtain a copy of the License at:
-  ~
-  ~    http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-  ~ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
-  ~ License for the specific language governing permissions and limitations
-  ~ under the License.
-  -->
+<!-- ~ Copyright 2010-2011 Ning, Inc. ~ ~ Ning licenses this file to you 
+	under the Apache License, version 2.0 ~ (the "License"); you may not use 
+	this file except in compliance with the ~ License. You may obtain a copy 
+	of the License at: ~ ~ http://www.apache.org/licenses/LICENSE-2.0 ~ ~ Unless 
+	required by applicable law or agreed to in writing, software ~ distributed 
+	under the License is distributed on an "AS IS" BASIS, WITHOUT ~ WARRANTIES 
+	OR CONDITIONS OF ANY KIND, either express or implied. See the ~ License for 
+	the specific language governing permissions and limitations ~ under the License. -->
 
-<!-- 
-Use cases covered so far:
-	Tiered Product (Pistol/Shotgun/Assault-Rifle)
-	Multiple changeEvent plan policies
-	Multiple PlanAlignment (see below, trial add-on alignments and rescue discount package)
-	Product transition rules
-	Add on (Scopes, Hoster)
-	Multi-pack addon (Extra-Ammo)
-	Addon Trial aligned to base plan (holster-monthly-regular)
-	Addon Trial aligned to creation (holster-monthly-special)
-	Rescue discount package (assault-rifle-annual-rescue)
-	Plan phase with a reccurring and a one off (refurbish-maintenance)
-	Phan with more than 2 phase (gunclub discount plans)
-		
-Use Cases to do:
-	Tiered Add On
-	Riskfree period
-	
-
-
- -->
+<!-- Use cases covered so far: Tiered Product (Pistol/Shotgun/Assault-Rifle) 
+	Multiple changeEvent plan policies Multiple PlanAlignment (see below, trial 
+	add-on alignments and rescue discount package) Product transition rules Add 
+	on (Scopes, Hoster) Multi-pack addon (Extra-Ammo) Addon Trial aligned to 
+	base plan (holster-monthly-regular) Addon Trial aligned to creation (holster-monthly-special) 
+	Rescue discount package (assault-rifle-annual-rescue) Plan phase with a reccurring 
+	and a one off (refurbish-maintenance) Phan with more than 2 phase (gunclub 
+	discount plans) Use Cases to do: Tiered Add On Riskfree period -->
 <catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 	xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
 
@@ -47,21 +27,21 @@ Use Cases to do:
 		<currency>EUR</currency>
 		<currency>GBP</currency>
 	</currencies>
-	
+
 	<products>
 		<product name="Pistol">
 			<category>BASE</category>
 		</product>
 		<product name="Shotgun">
 			<category>BASE</category>
-            <available>
-                <addonProduct>Telescopic-Scope</addonProduct>
-                <addonProduct>Laser-Scope</addonProduct>
-            </available>
+			<available>
+				<addonProduct>Telescopic-Scope</addonProduct>
+				<addonProduct>Laser-Scope</addonProduct>
+			</available>
 		</product>
 		<product name="Assault-Rifle">
 			<category>BASE</category>
-			<included> 
+			<included>
 				<addonProduct>Telescopic-Scope</addonProduct>
 			</included>
 			<available>
@@ -84,37 +64,37 @@ Use Cases to do:
 			<category>ADD_ON</category>
 		</product>
 	</products>
-	 
+
 	<rules>
 		<changePolicy>
-			<changePolicyCase> 
+			<changePolicyCase>
 				<phaseType>TRIAL</phaseType>
 				<policy>IMMEDIATE</policy>
 			</changePolicyCase>
-            <changePolicyCase> 
-                <toProduct>Assault-Rifle</toProduct>
-                <policy>IMMEDIATE</policy>
-            </changePolicyCase>
-            <changePolicyCase> 
-                <fromProduct>Pistol</fromProduct>            
-                <toProduct>Shotgun</toProduct>
-                <policy>IMMEDIATE</policy>
-            </changePolicyCase>
-			<changePolicyCase> 
+			<changePolicyCase>
+				<toProduct>Assault-Rifle</toProduct>
+				<policy>IMMEDIATE</policy>
+			</changePolicyCase>
+			<changePolicyCase>
+				<fromProduct>Pistol</fromProduct>
+				<toProduct>Shotgun</toProduct>
+				<policy>IMMEDIATE</policy>
+			</changePolicyCase>
+			<changePolicyCase>
 				<toPriceList>rescue</toPriceList>
 				<policy>END_OF_TERM</policy>
 			</changePolicyCase>
-			<changePolicyCase> 
+			<changePolicyCase>
 				<fromBillingPeriod>MONTHLY</fromBillingPeriod>
 				<toBillingPeriod>ANNUAL</toBillingPeriod>
 				<policy>IMMEDIATE</policy>
 			</changePolicyCase>
-			<changePolicyCase> 
+			<changePolicyCase>
 				<fromBillingPeriod>ANNUAL</fromBillingPeriod>
 				<toBillingPeriod>MONTHLY</toBillingPeriod>
 				<policy>END_OF_TERM</policy>
 			</changePolicyCase>
-			<changePolicyCase> 
+			<changePolicyCase>
 				<policy>END_OF_TERM</policy>
 			</changePolicyCase>
 		</changePolicy>
@@ -128,27 +108,27 @@ Use Cases to do:
 				<toPriceList>rescue</toPriceList>
 				<alignment>CHANGE_OF_PRICELIST</alignment>
 			</changeAlignmentCase>
-            <changeAlignmentCase>
-                <alignment>START_OF_SUBSCRIPTION</alignment>
-            </changeAlignmentCase>
+			<changeAlignmentCase>
+				<alignment>START_OF_SUBSCRIPTION</alignment>
+			</changeAlignmentCase>
 		</changeAlignment>
 		<cancelPolicy>
 			<cancelPolicyCase>
 				<phaseType>TRIAL</phaseType>
 				<policy>IMMEDIATE</policy>
 			</cancelPolicyCase>
-            <cancelPolicyCase>
-                <policy>END_OF_TERM</policy>
-            </cancelPolicyCase>
+			<cancelPolicyCase>
+				<policy>END_OF_TERM</policy>
+			</cancelPolicyCase>
 		</cancelPolicy>
 		<createAlignment>
-		    <createAlignmentCase>
-		        <product>Laser-Scope</product>
-                <alignment>START_OF_SUBSCRIPTION</alignment>
-            </createAlignmentCase>
-            <createAlignmentCase>
-                <alignment>START_OF_BUNDLE</alignment>
-            </createAlignmentCase>
+			<createAlignmentCase>
+				<product>Laser-Scope</product>
+				<alignment>START_OF_SUBSCRIPTION</alignment>
+			</createAlignmentCase>
+			<createAlignmentCase>
+				<alignment>START_OF_BUNDLE</alignment>
+			</createAlignmentCase>
 		</createAlignment>
 		<billingAlignment>
 			<billingAlignmentCase>
@@ -191,9 +171,18 @@ Use Cases to do:
 				</duration>
 				<billingPeriod>MONTHLY</billingPeriod>
 				<recurringPrice>
-					<price><currency>GBP</currency><value>29.95</value></price>
-					<price><currency>EUR</currency><value>29.95</value></price> 
-					<price><currency>USD</currency><value>29.95</value></price>								
+					<price>
+						<currency>GBP</currency>
+						<value>29.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>29.95</value>
+					</price>
+					<price>
+						<currency>USD</currency>
+						<value>29.95</value>
+					</price>
 				</recurringPrice>
 			</finalPhase>
 		</plan>
@@ -207,7 +196,7 @@ Use Cases to do:
 					</duration>
 					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice></fixedPrice>
-				    <!-- no price implies $0 -->
+					<!-- no price implies $0 -->
 				</phase>
 			</initialPhases>
 			<finalPhase type="EVERGREEN">
@@ -217,9 +206,18 @@ Use Cases to do:
 				</duration>
 				<billingPeriod>MONTHLY</billingPeriod>
 				<recurringPrice>
-					<price><currency>USD</currency><value>249.95</value></price>								
-					<price><currency>EUR</currency><value>149.95</value></price>
-					<price><currency>GBP</currency><value>169.95</value></price>
+					<price>
+						<currency>USD</currency>
+						<value>249.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>149.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>169.95</value>
+					</price>
 				</recurringPrice>
 			</finalPhase>
 		</plan>
@@ -242,9 +240,18 @@ Use Cases to do:
 				</duration>
 				<billingPeriod>MONTHLY</billingPeriod>
 				<recurringPrice>
-					<price><currency>USD</currency><value>599.95</value></price>								
-					<price><currency>EUR</currency><value>349.95</value></price>
-					<price><currency>GBP</currency><value>399.95</value></price>
+					<price>
+						<currency>USD</currency>
+						<value>599.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>349.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>399.95</value>
+					</price>
 				</recurringPrice>
 			</finalPhase>
 		</plan>
@@ -267,9 +274,18 @@ Use Cases to do:
 				</duration>
 				<billingPeriod>ANNUAL</billingPeriod>
 				<recurringPrice>
-					<price><currency>USD</currency><value>199.95</value></price>								
-					<price><currency>EUR</currency><value>199.95</value></price>
-					<price><currency>GBP</currency><value>199.95</value></price>
+					<price>
+						<currency>USD</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>199.95</value>
+					</price>
 				</recurringPrice>
 			</finalPhase>
 		</plan>
@@ -292,9 +308,18 @@ Use Cases to do:
 				</duration>
 				<billingPeriod>ANNUAL</billingPeriod>
 				<recurringPrice>
-					<price><currency>USD</currency><value>2399.95</value></price>								
-					<price><currency>EUR</currency><value>1499.95</value></price>
-					<price><currency>GBP</currency><value>1699.95</value></price>
+					<price>
+						<currency>USD</currency>
+						<value>2399.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>1499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>1699.95</value>
+					</price>
 				</recurringPrice>
 			</finalPhase>
 		</plan>
@@ -317,9 +342,18 @@ Use Cases to do:
 				</duration>
 				<billingPeriod>ANNUAL</billingPeriod>
 				<recurringPrice>
-					<price><currency>USD</currency><value>5999.95</value></price>								
-					<price><currency>EUR</currency><value>3499.95</value></price>
-					<price><currency>GBP</currency><value>3999.95</value></price>
+					<price>
+						<currency>USD</currency>
+						<value>5999.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>3499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>3999.95</value>
+					</price>
 				</recurringPrice>
 			</finalPhase>
 		</plan>
@@ -342,9 +376,18 @@ Use Cases to do:
 					</duration>
 					<billingPeriod>MONTHLY</billingPeriod>
 					<recurringPrice>
-						<price><currency>USD</currency><value>9.95</value></price>								
-						<price><currency>EUR</currency><value>9.95</value></price>
-						<price><currency>GBP</currency><value>9.95</value></price>
+						<price>
+							<currency>USD</currency>
+							<value>9.95</value>
+						</price>
+						<price>
+							<currency>EUR</currency>
+							<value>9.95</value>
+						</price>
+						<price>
+							<currency>GBP</currency>
+							<value>9.95</value>
+						</price>
 					</recurringPrice>
 				</phase>
 			</initialPhases>
@@ -354,9 +397,18 @@ Use Cases to do:
 				</duration>
 				<billingPeriod>ANNUAL</billingPeriod>
 				<recurringPrice>
-					<price><currency>USD</currency><value>199.95</value></price>								
-					<price><currency>EUR</currency><value>199.95</value></price>
-					<price><currency>GBP</currency><value>199.95</value></price>
+					<price>
+						<currency>USD</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>199.95</value>
+					</price>
 				</recurringPrice>
 			</finalPhase>
 		</plan>
@@ -379,9 +431,18 @@ Use Cases to do:
 					</duration>
 					<billingPeriod>MONTHLY</billingPeriod>
 					<recurringPrice>
-						<price><currency>USD</currency><value>19.95</value></price>								
-						<price><currency>EUR</currency><value>49.95</value></price>
-						<price><currency>GBP</currency><value>69.95</value></price>
+						<price>
+							<currency>USD</currency>
+							<value>19.95</value>
+						</price>
+						<price>
+							<currency>EUR</currency>
+							<value>49.95</value>
+						</price>
+						<price>
+							<currency>GBP</currency>
+							<value>69.95</value>
+						</price>
 					</recurringPrice>
 				</phase>
 			</initialPhases>
@@ -391,9 +452,18 @@ Use Cases to do:
 				</duration>
 				<billingPeriod>ANNUAL</billingPeriod>
 				<recurringPrice>
-					<price><currency>USD</currency><value>2399.95</value></price>								
-					<price><currency>EUR</currency><value>1499.95</value></price>
-					<price><currency>GBP</currency><value>1699.95</value></price>
+					<price>
+						<currency>USD</currency>
+						<value>2399.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>1499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>1699.95</value>
+					</price>
 				</recurringPrice>
 			</finalPhase>
 		</plan>
@@ -416,10 +486,19 @@ Use Cases to do:
 					</duration>
 					<billingPeriod>MONTHLY</billingPeriod>
 					<recurringPrice>
-						<price><currency>USD</currency><value>99.95</value></price>								
-						<price><currency>EUR</currency><value>99.95</value></price>
-						<price><currency>GBP</currency><value>99.95</value></price>
-						</recurringPrice>
+						<price>
+							<currency>USD</currency>
+							<value>99.95</value>
+						</price>
+						<price>
+							<currency>EUR</currency>
+							<value>99.95</value>
+						</price>
+						<price>
+							<currency>GBP</currency>
+							<value>99.95</value>
+						</price>
+					</recurringPrice>
 				</phase>
 			</initialPhases>
 			<finalPhase type="EVERGREEN">
@@ -428,65 +507,110 @@ Use Cases to do:
 				</duration>
 				<billingPeriod>ANNUAL</billingPeriod>
 				<recurringPrice>
-					<price><currency>USD</currency><value>5999.95</value></price>								
-					<price><currency>EUR</currency><value>3499.95</value></price>
-					<price><currency>GBP</currency><value>3999.95</value></price>
+					<price>
+						<currency>USD</currency>
+						<value>5999.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>3499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>3999.95</value>
+					</price>
 				</recurringPrice>
 			</finalPhase>
 		</plan>
 		<plan name="laser-scope-monthly">
-		<product>Laser-Scope</product>
-           <initialPhases>
-              <phase type="DISCOUNT">
-                 <duration>
-                    <unit>MONTHS</unit>
-                    <number>1</number>
-                  </duration>
-                 <billingPeriod>MONTHLY</billingPeriod>
-                    <recurringPrice>
-                      <price><currency>USD</currency><value>999.95</value></price>                             
-                      <price><currency>EUR</currency><value>499.95</value></price>
-                      <price><currency>GBP</currency><value>999.95</value></price>
-                      </recurringPrice>
-                </phase>
-            </initialPhases>
+			<product>Laser-Scope</product>
+			<initialPhases>
+				<phase type="DISCOUNT">
+					<duration>
+						<unit>MONTHS</unit>
+						<number>1</number>
+					</duration>
+					<billingPeriod>MONTHLY</billingPeriod>
+					<recurringPrice>
+						<price>
+							<currency>USD</currency>
+							<value>999.95</value>
+						</price>
+						<price>
+							<currency>EUR</currency>
+							<value>499.95</value>
+						</price>
+						<price>
+							<currency>GBP</currency>
+							<value>999.95</value>
+						</price>
+					</recurringPrice>
+				</phase>
+			</initialPhases>
 			<finalPhase type="EVERGREEN">
 				<duration>
 					<unit>UNLIMITED</unit>
 				</duration>
 				<billingPeriod>MONTHLY</billingPeriod>
 				<recurringPrice>
-					<price><currency>USD</currency><value>1999.95</value></price>								
-					<price><currency>EUR</currency><value>1499.95</value></price>
-					<price><currency>GBP</currency><value>1999.95</value></price>
+					<price>
+						<currency>USD</currency>
+						<value>1999.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>1499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>1999.95</value>
+					</price>
 				</recurringPrice>
 			</finalPhase>
 		</plan>
 		<plan name="telescopic-scope-monthly">
 			<product>Telescopic-Scope</product>
 			<initialPhases>
-              <phase type="DISCOUNT">
-                 <duration>
-                    <unit>MONTHS</unit>
-                    <number>1</number>
-                  </duration>
-                 <billingPeriod>MONTHLY</billingPeriod>
-                    <recurringPrice>
-                      <price><currency>USD</currency><value>399.95</value></price>                             
-                      <price><currency>EUR</currency><value>299.95</value></price>
-                      <price><currency>GBP</currency><value>399.95</value></price>
-                      </recurringPrice>
-                </phase>
-            </initialPhases>
+				<phase type="DISCOUNT">
+					<duration>
+						<unit>MONTHS</unit>
+						<number>1</number>
+					</duration>
+					<billingPeriod>MONTHLY</billingPeriod>
+					<recurringPrice>
+						<price>
+							<currency>USD</currency>
+							<value>399.95</value>
+						</price>
+						<price>
+							<currency>EUR</currency>
+							<value>299.95</value>
+						</price>
+						<price>
+							<currency>GBP</currency>
+							<value>399.95</value>
+						</price>
+					</recurringPrice>
+				</phase>
+			</initialPhases>
 			<finalPhase type="EVERGREEN">
 				<duration>
 					<unit>UNLIMITED</unit>
 				</duration>
 				<billingPeriod>MONTHLY</billingPeriod>
 				<recurringPrice>
-					<price><currency>USD</currency><value>999.95</value></price>								
-					<price><currency>EUR</currency><value>499.95</value></price>
-					<price><currency>GBP</currency><value>999.95</value></price>
+					<price>
+						<currency>USD</currency>
+						<value>999.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>999.95</value>
+					</price>
 				</recurringPrice>
 			</finalPhase>
 		</plan>
@@ -498,9 +622,18 @@ Use Cases to do:
 				</duration>
 				<billingPeriod>MONTHLY</billingPeriod>
 				<recurringPrice>
-					<price><currency>USD</currency><value>999.95</value></price>								
-					<price><currency>EUR</currency><value>499.95</value></price>
-					<price><currency>GBP</currency><value>999.95</value></price>
+					<price>
+						<currency>USD</currency>
+						<value>999.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>999.95</value>
+					</price>
 				</recurringPrice>
 			</finalPhase>
 			<plansAllowedInBundle>-1</plansAllowedInBundle> <!-- arbitrary number of these (multipack) -->
@@ -524,9 +657,18 @@ Use Cases to do:
 				</duration>
 				<billingPeriod>ANNUAL</billingPeriod>
 				<recurringPrice>
-					<price><currency>USD</currency><value>199.95</value></price>								
-					<price><currency>EUR</currency><value>199.95</value></price>
-					<price><currency>GBP</currency><value>199.95</value></price>
+					<price>
+						<currency>USD</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>199.95</value>
+					</price>
 				</recurringPrice>
 			</finalPhase>
 		</plan>
@@ -549,9 +691,18 @@ Use Cases to do:
 				</duration>
 				<billingPeriod>ANNUAL</billingPeriod>
 				<recurringPrice>
-					<price><currency>USD</currency><value>199.95</value></price>								
-					<price><currency>EUR</currency><value>199.95</value></price>
-					<price><currency>GBP</currency><value>199.95</value></price>
+					<price>
+						<currency>USD</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>199.95</value>
+					</price>
 				</recurringPrice>
 			</finalPhase>
 		</plan>
@@ -565,9 +716,18 @@ Use Cases to do:
 					</duration>
 					<billingPeriod>ANNUAL</billingPeriod>
 					<recurringPrice>
-						<price><currency>USD</currency><value>5999.95</value></price>								
-						<price><currency>EUR</currency><value>3499.95</value></price>
-						<price><currency>GBP</currency><value>3999.95</value></price>
+						<price>
+							<currency>USD</currency>
+							<value>5999.95</value>
+						</price>
+						<price>
+							<currency>EUR</currency>
+							<value>3499.95</value>
+						</price>
+						<price>
+							<currency>GBP</currency>
+							<value>3999.95</value>
+						</price>
 					</recurringPrice>
 				</phase>
 			</initialPhases>
@@ -577,9 +737,18 @@ Use Cases to do:
 				</duration>
 				<billingPeriod>ANNUAL</billingPeriod>
 				<recurringPrice>
-					<price><currency>USD</currency><value>5999.95</value></price>								
-					<price><currency>EUR</currency><value>3499.95</value></price>
-					<price><currency>GBP</currency><value>3999.95</value></price>
+					<price>
+						<currency>USD</currency>
+						<value>5999.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>3499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>3999.95</value>
+					</price>
 				</recurringPrice>
 			</finalPhase>
 		</plan>
@@ -592,20 +761,38 @@ Use Cases to do:
 				</duration>
 				<billingPeriod>MONTHLY</billingPeriod>
 				<recurringPrice>
-					<price><currency>USD</currency><value>199.95</value></price>								
-					<price><currency>EUR</currency><value>199.95</value></price>
-					<price><currency>GBP</currency><value>199.95</value></price>
+					<price>
+						<currency>USD</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>199.95</value>
+					</price>
 				</recurringPrice>
 				<fixedPrice>
-					<price><currency>USD</currency><value>599.95</value></price>								
-					<price><currency>EUR</currency><value>599.95</value></price>
-					<price><currency>GBP</currency><value>599.95</value></price>
+					<price>
+						<currency>USD</currency>
+						<value>599.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>599.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>599.95</value>
+					</price>
 				</fixedPrice>
 			</finalPhase>
 		</plan>
 	</plans>
 	<priceLists>
-		<defaultPriceList name="DEFAULT"> 
+		<defaultPriceList name="DEFAULT">
 			<plans>
 				<plan>pistol-monthly</plan>
 				<plan>shotgun-monthly</plan>