killbill-memoizeit

catalog: remove PlanPhase#getPlan This was causing a recursion

2/11/2015 12:07:56 PM

Details

diff --git a/api/src/main/java/org/killbill/billing/subscription/api/timeline/SubscriptionBaseTimeline.java b/api/src/main/java/org/killbill/billing/subscription/api/timeline/SubscriptionBaseTimeline.java
index c8a3ad6..855e0a1 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/timeline/SubscriptionBaseTimeline.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/timeline/SubscriptionBaseTimeline.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -89,6 +91,11 @@ public interface SubscriptionBaseTimeline extends Entity {
         public DateTime getEffectiveDate();
 
         /**
+         * @return the name of the plan
+         */
+        public String getPlanName();
+
+        /**
          * @return the name of the phase
          */
         public String getPlanPhaseName();
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestRepairIntegration.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestRepairIntegration.java
index 3a681d2..5c9b104 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestRepairIntegration.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestRepairIntegration.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -257,6 +259,11 @@ public class TestRepairIntegration extends TestIntegrationBase {
             }
 
             @Override
+            public String getPlanName() {
+                return null;
+            }
+
+            @Override
             public String getPlanPhaseName() {
                 return null;
             }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhase.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhase.java
index d9b2d5b..ff0507b 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhase.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhase.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -72,9 +74,6 @@ public class DefaultPlanPhase extends ValidatingConfig<StandaloneCatalog> implem
         throw new CatalogApiException(ErrorCode.CAT_BAD_PHASE_NAME, phaseName);
     }
 
-    /* (non-Javadoc)
-      * @see org.killbill.billing.catalog.IPlanPhase#getCohort()
-      */
     @Override
     public PhaseType getPhaseType() {
         return type;
@@ -107,27 +106,11 @@ public class DefaultPlanPhase extends ValidatingConfig<StandaloneCatalog> implem
         return usages;
     }
 
-    /* (non-Javadoc)
-          * @see org.killbill.billing.catalog.IPlanPhase#getName()
-          */
     @Override
     public String getName() {
         return phaseName(plan.getName(), this.getPhaseType());
     }
 
-    /* (non-Javadoc)
-      * @see org.killbill.billing.catalog.IPlanPhase#getPlan()
-      */
-    @Override
-    public Plan getPlan() {
-        return plan;
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see org.killbill.billing.catalog.IPlanPhase#getDuration()
-     */
     @Override
     public Duration getDuration() {
         return duration;
@@ -159,6 +142,7 @@ public class DefaultPlanPhase extends ValidatingConfig<StandaloneCatalog> implem
         }
         if (recurring != null) {
             recurring.initialize(root, uri);
+            recurring.setPlan(plan);
             recurring.setPhase(this);
         }
         if (usages != null) {
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultRecurring.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultRecurring.java
index c9b6ae8..644052e 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultRecurring.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultRecurring.java
@@ -1,5 +1,6 @@
 /*
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -23,6 +24,7 @@ import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 
 import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.Recurring;
 import org.killbill.xmlloader.ValidatingConfig;
@@ -39,6 +41,7 @@ public class DefaultRecurring extends ValidatingConfig<StandaloneCatalog> implem
     private DefaultInternationalPrice recurringPrice;
 
     // Not exposed in xml.
+    private Plan plan;
     private PlanPhase phase;
 
     @Override
@@ -60,23 +63,22 @@ public class DefaultRecurring extends ValidatingConfig<StandaloneCatalog> implem
 
     @Override
     public ValidationErrors validate(final StandaloneCatalog catalog, final ValidationErrors errors) {
-
-        //Validation: check for nulls
+        // Validation: check for nulls
         if (billingPeriod == null) {
-            errors.add(new ValidationError(String.format("Recurring section of Phase %s of plan %s has a recurring price but no billing period", phase.getPhaseType().toString(), phase.getPlan().getName()),
+            errors.add(new ValidationError(String.format("Recurring section of Phase %s of plan %s has a recurring price but no billing period", phase.getPhaseType().toString(), plan.getName()),
                                            catalog.getCatalogURI(), DefaultPlanPhase.class, phase.getPhaseType().toString()));
         }
 
-        //Validation: if there is a recurring price there must be a billing period
+        // Validation: if there is a recurring price there must be a billing period
         if ((recurringPrice != null) && (billingPeriod == null || billingPeriod == BillingPeriod.NO_BILLING_PERIOD)) {
-            errors.add(new ValidationError(String.format("Recurring section of Phase %s of plan %s has a recurring price but no billing period", phase.getPhaseType().toString(), phase.getPlan().getName()),
+            errors.add(new ValidationError(String.format("Recurring section of Phase %s of plan %s has a recurring price but no billing period", phase.getPhaseType().toString(), plan.getName()),
                                            catalog.getCatalogURI(), DefaultPlanPhase.class, phase.getPhaseType().toString()));
         }
 
-        //Validation: if there is no recurring price there should be no billing period
+        // Validation: if there is no recurring price there should be no billing period
         if ((recurringPrice == null) && billingPeriod != BillingPeriod.NO_BILLING_PERIOD) {
             errors.add(new ValidationError(String.format("Recurring section of Phase %s of plan %s has no recurring price but does have a billing period. The billing period should be set to '%s'",
-                                                         phase.getPhaseType().toString(), phase.getPlan().getName(), BillingPeriod.NO_BILLING_PERIOD),
+                                                         phase.getPhaseType().toString(), plan.getName(), BillingPeriod.NO_BILLING_PERIOD),
                                            catalog.getCatalogURI(), DefaultPlanPhase.class, phase.getPhaseType().toString()));
         }
         return errors;
@@ -92,6 +94,11 @@ public class DefaultRecurring extends ValidatingConfig<StandaloneCatalog> implem
         return this;
     }
 
+    public DefaultRecurring setPlan(final Plan plan) {
+        this.plan = plan;
+        return this;
+    }
+
     public DefaultRecurring setPhase(final PlanPhase phase) {
         this.phase = phase;
         return this;
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestPlanPhase.java b/catalog/src/test/java/org/killbill/billing/catalog/TestPlanPhase.java
index dfa049b..df1663c 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestPlanPhase.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestPlanPhase.java
@@ -30,14 +30,18 @@ public class TestPlanPhase extends CatalogTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testValidation() {
+        final MockCatalog catalog = new MockCatalog();
+
         DefaultPlanPhase pp = MockPlanPhase.createUSDMonthlyEvergreen(null, "1.00").setPlan(MockPlan.createBicycleNoTrialEvergreen1USD());
+        pp.initialize(catalog, null);
 
-        ValidationErrors errors = pp.validate(new MockCatalog(), new ValidationErrors());
+        ValidationErrors errors = pp.validate(catalog, new ValidationErrors());
         errors.log(log);
         Assert.assertEquals(errors.size(), 1);
 
         pp = MockPlanPhase.createUSDMonthlyEvergreen("1.00", null).setRecurring(new MockRecurring(BillingPeriod.NO_BILLING_PERIOD, MockInternationalPrice.createUSD("1.00")).setPhase(pp)).setPlan(MockPlan.createBicycleNoTrialEvergreen1USD());
-        errors = pp.validate(new MockCatalog(), new ValidationErrors());
+        pp.initialize(catalog, null);
+        errors = pp.validate(catalog, new ValidationErrors());
         errors.log(log);
         Assert.assertEquals(errors.size(), 1);
     }
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlement.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlement.java
index f6e459a..89d16e9 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlement.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlement.java
@@ -259,7 +259,7 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
 
         // Verify the change is immediate
         final Entitlement entitlement2 = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
-        assertEquals(entitlement2.getLastActivePhase().getPlan().getProduct().getName(), "Assault-Rifle");
+        assertEquals(entitlement2.getLastActivePlan().getProduct().getName(), "Assault-Rifle");
 
         testListener.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK);
         final Entitlement cancelledEntitlement = entitlement.cancelEntitlementWithPolicy(EntitlementActionPolicy.END_OF_TERM, callContext);
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimeline.java b/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimeline.java
index af2acc8..e81a703 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimeline.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimeline.java
@@ -94,6 +94,7 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
 
         final List<ExistingEvent> result = new LinkedList<SubscriptionBaseTimeline.ExistingEvent>();
 
+        String prevPlanName = null;
         String prevProductName = null;
         BillingPeriod prevBillingPeriod = null;
         String prevPriceListName = null;
@@ -112,11 +113,11 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
             }
             startDate = (startDate == null) ? cur.getEffectiveDate() : startDate;
 
-
             String productName = null;
             BillingPeriod billingPeriod = null;
             String priceListName = null;
             PhaseType phaseType = null;
+            String planName = null;
             String planPhaseName = null;
 
             ApiEventType apiType = null;
@@ -125,6 +126,8 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
                     final PhaseEvent phaseEV = (PhaseEvent) cur;
                     planPhaseName = phaseEV.getPhase();
                     phaseType = catalog.findPhase(phaseEV.getPhase(), cur.getEffectiveDate(), startDate).getPhaseType();
+                    // A PHASE event always occurs within the same plan (and is never the first event)
+                    planName = prevPlanName;
                     productName = prevProductName;
                     billingPeriod = getBillingPeriod(catalog, phaseEV.getPhase(), cur.getEffectiveDate(), startDate);
                     priceListName = prevPriceListName;
@@ -133,6 +136,7 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
                 case API_USER:
                     final ApiEvent userEV = (ApiEvent) cur;
                     apiType = userEV.getEventType();
+                    planName = userEV.getEventPlan();
                     planPhaseName = userEV.getEventPlanPhase();
                     final Plan plan = (userEV.getEventPlan() != null) ? catalog.findPlan(userEV.getEventPlan(), cur.getRequestedDate(), startDate) : null;
                     phaseType = (userEV.getEventPlanPhase() != null) ? catalog.findPhase(userEV.getEventPlanPhase(), cur.getEffectiveDate(), startDate).getPhaseType() : prevPhaseType;
@@ -144,6 +148,7 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
 
             final SubscriptionBaseTransitionType transitionType = SubscriptionBaseTransitionData.toSubscriptionTransitionType(cur.getType(), apiType);
 
+            final String planNameWithClosure = planName;
             final String planPhaseNameWithClosure = planPhaseName;
             final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, category, billingPeriod, priceListName, phaseType);
             result.add(new ExistingEvent() {
@@ -173,11 +178,17 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
                 }
 
                 @Override
+                public String getPlanName() {
+                    return planNameWithClosure;
+                }
+
+                @Override
                 public String getPlanPhaseName() {
                     return planPhaseNameWithClosure;
                 }
             });
 
+            prevPlanName = planName;
             prevProductName = productName;
             prevBillingPeriod = billingPeriod;
             prevPriceListName = priceListName;
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
index b9dd7f2..dab95a5 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -28,6 +30,7 @@ import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.CatalogService;
+import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.ProductCategory;
@@ -95,7 +98,7 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
         }
         final ApiEventBuilder apiBuilder = new ApiEventBuilder()
                 .setSubscriptionId(subscription.getId())
-                .setEventPlan(currentPhase.getPlan().getName())
+                .setEventPlan(existingEvent.getPlanName())
                 .setEventPlanPhase(currentPhase.getName())
                 .setEventPriceList(spec.getPriceListName())
                 .setActiveVersion(subscription.getActiveVersion())
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java b/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java
index 9aed26d..02a39de 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -175,6 +177,11 @@ public class TestDefaultSubscriptionTransferApi extends SubscriptionTestSuiteNoD
             }
 
             @Override
+            public String getPlanName() {
+                return "BicycleTrialEvergreen1USD";
+            }
+
+            @Override
             public String getPlanPhaseName() {
                 return SubscriptionBaseTransitionType.CANCEL.equals(subscriptionTransitionType) ? null : "BicycleTrialEvergreen1USD-trial";
             }
@@ -211,6 +218,11 @@ public class TestDefaultSubscriptionTransferApi extends SubscriptionTestSuiteNoD
             }
 
             @Override
+            public String getPlanName() {
+                return "BicycleTrialEvergreen1USD";
+            }
+
+            @Override
             public String getPlanPhaseName() {
                 return "BicycleTrialEvergreen1USD-trial";
             }
@@ -245,6 +257,11 @@ public class TestDefaultSubscriptionTransferApi extends SubscriptionTestSuiteNoD
             }
 
             @Override
+            public String getPlanName() {
+                return migrateEntitlementEvent.getPlanName();
+            }
+
+            @Override
             public String getPlanPhaseName() {
                 return migrateEntitlementEvent.getPlanPhaseName();
             }
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java
index 6f6e57d..54bc97f 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java
@@ -505,6 +505,11 @@ public class TestSubscriptionHelper {
             }
 
             @Override
+            public String getPlanName() {
+                return null;
+            }
+
+            @Override
             public String getPlanPhaseName() {
                 return null;
             }