killbill-uncached

Migration bug fixes (nuts tests)

12/24/2011 3:11:42 AM

Details

diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java
index da1eb4d..66613d3 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java
@@ -16,16 +16,22 @@
 
 package com.ning.billing.entitlement.alignment;
 
+
 import org.joda.time.DateTime;
 
 import com.google.inject.Inject;
+import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.CatalogService;
+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.entitlement.api.migration.EntitlementMigrationApiException;
-import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.EntitlementSubscriptionMigrationCase;
 import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
 import com.ning.billing.entitlement.events.user.ApiEventType;
+import com.ning.billing.util.clock.DefaultClock;
 
 public class MigrationPlanAligner {
 
@@ -37,15 +43,84 @@ public class MigrationPlanAligner {
     }
 
 
-    public TimedMigration [] getEventsOnRegularMigration(SubscriptionData subscription,
-            Plan plan, PlanPhase initialPhase, String priceList, DateTime effectiveDate) {
+    public TimedMigration [] getEventsMigration(EntitlementSubscriptionMigrationCase [] input, DateTime now)
+        throws EntitlementMigrationApiException {
+
+        try {
+            TimedMigration [] events = null;
+            Plan plan0 = catalogService.getCatalog().findPlan(input[0].getPlanPhaseSpecifer().getProductName(),
+                    input[0].getPlanPhaseSpecifer().getBillingPeriod(), input[0].getPlanPhaseSpecifer().getPriceListName());
+
+            Plan plan1 = (input.length > 1) ? catalogService.getCatalog().findPlan(input[1].getPlanPhaseSpecifer().getProductName(),
+                    input[1].getPlanPhaseSpecifer().getBillingPeriod(), input[1].getPlanPhaseSpecifer().getPriceListName()) :
+                        null;
+
+            DateTime migrationStartDate = now;
+
+            if (isRegularMigratedSubscription(input)) {
+
+                events = getEventsOnRegularMigration(plan0,
+                        getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
+                        input[0].getPlanPhaseSpecifer().getPriceListName(),
+                        now);
+
+            } else if (isRegularFutureCancelledMigratedSubscription(input)) {
+
+                events = getEventsOnFuturePlanCancelMigration(plan0,
+                        getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
+                        input[0].getPlanPhaseSpecifer().getPriceListName(),
+                        now,
+                        input[0].getCancelledDate());
+
+            } else if (isPhaseChangeMigratedSubscription(input)) {
+
+                PhaseType curPhaseType = input[0].getPlanPhaseSpecifer().getPhaseType();
+                Duration curPhaseDuration = null;
+                for (PlanPhase cur : plan0.getAllPhases()) {
+                    if (cur.getPhaseType() == curPhaseType) {
+                        curPhaseDuration = cur.getDuration();
+                        break;
+                    }
+                }
+                if (curPhaseDuration == null) {
+                    throw new EntitlementMigrationApiException(String.format("Failed to compute current phase duration for plan %s and phase %s",
+                            plan0.getName(), curPhaseType));
+                }
+
+                migrationStartDate = DefaultClock.removeDuration(input[1].getEffectiveDate(), curPhaseDuration);
+                events = getEventsOnFuturePhaseChangeMigration(plan0,
+                        getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
+                        input[0].getPlanPhaseSpecifer().getPriceListName(),
+                        migrationStartDate,
+                        input[1].getEffectiveDate());
+
+            } else if (isPlanChangeMigratedSubscription(input)) {
+
+                events = getEventsOnFuturePlanChangeMigration(plan0,
+                        getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
+                        plan1,
+                        getPlanPhase(plan1, input[1].getPlanPhaseSpecifer().getPhaseType()),
+                        input[0].getPlanPhaseSpecifer().getPriceListName(),
+                        now,
+                        input[1].getEffectiveDate());
+
+            } else {
+                throw new EntitlementMigrationApiException("Unknown migration type");
+            }
+
+            return events;
+        } catch (CatalogApiException e) {
+            throw new EntitlementMigrationApiException(e);
+        }
+    }
+
+    private TimedMigration [] getEventsOnRegularMigration(Plan plan, PlanPhase initialPhase, String priceList, DateTime effectiveDate) {
         TimedMigration [] result = new TimedMigration[1];
         result[0] = new TimedMigration(effectiveDate, EventType.API_USER, ApiEventType.MIGRATE_ENTITLEMENT, plan, initialPhase, priceList);
         return result;
     }
 
-    public TimedMigration [] getEventsOnFuturePhaseChangeMigration(SubscriptionData subscription,
-            Plan plan, PlanPhase initialPhase, String priceList, DateTime effectiveDate, DateTime effectiveDateForNextPhase)
+    private TimedMigration [] getEventsOnFuturePhaseChangeMigration(Plan plan, PlanPhase initialPhase, String priceList, DateTime effectiveDate, DateTime effectiveDateForNextPhase)
         throws EntitlementMigrationApiException {
 
         TimedMigration [] result = new TimedMigration[2];
@@ -69,19 +144,58 @@ public class MigrationPlanAligner {
         return result;
     }
 
-    public TimedMigration [] getEventsOnFuturePlanChangeMigration(SubscriptionData subscription,
-            Plan currentPlan, PlanPhase currentPhase, Plan newPlan, PlanPhase newPhase, String priceList, DateTime effectiveDate, DateTime effectiveDateForChangePlan) {
+    private TimedMigration [] getEventsOnFuturePlanChangeMigration(Plan currentPlan, PlanPhase currentPhase, Plan newPlan, PlanPhase newPhase, String priceList, DateTime effectiveDate, DateTime effectiveDateForChangePlan) {
         TimedMigration [] result = new TimedMigration[2];
         result[0] = new TimedMigration(effectiveDate, EventType.API_USER, ApiEventType.MIGRATE_ENTITLEMENT, currentPlan, currentPhase, priceList);
         result[1] = new TimedMigration(effectiveDateForChangePlan, EventType.API_USER, ApiEventType.CHANGE, newPlan, newPhase, priceList);
         return result;
     }
 
-    public TimedMigration [] getEventsOnFuturePlanCancelMigration(SubscriptionData subscription,
-            Plan plan, PlanPhase initialPhase, String priceList, DateTime effectiveDate, DateTime effectiveDateForCancellation) {
+    private TimedMigration [] getEventsOnFuturePlanCancelMigration(Plan plan, PlanPhase initialPhase, String priceList, DateTime effectiveDate, DateTime effectiveDateForCancellation) {
         TimedMigration [] result = new TimedMigration[2];
         result[0] = new TimedMigration(effectiveDate, EventType.API_USER, ApiEventType.MIGRATE_ENTITLEMENT, plan, initialPhase, priceList);
         result[1] = new TimedMigration(effectiveDateForCancellation, EventType.API_USER, ApiEventType.CANCEL, null, null, null);
         return result;
     }
+
+
+    // STEPH should be in catalog
+    private PlanPhase getPlanPhase(Plan plan, PhaseType phaseType) throws EntitlementMigrationApiException {
+        for (PlanPhase cur: plan.getAllPhases()) {
+            if (cur.getPhaseType() == phaseType) {
+                return cur;
+            }
+        }
+        throw new EntitlementMigrationApiException(String.format("Cannot find PlanPhase from Plan %s and type %s", plan.getName(), phaseType));
+    }
+
+    private boolean isRegularMigratedSubscription(EntitlementSubscriptionMigrationCase [] input) {
+        return (input.length == 1 && input[0].getCancelledDate() == null);
+    }
+
+    private boolean isRegularFutureCancelledMigratedSubscription(EntitlementSubscriptionMigrationCase [] input) {
+        return (input.length == 1 && input[0].getCancelledDate() != null);
+    }
+
+    private boolean isPhaseChangeMigratedSubscription(EntitlementSubscriptionMigrationCase [] input) {
+        if (input.length != 2) {
+            return false;
+        }
+        return isSamePlan(input[0].getPlanPhaseSpecifer(), input[1].getPlanPhaseSpecifer());
+    }
+
+    private boolean isPlanChangeMigratedSubscription(EntitlementSubscriptionMigrationCase [] input) {
+        if (input.length != 2) {
+            return false;
+        }
+        return ! isSamePlan(input[0].getPlanPhaseSpecifer(), input[1].getPlanPhaseSpecifer());
+    }
+
+    private boolean isSamePlan(PlanPhaseSpecifier plan0, PlanPhaseSpecifier plan1) {
+        if (plan0.getPriceListName().equals(plan1.getPriceListName()) &&
+                plan0.getProductName().equals(plan1.getProductName())) {
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
index b847ebb..aa16ac7 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
@@ -27,6 +27,7 @@ import org.joda.time.DateTime;
 import com.google.inject.Inject;
 import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.CatalogService;
+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;
@@ -51,6 +52,7 @@ import com.ning.billing.entitlement.events.user.ApiEventChange;
 import com.ning.billing.entitlement.events.user.ApiEventMigrate;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.DefaultClock;
 
 public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
 
@@ -129,86 +131,21 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
             EntitlementSubscriptionMigrationCase [] input, DateTime now)
         throws EntitlementMigrationApiException {
 
-        try {
-            final DateTime bundleStartDate = now;
-
-            List<EntitlementEvent> emptyEvents =  Collections.emptyList();
-
-            SubscriptionData subscriptionData = factory.createSubscription(new SubscriptionBuilder()
+        TimedMigration [] events = migrationAligner.getEventsMigration(input, now);
+        DateTime migrationStartDate= events[0].getEventTime();
+        List<EntitlementEvent> emptyEvents =  Collections.emptyList();
+        SubscriptionData subscriptionData = factory.createSubscription(new SubscriptionBuilder()
             .setId(UUID.randomUUID())
             .setBundleId(bundleId)
             .setCategory(productCategory)
-            .setBundleStartDate(bundleStartDate)
-            .setStartDate(now),
+            .setBundleStartDate(migrationStartDate)
+            .setStartDate(migrationStartDate),
             emptyEvents);
-
-            TimedMigration [] events = null;
-            Plan plan0 = catalogService.getCatalog().findPlan(input[0].getPlanPhaseSpecifer().getProductName(),
-                    input[0].getPlanPhaseSpecifer().getBillingPeriod(), input[0].getPlanPhaseSpecifer().getPriceListName());
-
-            Plan plan1 = (input.length > 1) ? catalogService.getCatalog().findPlan(input[1].getPlanPhaseSpecifer().getProductName(),
-                    input[1].getPlanPhaseSpecifer().getBillingPeriod(), input[1].getPlanPhaseSpecifer().getPriceListName()) :
-                        null;
-
-            if (isRegularMigratedSubscription(input)) {
-
-                events = migrationAligner.getEventsOnRegularMigration(subscriptionData,
-                        plan0,
-                        getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
-                        input[0].getPlanPhaseSpecifer().getPriceListName(),
-                        now);
-
-            } else if (isRegularFutureCancelledMigratedSubscription(input)) {
-
-                events = migrationAligner.getEventsOnFuturePlanCancelMigration(subscriptionData,
-                        plan0,
-                        getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
-                        input[0].getPlanPhaseSpecifer().getPriceListName(),
-                        now,
-                        input[0].getCancelledDate());
-
-            } else if (isPhaseChangeMigratedSubscription(input)) {
-
-                events = migrationAligner.getEventsOnFuturePhaseChangeMigration(subscriptionData,
-                        plan0,
-                        getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
-                        input[0].getPlanPhaseSpecifer().getPriceListName(),
-                        now,
-                        input[1].getEffectiveDate());
-
-            } else if (isPlanChangeMigratedSubscription(input)) {
-
-                events = migrationAligner.getEventsOnFuturePlanChangeMigration(subscriptionData,
-                        plan0,
-                        getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
-                        plan1,
-                        getPlanPhase(plan1, input[1].getPlanPhaseSpecifer().getPhaseType()),
-                        input[0].getPlanPhaseSpecifer().getPriceListName(),
-                        now,
-                        input[1].getEffectiveDate());
-
-            } else {
-                throw new EntitlementMigrationApiException("Unknown migration type");
-            }
-            return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, events));
-        } catch (CatalogApiException e) {
-            throw new EntitlementMigrationApiException(e);
-        }
-    }
-
-    // STEPH should be in catalog
-    private PlanPhase getPlanPhase(Plan plan, PhaseType phaseType) throws EntitlementMigrationApiException {
-        for (PlanPhase cur: plan.getAllPhases()) {
-            if (cur.getPhaseType() == phaseType) {
-                return cur;
-            }
-        }
-        throw new EntitlementMigrationApiException(String.format("Cannot find PlanPhase from Plan %s and type %s", plan.getName(), phaseType));
+        return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, events));
     }
 
     private List<EntitlementEvent> toEvents(SubscriptionData subscriptionData, DateTime now, TimedMigration [] migrationEvents) {
 
-
         List<EntitlementEvent> events = new ArrayList<EntitlementEvent>(migrationEvents.length);
         for (TimedMigration cur : migrationEvents) {
 
@@ -248,34 +185,4 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
         }
         return events;
     }
-
-    private boolean isRegularMigratedSubscription(EntitlementSubscriptionMigrationCase [] input) {
-        return (input.length == 1 && input[0].getCancelledDate() == null);
-    }
-
-    private boolean isRegularFutureCancelledMigratedSubscription(EntitlementSubscriptionMigrationCase [] input) {
-        return (input.length == 1 && input[0].getCancelledDate() != null);
-    }
-
-    private boolean isPhaseChangeMigratedSubscription(EntitlementSubscriptionMigrationCase [] input) {
-        if (input.length != 2) {
-            return false;
-        }
-        return isSamePlan(input[0].getPlanPhaseSpecifer(), input[1].getPlanPhaseSpecifer());
-    }
-
-    private boolean isPlanChangeMigratedSubscription(EntitlementSubscriptionMigrationCase [] input) {
-        if (input.length != 2) {
-            return false;
-        }
-        return ! isSamePlan(input[0].getPlanPhaseSpecifer(), input[1].getPlanPhaseSpecifer());
-    }
-
-    private boolean isSamePlan(PlanPhaseSpecifier plan0, PlanPhaseSpecifier plan1) {
-        if (plan0.getPriceListName().equals(plan1.getPriceListName()) &&
-                plan0.getProductName().equals(plan1.getProductName())) {
-            return true;
-        }
-        return false;
-    }
 }
diff --git a/util/src/main/java/com/ning/billing/util/clock/DefaultClock.java b/util/src/main/java/com/ning/billing/util/clock/DefaultClock.java
index 8d61ba5..bad6a0a 100644
--- a/util/src/main/java/com/ning/billing/util/clock/DefaultClock.java
+++ b/util/src/main/java/com/ning/billing/util/clock/DefaultClock.java
@@ -42,21 +42,21 @@ public class DefaultClock implements Clock {
         return input.minus(input.getMillisOfSecond());
     }
 
-    public static DateTime addDuration(DateTime input, List<Duration> durations) {
 
+    public static DateTime addOrRemoveDuration(DateTime input, List<Duration> durations, boolean add) {
         DateTime result = input;
         for (Duration cur : durations) {
             switch (cur.getUnit()) {
             case DAYS:
-                result = result.plusDays(cur.getNumber());
+                result = add ? result.plusDays(cur.getNumber()) : result.minusDays(cur.getNumber());
                 break;
 
             case MONTHS:
-                result = result.plusMonths(cur.getNumber());
+                result = add ? result.plusMonths(cur.getNumber()) : result.minusMonths(cur.getNumber());
                 break;
 
             case YEARS:
-                result = result.plusYears(cur.getNumber());
+                result = add ? result.plusYears(cur.getNumber()) : result.minusYears(cur.getNumber());
                 break;
             case UNLIMITED:
             default:
@@ -66,9 +66,23 @@ public class DefaultClock implements Clock {
         return result;
     }
 
+    public static DateTime addDuration(DateTime input, List<Duration> durations) {
+        return addOrRemoveDuration(input, durations, true);
+    }
+
+    public static DateTime removeDuration(DateTime input, List<Duration> durations) {
+        return addOrRemoveDuration(input, durations, false);
+    }
+
     public static DateTime addDuration(DateTime input, Duration duration) {
         List<Duration> list = new ArrayList<Duration>();
         list.add(duration);
-        return addDuration(input, list);
+        return addOrRemoveDuration(input, list, true);
+    }
+
+    public static DateTime removeDuration(DateTime input, Duration duration) {
+        List<Duration> list = new ArrayList<Duration>();
+        list.add(duration);
+        return addOrRemoveDuration(input, list, false);
     }
 }