killbill-aplcache
Changes
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultEntitlementRepairApi.java 77(+46 -31)
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionDataRepair.java 4(+2 -2)
entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestApiBaseRepair.java 195(+195 -0)
entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithAO.java 341(+341 -0)
entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithError.java 245(+245 -0)
entitlement/src/test/resources/testInput.xml 515(+351 -164)
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);
+ }
+}
entitlement/src/test/resources/testInput.xml 515(+351 -164)
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>