killbill-aplcache

Merge branch 'integration' of github.com:ning/killbill

3/8/2012 2:26:46 PM

Changes

entitlement/pom.xml 50(+26 -24)

entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadMockEntitlementDao.java 142(+0 -142)

entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadSubscription.java 149(+0 -149)

invoice/src/test/java/com/ning/billing/invoice/dao/MockSubscription.java 127(+0 -127)

invoice/src/test/java/com/ning/billing/invoice/notification/BrainDeadSubscription.java 149(+0 -149)

Details

diff --git a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
index ba9dc2a..4af7004 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
@@ -51,12 +51,6 @@ public class AnalyticsListener {
             case CHANGE:
                 bstRecorder.subscriptionChanged(event);
                 break;
-            case PAUSE:
-                bstRecorder.subscriptionPaused(event);
-                break;
-            case RESUME:
-                bstRecorder.subscriptionResumed(event);
-                break;
             case UNCANCEL:
                 break;
             case PHASE:
diff --git a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
index adf685f..852d4fc 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
@@ -16,6 +16,24 @@
 
 package com.ning.billing.analytics.api;
 
+import static org.testng.Assert.fail;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.commons.io.IOUtils;
+import org.joda.time.DateTime;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
 import com.google.inject.Inject;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountCreationNotification;
@@ -62,23 +80,6 @@ import com.ning.billing.util.tag.DefaultTagDefinition;
 import com.ning.billing.util.tag.DescriptiveTag;
 import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.dao.TagDefinitionSqlDao;
-import org.apache.commons.io.IOUtils;
-import org.joda.time.DateTime;
-import org.testng.Assert;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Guice;
-import org.testng.annotations.Test;
-
-import java.io.IOException;
-import java.math.BigDecimal;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.UUID;
-
-import static org.testng.Assert.fail;
 
 @Guice(modules = AnalyticsTestModule.class)
 public class TestAnalyticsService {
@@ -206,7 +207,8 @@ public class TestAnalyticsService {
                 Subscription.SubscriptionState.ACTIVE,
                 plan,
                 phase,
-                priceList
+                priceList,
+                true
         );
         expectedTransition = new BusinessSubscriptionTransition(
                 ID,
@@ -241,7 +243,7 @@ public class TestAnalyticsService {
 
         paymentInfoNotification = new PaymentInfo.Builder().setPaymentId(UUID.randomUUID().toString()).setPaymentMethod(PAYMENT_METHOD).setCardCountry(CARD_COUNTRY).build();
         final PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice.getId(), account.getId(), BigDecimal.TEN,
-                ACCOUNT_CURRENCY, clock.getUTCNow(), clock.getUTCNow(), paymentInfoNotification.getPaymentId(), 1, clock.getUTCNow().plusDays(1));
+                ACCOUNT_CURRENCY, clock.getUTCNow(), clock.getUTCNow(), paymentInfoNotification.getPaymentId(), 1);
         paymentDao.createPaymentAttempt(paymentAttempt);
         paymentDao.savePaymentInfo(paymentInfoNotification);
         Assert.assertEquals(paymentDao.getPaymentInfo(Arrays.asList(invoice.getId().toString())).size(), 1);
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java b/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
index f592c41..69abfb4 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
@@ -19,9 +19,13 @@ package com.ning.billing.analytics;
 import com.ning.billing.catalog.api.BillingPeriod;
 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.ProductCategory;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.util.customfield.CustomField;
+
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 
@@ -58,18 +62,6 @@ public class MockSubscription implements Subscription
     }
 
     @Override
-    public void pause()
-    {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void resume()
-    {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
     public UUID getId()
     {
         return ID;
@@ -146,11 +138,52 @@ public class MockSubscription implements Subscription
 
 	@Override
 	public DateTime getPaidThroughDate() {
-		throw new UnsupportedOperationException();
+        throw new UnsupportedOperationException();
 	}
 
     @Override
     public SubscriptionTransition getPreviousTransition() {
         return null;
     }
+
+    @Override
+    public ProductCategory getCategory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void recreate(PlanPhaseSpecifier spec, DateTime requestedDate)
+            throws EntitlementUserApiException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getFieldValue(String fieldName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setFieldValue(String fieldName, String fieldValue) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<CustomField> getFieldList() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void addFields(List<CustomField> fields) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void clearFields() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getObjectName() {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java b/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
index a9a8293..79bea58 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
@@ -72,32 +72,14 @@ public class TestAnalyticsListener
         Assert.assertEquals(dao.getTransitions(KEY).size(), 1);
         Assert.assertEquals(dao.getTransitions(KEY).get(0), firstBST);
 
-        // Pause it
-        final DateTime effectivePauseTransitionTime = new DateTime(DateTimeZone.UTC);
-        final DateTime requestedPauseTransitionTime = new DateTime(DateTimeZone.UTC);
-        final SubscriptionTransitionData pausedSubscriptionTransition = createPauseSubscriptionTransition(effectivePauseTransitionTime, requestedPauseTransitionTime, firstTransition.getNextState());
-        final BusinessSubscriptionTransition pausedBST = createExpectedPausedBST(pausedSubscriptionTransition.getId(), requestedPauseTransitionTime, effectivePauseTransitionTime, firstBST.getNextSubscription());
-        listener.handleSubscriptionTransitionChange(pausedSubscriptionTransition);
-        Assert.assertEquals(dao.getTransitions(KEY).size(), 2);
-        Assert.assertEquals(dao.getTransitions(KEY).get(1), pausedBST);
-
-        // Un-Pause it
-        final DateTime effectiveResumeTransitionTime = new DateTime(DateTimeZone.UTC);
-        final DateTime requestedResumeTransitionTime = new DateTime(DateTimeZone.UTC);
-        final SubscriptionTransitionData resumedSubscriptionTransition = createResumeSubscriptionTransition(requestedResumeTransitionTime, effectiveResumeTransitionTime, pausedSubscriptionTransition.getNextState());
-        final BusinessSubscriptionTransition resumedBST = createExpectedResumedBST(resumedSubscriptionTransition.getId(), requestedResumeTransitionTime, effectiveResumeTransitionTime, pausedBST.getNextSubscription());
-        listener.handleSubscriptionTransitionChange(resumedSubscriptionTransition);
-        Assert.assertEquals(dao.getTransitions(KEY).size(), 3);
-        Assert.assertEquals(dao.getTransitions(KEY).get(2), resumedBST);
-
         // Cancel it
         final DateTime effectiveCancelTransitionTime = new DateTime(DateTimeZone.UTC);
         final DateTime requestedCancelTransitionTime = new DateTime(DateTimeZone.UTC);
-        final SubscriptionTransitionData cancelledSubscriptionTransition = createCancelSubscriptionTransition(requestedCancelTransitionTime, effectiveCancelTransitionTime, resumedSubscriptionTransition.getNextState());
-        final BusinessSubscriptionTransition cancelledBST = createExpectedCancelledBST(cancelledSubscriptionTransition.getId(), requestedCancelTransitionTime, effectiveCancelTransitionTime, resumedBST.getNextSubscription());
+        final SubscriptionTransitionData cancelledSubscriptionTransition = createCancelSubscriptionTransition(requestedCancelTransitionTime, effectiveCancelTransitionTime, firstTransition.getNextState());
+        final BusinessSubscriptionTransition cancelledBST = createExpectedCancelledBST(cancelledSubscriptionTransition.getId(), requestedCancelTransitionTime, effectiveCancelTransitionTime, firstBST.getNextSubscription());
         listener.handleSubscriptionTransitionChange(cancelledSubscriptionTransition);
-        Assert.assertEquals(dao.getTransitions(KEY).size(), 4);
-        Assert.assertEquals(dao.getTransitions(KEY).get(3), cancelledBST);
+        Assert.assertEquals(dao.getTransitions(KEY).size(), 2);
+        Assert.assertEquals(dao.getTransitions(KEY).get(1), cancelledBST);
     }
 
     private BusinessSubscriptionTransition createExpectedFirstBST(final UUID id, final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime)
@@ -107,20 +89,6 @@ public class TestAnalyticsListener
         return createExpectedBST(id, event, requestedTransitionTime, effectiveTransitionTime, null, subscriptionState);
     }
 
-    private BusinessSubscriptionTransition createExpectedPausedBST(final UUID id, final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime, final BusinessSubscription lastSubscription)
-    {
-        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPaused(plan);
-        final Subscription.SubscriptionState subscriptionState = Subscription.SubscriptionState.PAUSED;
-        return createExpectedBST(id, event, requestedTransitionTime, effectiveTransitionTime, lastSubscription, subscriptionState);
-    }
-
-    private BusinessSubscriptionTransition createExpectedResumedBST(final UUID id, final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime, final BusinessSubscription lastSubscription)
-    {
-        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionResumed(plan);
-        final Subscription.SubscriptionState nextState = Subscription.SubscriptionState.ACTIVE;
-        return createExpectedBST(id, event, requestedTransitionTime, effectiveTransitionTime, lastSubscription, nextState);
-    }
-
     private BusinessSubscriptionTransition createExpectedCancelledBST(final UUID id, final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime, final BusinessSubscription lastSubscription)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCancelled(plan);
@@ -175,23 +143,11 @@ public class TestAnalyticsListener
             nextState,
             plan,
             phase,
-            priceList
+            priceList,
+            true
         );
     }
 
-    private SubscriptionTransitionData createPauseSubscriptionTransition(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime, final Subscription.SubscriptionState previousState)
-    {
-        final ApiEventType eventType = ApiEventType.PAUSE;
-        final Subscription.SubscriptionState nextState = Subscription.SubscriptionState.PAUSED;
-        return createSubscriptionTransition(eventType, requestedTransitionTime, effectiveTransitionTime, previousState, nextState);
-    }
-
-    private SubscriptionTransitionData createResumeSubscriptionTransition(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime, final Subscription.SubscriptionState previousState)
-    {
-        final ApiEventType eventType = ApiEventType.RESUME;
-        final Subscription.SubscriptionState nextState = Subscription.SubscriptionState.ACTIVE;
-        return createSubscriptionTransition(eventType, requestedTransitionTime, effectiveTransitionTime, previousState, nextState);
-    }
 
     private SubscriptionTransitionData createCancelSubscriptionTransition(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime, final Subscription.SubscriptionState previousState)
     {
@@ -212,7 +168,8 @@ public class TestAnalyticsListener
             null,
             null,
             null,
-            null
+            null,
+            true
         );
     }
 
@@ -239,7 +196,8 @@ public class TestAnalyticsListener
             nextState,
             plan,
             phase,
-            priceList
+            priceList,
+            true
         );
     }
 }
\ No newline at end of file
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java b/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
index 5c626fc..a9ce2c7 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
@@ -19,13 +19,17 @@ package com.ning.billing.entitlement.api.user;
 import com.ning.billing.catalog.api.BillingPeriod;
 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.ProductCategory;
+import com.ning.billing.util.customfield.CustomizableEntity;
+
 import org.joda.time.DateTime;
 
 import java.util.List;
 import java.util.UUID;
 
 
-public interface Subscription {
+public interface Subscription extends CustomizableEntity {
 
     public void cancel(DateTime requestedDate, boolean eot)
     throws EntitlementUserApiException;
@@ -34,23 +38,16 @@ public interface Subscription {
     throws EntitlementUserApiException;
 
     public void changePlan(String productName, BillingPeriod term, String planSet, DateTime requestedDate)
-        throws EntitlementUserApiException ;
-
-    public void pause()
-        throws EntitlementUserApiException ;
-
-    public void resume()
-        throws EntitlementUserApiException ;
+        throws EntitlementUserApiException;
 
+    public void recreate(PlanPhaseSpecifier spec, DateTime requestedDate)
+        throws EntitlementUserApiException;
 
     public enum SubscriptionState {
         ACTIVE,
-        PAUSED,
         CANCELLED
     }
 
-    public UUID getId();
-
     public UUID getBundleId();
 
     public SubscriptionState getState();
@@ -69,6 +66,7 @@ public interface Subscription {
 
     public DateTime getPaidThroughDate();
 
+    public ProductCategory getCategory();
 
     public List<SubscriptionTransition> getActiveTransitions();
 
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java b/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
index f4c971d..43f96c6 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
@@ -30,8 +30,7 @@ public interface SubscriptionTransition extends BusEvent {
         MIGRATE_ENTITLEMENT,
         CREATE,
         CHANGE,
-        PAUSE,
-        RESUME,
+        RE_CREATE,
         CANCEL,
         UNCANCEL,
         PHASE
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index a90c6aa..340f2cb 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -37,13 +37,21 @@ public enum ErrorCode {
     ENT_CREATE_NO_BUNDLE(1012, "Bundle %s does not exist"),
     ENT_CREATE_NO_BP(1013, "Missing Base Subscription for bundle %s"),
     ENT_CREATE_BP_EXISTS(1015, "Subscription bundle %s already has a base subscription"),
+    ENT_CREATE_AO_BP_NON_ACTIVE(1017, "Can't create AddOn %s for non active Base Plan"),
+    ENT_CREATE_AO_ALREADY_INCLUDED(1018, "Can't create AddOn %s for BasePlan %s (Already included)"),
+    ENT_CREATE_AO_NOT_AVAILABLE(1019, "Can't create AddOn %s for BasePlan %s (Not available)"),
+
     /* Change plan */
-    ENT_CHANGE_NON_ACTIVE(1021, "Subscription %s is in state %s"),
-    ENT_CHANGE_FUTURE_CANCELLED(1022, "Subscription %s is future cancelled"),
+    ENT_CHANGE_NON_ACTIVE(1021, "Subscription %s is in state %s: Failed to change plan"),
+    ENT_CHANGE_FUTURE_CANCELLED(1022, "Subscription %s is future cancelled: Failed to change plan"),
     /* Cancellation */
-    ENT_CANCEL_BAD_STATE(1031, "Subscription %s is in state %s"),
+    ENT_CANCEL_BAD_STATE(1031, "Subscription %s is in state %s: Failed to cancel"),
+    /* Recreation */
+    ENT_RECREATE_BAD_STATE(1041, "Subscription %s is in state %s: Failed to recreate"),
+
     /* Un-cancellation */
-    ENT_UNCANCEL_BAD_STATE(1070, "Subscription %s was not in a cancelled state"),
+    ENT_UNCANCEL_BAD_STATE(1070, "Subscription %s was not in a cancelled state: Failed to uncancel plan"),
+
     /* Fetch */
     ENT_GET_NO_BUNDLE_FOR_SUBSCRIPTION(1080, "Could not find a bundle for subscription %s"),
     ENT_GET_INVALID_BUNDLE_ID(1081, "Could not find a bundle matching id %s"),
diff --git a/api/src/main/java/com/ning/billing/payment/api/CreditCardPaymentMethodInfo.java b/api/src/main/java/com/ning/billing/payment/api/CreditCardPaymentMethodInfo.java
index 572e362..bc3d372 100644
--- a/api/src/main/java/com/ning/billing/payment/api/CreditCardPaymentMethodInfo.java
+++ b/api/src/main/java/com/ning/billing/payment/api/CreditCardPaymentMethodInfo.java
@@ -16,8 +16,90 @@
 
 package com.ning.billing.payment.api;
 
+import org.codehaus.jackson.annotate.JsonCreator;
+import org.codehaus.jackson.annotate.JsonProperty;
+
 
 public final class CreditCardPaymentMethodInfo extends PaymentMethodInfo {
+    private final String cardHolderName;
+    private final String cardType;
+    private final String expirationDate;
+    private final String maskNumber;
+    private final String cardAddress1;
+    private final String cardAddress2;
+    private final String cardCity;
+    private final String cardState;
+    private final String cardPostalCode;
+    private final String cardCountry;
+
+    @JsonCreator
+    public CreditCardPaymentMethodInfo(@JsonProperty("id") String id,
+                                       @JsonProperty("accountId") String accountId,
+                                       @JsonProperty("defaultMethod") Boolean defaultMethod,
+                                       @JsonProperty("cardHolderName") String cardHolderName,
+                                       @JsonProperty("cardType") String cardType,
+                                       @JsonProperty("expirationDate") String expirationDate,
+                                       @JsonProperty("maskNumber") String maskNumber,
+                                       @JsonProperty("cardAddress1") String cardAddress1,
+                                       @JsonProperty("cardAddress2") String cardAddress2,
+                                       @JsonProperty("cardCity") String cardCity,
+                                       @JsonProperty("cardState") String cardState,
+                                       @JsonProperty("cardPostalCode") String cardPostalCode,
+                                       @JsonProperty("cardCountry") String cardCountry) {
+
+      super(id, accountId, defaultMethod, "CreditCard");
+      this.cardHolderName = cardHolderName;
+      this.cardType = cardType;
+      this.expirationDate = expirationDate;
+      this.maskNumber = maskNumber;
+      this.cardAddress1 = cardAddress1;
+      this.cardAddress2 = cardAddress2;
+      this.cardCity = cardCity;
+      this.cardState = cardState;
+      this.cardPostalCode = cardPostalCode;
+      this.cardCountry = cardCountry;
+    }
+
+    public String getCardHolderName() {
+      return cardHolderName;
+    }
+
+    public String getCardType() {
+      return cardType;
+    }
+
+    public String getCardAddress1() {
+        return cardAddress1;
+    }
+
+    public String getCardAddress2() {
+        return cardAddress2;
+    }
+
+    public String getCardCity() {
+        return cardCity;
+    }
+
+    public String getCardState() {
+        return cardState;
+    }
+
+    public String getCardPostalCode() {
+        return cardPostalCode;
+    }
+
+    public String getCardCountry() {
+        return cardCountry;
+    }
+
+    public String getExpirationDate() {
+      return expirationDate;
+    }
+
+    public String getMaskNumber() {
+      return maskNumber;
+    }
+
     public static final class Builder extends BuilderBase<CreditCardPaymentMethodInfo, Builder> {
         private String cardHolderName;
         private String cardType;
@@ -115,84 +197,6 @@ public final class CreditCardPaymentMethodInfo extends PaymentMethodInfo {
         }
     }
 
-    private final String cardHolderName;
-    private final String cardType;
-    private final String expirationDate;
-    private final String maskNumber;
-    private final String cardAddress1;
-    private final String cardAddress2;
-    private final String cardCity;
-    private final String cardState;
-    private final String cardPostalCode;
-    private final String cardCountry;
-
-    public CreditCardPaymentMethodInfo(String id,
-                                   String accountId,
-                                   Boolean defaultMethod,
-                                   String cardHolderName,
-                                   String cardType,
-                                   String expirationDate,
-                                   String maskNumber,
-                                   String cardAddress1,
-                                   String cardAddress2,
-                                   String cardCity,
-                                   String cardState,
-                                   String cardPostalCode,
-                                   String cardCountry) {
-
-      super(id, accountId, defaultMethod, "CreditCard");
-      this.cardHolderName = cardHolderName;
-      this.cardType = cardType;
-      this.expirationDate = expirationDate;
-      this.maskNumber = maskNumber;
-      this.cardAddress1 = cardAddress1;
-      this.cardAddress2 = cardAddress2;
-      this.cardCity = cardCity;
-      this.cardState = cardState;
-      this.cardPostalCode = cardPostalCode;
-      this.cardCountry = cardCountry;
-    }
-
-    public String getCardHolderName() {
-      return cardHolderName;
-    }
-
-    public String getCardType() {
-      return cardType;
-    }
-
-    public String getCardAddress1() {
-        return cardAddress1;
-    }
-
-    public String getCardAddress2() {
-        return cardAddress2;
-    }
-
-    public String getCardCity() {
-        return cardCity;
-    }
-
-    public String getCardState() {
-        return cardState;
-    }
-
-    public String getCardPostalCode() {
-        return cardPostalCode;
-    }
-
-    public String getCardCountry() {
-        return cardCountry;
-    }
-
-    public String getExpirationDate() {
-      return expirationDate;
-    }
-
-    public String getMaskNumber() {
-      return maskNumber;
-    }
-
     @Override
     public String toString() {
         return "CreditCardPaymentMethodInfo [cardHolderName=" + cardHolderName + ", cardType=" + cardType + ", expirationDate=" + expirationDate + ", maskNumber=" + maskNumber + ", cardAddress1=" + cardAddress1 + ", cardAddress2=" + cardAddress2 + ", cardCity=" + cardCity + ", cardState=" + cardState + ", cardPostalCode=" + cardPostalCode + ", cardCountry=" + cardCountry + "]";
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java b/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java
index b099739..8085310 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java
@@ -36,7 +36,6 @@ public class PaymentAttempt {
     private final DateTime invoiceDate;
     private final DateTime paymentAttemptDate;
     private final Integer retryCount;
-    private final DateTime nextRetryDate;
     private final DateTime createdDate;
     private final DateTime updatedDate;
 
@@ -49,7 +48,6 @@ public class PaymentAttempt {
                           DateTime paymentAttemptDate,
                           String paymentId,
                           Integer retryCount,
-                          DateTime nextRetryDate,
                           DateTime createdDate,
                           DateTime updatedDate) {
         this.paymentAttemptId = paymentAttemptId;
@@ -61,7 +59,6 @@ public class PaymentAttempt {
         this.paymentAttemptDate = paymentAttemptDate == null ? new DateTime(DateTimeZone.UTC) : paymentAttemptDate;
         this.paymentId = paymentId;
         this.retryCount = retryCount == null ? 0 : retryCount;
-        this.nextRetryDate = nextRetryDate;
         this.createdDate = createdDate == null ? new DateTime(DateTimeZone.UTC) : createdDate;
         this.updatedDate = updatedDate == null ? new DateTime(DateTimeZone.UTC) : updatedDate;
     }
@@ -74,8 +71,7 @@ public class PaymentAttempt {
                           DateTime invoiceDate,
                           DateTime paymentAttemptDate,
                           String paymentId,
-                          Integer retryCount,
-                          DateTime nextRetryDate) {
+                          Integer retryCount) {
         this(paymentAttemptId,
              invoiceId,
              accountId,
@@ -85,21 +81,20 @@ public class PaymentAttempt {
              paymentAttemptDate,
              paymentId,
              retryCount,
-             nextRetryDate,
              null,
              null);
     }
 
     public PaymentAttempt(UUID paymentAttemptId, UUID invoiceId, UUID accountId, BigDecimal amount, Currency currency, DateTime invoiceDate, DateTime paymentAttemptDate) {
-        this(paymentAttemptId, invoiceId, accountId, amount, currency, invoiceDate, paymentAttemptDate, null, null, null);
+        this(paymentAttemptId, invoiceId, accountId, amount, currency, invoiceDate, paymentAttemptDate, null, null);
     }
 
     public PaymentAttempt(UUID paymentAttemptId, UUID invoiceId, UUID accountId, DateTime invoiceDate, DateTime paymentAttemptDate) {
-        this(paymentAttemptId, invoiceId, accountId, null, null, invoiceDate, paymentAttemptDate, null, null, null);
+        this(paymentAttemptId, invoiceId, accountId, null, null, invoiceDate, paymentAttemptDate, null, null);
     }
 
     public PaymentAttempt(UUID paymentAttemptId, Invoice invoice) {
-        this(paymentAttemptId, invoice.getId(), invoice.getAccountId(), invoice.getBalance(), invoice.getCurrency(), invoice.getInvoiceDate(), null, null, null, null);
+        this(paymentAttemptId, invoice.getId(), invoice.getAccountId(), invoice.getBalance(), invoice.getCurrency(), invoice.getInvoiceDate(), null, null, null);
     }
 
     public DateTime getInvoiceDate() {
@@ -146,13 +141,9 @@ public class PaymentAttempt {
         return retryCount;
     }
 
-    public DateTime getNextRetryDate() {
-        return nextRetryDate;
-    }
-
     @Override
     public String toString() {
-        return "PaymentAttempt [paymentAttemptId=" + paymentAttemptId + ", invoiceId=" + invoiceId + ", accountId=" + accountId + ", amount=" + amount + ", currency=" + currency + ", paymentId=" + paymentId + ", invoiceDate=" + invoiceDate + ", paymentAttemptDate=" + paymentAttemptDate + ", retryCount=" + retryCount + ", nextRetryDate=" + nextRetryDate + ", createdDate=" + createdDate + ", updatedDate=" + updatedDate + "]";
+        return "PaymentAttempt [paymentAttemptId=" + paymentAttemptId + ", invoiceId=" + invoiceId + ", accountId=" + accountId + ", amount=" + amount + ", currency=" + currency + ", paymentId=" + paymentId + ", invoiceDate=" + invoiceDate + ", paymentAttemptDate=" + paymentAttemptDate + ", retryCount=" + retryCount + ", createdDate=" + createdDate + ", updatedDate=" + updatedDate + "]";
     }
 
     public Builder cloner() {
@@ -169,7 +160,6 @@ public class PaymentAttempt {
         private DateTime paymentAttemptDate;
         private String paymentId;
         private Integer retryCount;
-        private DateTime nextRetryDate;
         private DateTime createdDate;
         private DateTime updatedDate;
 
@@ -186,7 +176,6 @@ public class PaymentAttempt {
             this.paymentAttemptDate = src.paymentAttemptDate;
             this.paymentId = src.paymentId;
             this.retryCount = src.retryCount;
-            this.nextRetryDate = src.nextRetryDate;
             this.createdDate = src.createdDate;
             this.updatedDate = src.updatedDate;
         }
@@ -246,11 +235,6 @@ public class PaymentAttempt {
             return this;
         }
 
-        public Builder setNextRetryDate(DateTime nextRetryDate) {
-            this.nextRetryDate = nextRetryDate;
-            return this;
-        }
-
         public PaymentAttempt build() {
             return new PaymentAttempt(paymentAttemptId,
                                       invoiceId,
@@ -261,7 +245,6 @@ public class PaymentAttempt {
                                       paymentAttemptDate,
                                       paymentId,
                                       retryCount,
-                                      nextRetryDate,
                                       createdDate,
                                       updatedDate);
         }
@@ -279,7 +262,6 @@ public class PaymentAttempt {
                                 paymentAttemptDate,
                                 paymentId,
                                 retryCount,
-                                nextRetryDate,
                                 createdDate,
                                 updatedDate);
     }
@@ -297,8 +279,6 @@ public class PaymentAttempt {
         if (currency != that.currency) return false;
         if (invoiceDate != null ? !(getUnixTimestamp(invoiceDate) == getUnixTimestamp(that.invoiceDate)) : that.invoiceDate != null) return false;
         if (invoiceId != null ? !invoiceId.equals(that.invoiceId) : that.invoiceId != null) return false;
-        if (nextRetryDate != null ? !(getUnixTimestamp(nextRetryDate) == getUnixTimestamp(that.nextRetryDate)) : that.nextRetryDate != null)
-            return false;
         if (paymentAttemptDate != null ? !(getUnixTimestamp(paymentAttemptDate) == getUnixTimestamp(that.paymentAttemptDate)) : that.paymentAttemptDate != null)
             return false;
         if (paymentAttemptId != null ? !paymentAttemptId.equals(that.paymentAttemptId) : that.paymentAttemptId != null)
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentError.java b/api/src/main/java/com/ning/billing/payment/api/PaymentError.java
index d9b8c49..5541b01 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentError.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentError.java
@@ -43,6 +43,13 @@ public class PaymentError implements BusEvent {
         this.invoiceId = invoiceId;
     }
 
+    public PaymentError(String type, String message) {
+        this.type = type;
+        this.message = message;
+        this.accountId = null;
+        this.invoiceId = null;
+    }
+
     public String getType() {
         return type;
     }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBasic.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBasic.java
index 22065ac..fad00af 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBasic.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBasic.java
@@ -286,6 +286,7 @@ public class TestBasic {
         }
     }
 
+
     private void testBasePlanComplete(DateTime initialCreationDate, int billingDay,
                                       boolean proRationExpected) throws Exception {
         long DELAY = 5000;
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBusHandler.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBusHandler.java
index edc38a1..f915509 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBusHandler.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBusHandler.java
@@ -80,16 +80,6 @@ public class TestBusHandler {
             notifyIfStackEmpty();
 
             break;
-        case PAUSE:
-            assertEqualsNicely(NextEvent.PAUSE);
-            notifyIfStackEmpty();
-
-            break;
-        case RESUME:
-            assertEqualsNicely(NextEvent.RESUME);
-            notifyIfStackEmpty();
-
-            break;
         case UNCANCEL:
             assertEqualsNicely(NextEvent.UNCANCEL);
             notifyIfStackEmpty();
@@ -121,7 +111,7 @@ public class TestBusHandler {
     @Subscribe
     public void handlePaymentErrorEvents(PaymentError event) {
         log.info(String.format("TestBusHandler Got PaymentError event %s", event.toString()));
-        Assert.fail("Unexpected payment failure");
+        //Assert.fail("Unexpected payment failure");
     }
 
     public void reset() {
diff --git a/catalog/src/test/resources/WeaponsHire.xml b/catalog/src/test/resources/WeaponsHire.xml
index 01d7cb4..3d36b9d 100644
--- a/catalog/src/test/resources/WeaponsHire.xml
+++ b/catalog/src/test/resources/WeaponsHire.xml
@@ -51,13 +51,13 @@ Use Cases to do:
 	<products>
 		<product name="Pistol">
 			<category>BASE</category>
-			<available>
-				<addonProduct>Telescopic-Scope</addonProduct>
-				<addonProduct>Laser-Scope</addonProduct>
-			</available>
 		</product>
 		<product name="Shotgun">
 			<category>BASE</category>
+            <available>
+                <addonProduct>Telescopic-Scope</addonProduct>
+                <addonProduct>Laser-Scope</addonProduct>
+            </available>
 		</product>
 		<product name="Assault-Rifle">
 			<category>BASE</category>
@@ -91,33 +91,18 @@ Use Cases to do:
 				<phaseType>TRIAL</phaseType>
 				<policy>IMMEDIATE</policy>
 			</changePolicyCase>
-			<changePolicyCase> 
-				<toProduct>Pistol</toProduct>
-				<policy>END_OF_TERM</policy>
-			</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> 
-				<fromProduct>Pistol</fromProduct>
-				<toProduct>Shotgun</toProduct>
-				<policy>IMMEDIATE</policy>
-			</changePolicyCase>
-			<changePolicyCase> 
-				<fromProduct>Assault-Rifle</fromProduct>
-				<toProduct>Shotgun</toProduct>
-				<policy>END_OF_TERM</policy>
-			</changePolicyCase>
-			<changePolicyCase> 
-				<fromBillingPeriod>MONTHLY</fromBillingPeriod>
-				<toProduct>Assault-Rifle</toProduct>
-				<toBillingPeriod>MONTHLY</toBillingPeriod>
-				<policy>END_OF_TERM</policy>
-			</changePolicyCase>
-			<changePolicyCase> 
-				<toProduct>Assault-Rifle</toProduct>
-				<policy>IMMEDIATE</policy>
 			</changePolicyCase>
 			<changePolicyCase> 
 				<fromBillingPeriod>MONTHLY</fromBillingPeriod>
@@ -135,9 +120,6 @@ Use Cases to do:
 		</changePolicy>
 		<changeAlignment>
 			<changeAlignmentCase>
-				<alignment>START_OF_SUBSCRIPTION</alignment>
-			</changeAlignmentCase>
-			<changeAlignmentCase>
 				<toPriceList>rescue</toPriceList>
 				<alignment>CHANGE_OF_PLAN</alignment>
 			</changeAlignmentCase>
@@ -146,20 +128,27 @@ Use Cases to do:
 				<toPriceList>rescue</toPriceList>
 				<alignment>CHANGE_OF_PRICELIST</alignment>
 			</changeAlignmentCase>
+            <changeAlignmentCase>
+                <alignment>START_OF_SUBSCRIPTION</alignment>
+            </changeAlignmentCase>
 		</changeAlignment>
 		<cancelPolicy>
 			<cancelPolicyCase>
-				<policy>END_OF_TERM</policy>
-			</cancelPolicyCase>
-			<cancelPolicyCase>
 				<phaseType>TRIAL</phaseType>
 				<policy>IMMEDIATE</policy>
 			</cancelPolicyCase>
+            <cancelPolicyCase>
+                <policy>END_OF_TERM</policy>
+            </cancelPolicyCase>
 		</cancelPolicy>
 		<createAlignment>
-			<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>
@@ -447,6 +436,20 @@ Use Cases to do:
 		</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>
 			<finalPhase type="EVERGREEN">
 				<duration>
 					<unit>UNLIMITED</unit>
@@ -461,6 +464,20 @@ Use Cases to do:
 		</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>
 			<finalPhase type="EVERGREEN">
 				<duration>
 					<unit>UNLIMITED</unit>

entitlement/pom.xml 50(+26 -24)

diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index cf8d926..137fb52 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -8,7 +8,8 @@
     OR CONDITIONS OF ANY KIND, either express or implied. See the ~ License for 
     the specific language governing permissions and limitations ~ under the License. -->
 
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>com.ning.billing</groupId>
@@ -29,10 +30,6 @@
             <artifactId>jdbi-metrics</artifactId>
         </dependency>
         <dependency>
-            <groupId>mysql</groupId>
-            <artifactId>mysql-connector-java</artifactId>
-        </dependency>
-        <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
@@ -56,10 +53,10 @@
             <artifactId>killbill-catalog</artifactId>
             <scope>test</scope>
         </dependency>
+        <!-- Should be in test scope , but broken right now -->
         <dependency>
             <groupId>com.ning.billing</groupId>
             <artifactId>killbill-account</artifactId>
-            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>com.ning.billing</groupId>
@@ -72,15 +69,33 @@
             <artifactId>killbill-util</artifactId>
         </dependency>
         <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-account</artifactId>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>management</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>management-dbfiles</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <!-- Same here, this is really debatable whether or not we should keep that here -->
+        <dependency>
             <groupId>com.ning.billing</groupId>
             <artifactId>killbill-account</artifactId>
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <scope>runtime</scope>
+        </dependency>
         <dependency>
             <groupId>joda-time</groupId>
             <artifactId>joda-time</artifactId>
@@ -126,10 +141,10 @@
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
                 <configuration>
-                    <groups>setup,fast</groups>
+                    <groups>fast,slow</groups>
                 </configuration>
             </plugin>
-             <plugin>
+            <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>
                 <executions>
@@ -144,26 +159,13 @@
     </build>
     <profiles>
         <profile>
-            <id>test-sql</id>
-            <build>
-                <plugins>
-                    <plugin>
-                        <artifactId>maven-surefire-plugin</artifactId>
-                        <configuration>
-                            <groups>setup,sql</groups>
-                        </configuration>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-        <profile>
             <id>test-stress</id>
             <build>
                 <plugins>
                     <plugin>
                         <artifactId>maven-surefire-plugin</artifactId>
                         <configuration>
-                            <groups>setup,stress</groups>
+                            <groups>stress</groups>
                         </configuration>
                     </plugin>
                 </plugins>
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 b7574e1..82a83bf 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
@@ -21,6 +21,7 @@ import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.*;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 import com.ning.billing.util.clock.DefaultClock;
 import org.joda.time.DateTime;
@@ -61,10 +62,11 @@ public class PlanAligner  {
      * @throws CatalogApiException
      * @throws EntitlementUserApiException
      */
-    public TimedPhase [] getCurrentAndNextTimedPhaseOnCreate(SubscriptionData subscription,
-            Plan plan, PhaseType initialPhase, String priceList, DateTime requestedDate, DateTime effectiveDate)
+    public TimedPhase [] getCurrentAndNextTimedPhaseOnCreate(final SubscriptionData subscription,
+            final Plan plan, final PhaseType initialPhase, final String priceList, final DateTime requestedDate, final DateTime effectiveDate)
         throws CatalogApiException, EntitlementUserApiException {
-        List<TimedPhase> timedPhases = getTimedPhaseOnCreate(subscription, plan, initialPhase, priceList, requestedDate, effectiveDate);
+        List<TimedPhase> timedPhases = getTimedPhaseOnCreate(subscription.getStartDate(),
+                subscription.getBundleStartDate(), plan, initialPhase, priceList, requestedDate);
         TimedPhase [] result = new TimedPhase[2];
         result[0] = getTimedPhase(timedPhases, effectiveDate, WhichPhase.CURRENT);
         result[1] = getTimedPhase(timedPhases, effectiveDate, WhichPhase.NEXT);
@@ -83,8 +85,8 @@ public class PlanAligner  {
      * @throws CatalogApiException
      * @throws EntitlementUserApiException
      */
-    public TimedPhase getCurrentTimedPhaseOnChange(SubscriptionData subscription,
-            Plan plan, String priceList, DateTime requestedDate, DateTime effectiveDate)
+    public TimedPhase getCurrentTimedPhaseOnChange(final SubscriptionData subscription,
+            final Plan plan, final String priceList, final DateTime requestedDate, final DateTime effectiveDate)
         throws CatalogApiException, EntitlementUserApiException {
         return getTimedPhaseOnChange(subscription, plan, priceList, requestedDate, effectiveDate, WhichPhase.CURRENT);
     }
@@ -100,34 +102,65 @@ public class PlanAligner  {
      * @throws CatalogApiException
      * @throws EntitlementUserApiException
      */
-    public TimedPhase getNextTimedPhaseOnChange(SubscriptionData subscription,
-            Plan plan, String priceList, DateTime requestedDate, DateTime effectiveDate)
+    public TimedPhase getNextTimedPhaseOnChange(final SubscriptionData subscription,
+            final Plan plan, final String priceList, final DateTime requestedDate, final DateTime effectiveDate)
         throws CatalogApiException, EntitlementUserApiException {
         return getTimedPhaseOnChange(subscription, plan, priceList, requestedDate, effectiveDate, WhichPhase.NEXT);
     }
 
+
     /**
-     * Returns next future phase for that Plan based on effectiveDate
-     *
-     * @param plan
-     * @param initialPhase the initial phase that subscription started on that Plan
-     * @param effectiveDate the date used to consider what is future
-     * @param initialStartPhase the date for when we started on that Plan/initialPhase
-     * @return
-     * @throws EntitlementError
+     * Returns next Phase for that Subscription at a point in time
+     * <p>
+     * @param subscription the subscription for which we need to compute the next Phase event
+     * @param effectiveDate the date at which we look to compute that event. effective needs to be after last Plan change or initial Plan
+     * @return The PhaseEvent at the correct point in time
      */
-    public TimedPhase getNextTimedPhase(Plan plan, PhaseType initialPhase, DateTime effectiveDate, DateTime initialStartPhase)
-        throws EntitlementError {
+    public TimedPhase getNextTimedPhase(final SubscriptionData subscription, final DateTime requestedDate, final DateTime effectiveDate) {
         try {
-            List<TimedPhase> timedPhases = getPhaseAlignments(plan, initialPhase, initialStartPhase);
-            return getTimedPhase(timedPhases, effectiveDate, WhichPhase.NEXT);
-        } catch (EntitlementUserApiException e) {
-            throw new EntitlementError(String.format("Could not compute next phase change for plan %s with initialPhase %s", plan.getName(), initialPhase));
+
+            SubscriptionTransition lastPlanTransition = subscription.getInitialTransitionForCurrentPlan();
+            if (effectiveDate.isBefore(lastPlanTransition.getEffectiveTransitionTime())) {
+                throw new EntitlementError(String.format("Cannot specify an effectiveDate prior to last Plan Change, subscription = %s, effectiveDate = %s",
+                        subscription.getId(), effectiveDate));
+            }
+
+            switch(lastPlanTransition.getTransitionType()) {
+            // If we never had any Plan change, borrow the logics for createPlan alignment
+            case MIGRATE_ENTITLEMENT:
+            case CREATE:
+                List<TimedPhase> timedPhases = getTimedPhaseOnCreate(subscription.getStartDate(),
+                        subscription.getBundleStartDate(),
+                        lastPlanTransition.getNextPlan(),
+                        lastPlanTransition.getNextPhase().getPhaseType(),
+                        lastPlanTransition.getNextPriceList(),
+                        requestedDate);
+                return getTimedPhase(timedPhases, effectiveDate, WhichPhase.NEXT);
+            // If we went through Plan changes, borrow the logics for changePlan alignement
+            case CHANGE:
+                return getTimedPhaseOnChange(subscription.getStartDate(),
+                        subscription.getBundleStartDate(),
+                        lastPlanTransition.getPreviousPhase(),
+                        lastPlanTransition.getPreviousPlan(),
+                        lastPlanTransition.getPreviousPriceList(),
+                        lastPlanTransition.getNextPlan(),
+                        lastPlanTransition.getNextPriceList(),
+                        requestedDate,
+                        effectiveDate,
+                        WhichPhase.NEXT);
+            default:
+                throw new EntitlementError(String.format("Unexpectd initial transition %s for current plan %s on subscription %s",
+                        lastPlanTransition.getTransitionType(), subscription.getCurrentPlan(), subscription.getId()));
+            }
+        } catch (Exception /* EntitlementUserApiException, CatalogApiException */ e) {
+            throw new EntitlementError(String.format("Could not compute next phase change for subscription %s", subscription.getId()), e);
         }
     }
 
-    private List<TimedPhase> getTimedPhaseOnCreate(SubscriptionData subscription,
-            Plan plan, PhaseType initialPhase, String priceList, DateTime requestedDate, DateTime effectiveDate)
+
+    private List<TimedPhase> getTimedPhaseOnCreate(DateTime subscriptionStartDate,
+            DateTime bundleStartDate,
+            Plan plan, PhaseType initialPhase, String priceList, DateTime requestedDate)
         throws CatalogApiException, EntitlementUserApiException  {
 
         Catalog catalog = catalogService.getFullCatalog();
@@ -138,15 +171,13 @@ public class PlanAligner  {
                 priceList);
 
         DateTime planStartDate = null;
-        PlanAlignmentCreate alignement = null;
-        alignement = catalog.planCreateAlignment(planSpecifier, requestedDate);
-
+        PlanAlignmentCreate alignement = catalog.planCreateAlignment(planSpecifier, requestedDate);
         switch(alignement) {
         case START_OF_SUBSCRIPTION:
-            planStartDate = subscription.getStartDate();
+            planStartDate = subscriptionStartDate;
             break;
         case START_OF_BUNDLE:
-            planStartDate = subscription.getBundleStartDate();
+            planStartDate = bundleStartDate;
             break;
         default:
             throw new EntitlementError(String.format("Unknwon PlanAlignmentCreate %s", alignement));
@@ -155,36 +186,55 @@ public class PlanAligner  {
     }
 
     private TimedPhase getTimedPhaseOnChange(SubscriptionData subscription,
-            Plan plan, String priceList, DateTime requestedDate, DateTime effectiveDate, WhichPhase which)
+            Plan nextPlan, String nextPriceList, DateTime requestedDate, DateTime effectiveDate, WhichPhase which)
         throws CatalogApiException, EntitlementUserApiException {
+        return getTimedPhaseOnChange(subscription.getStartDate(),
+                subscription.getBundleStartDate(),
+                subscription.getCurrentPhase(),
+                subscription.getCurrentPlan(),
+                subscription.getCurrentPriceList(),
+                nextPlan,
+                nextPriceList,
+                requestedDate,
+                effectiveDate,
+                which);
+    }
 
-        Catalog catalog = catalogService.getFullCatalog();
 
-        PlanPhase currentPhase = subscription.getCurrentPhase();
-        Plan currentPlan = subscription.getCurrentPlan();
-        String currentPriceList = subscription.getCurrentPriceList();
+    private TimedPhase getTimedPhaseOnChange(DateTime subscriptionStartDate,
+            DateTime bundleStartDate,
+            PlanPhase currentPhase,
+            Plan currentPlan,
+            String currentPriceList,
+            Plan nextPlan, String priceList, DateTime requestedDate, DateTime effectiveDate, WhichPhase which)
+        throws CatalogApiException, EntitlementUserApiException {
+
+        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(),
-                currentPlan.getProduct().getCategory(),
+                currentCategory,
                 currentPlan.getBillingPeriod(),
                 currentPriceList,
                 currentPhase.getPhaseType());
 
-        PlanSpecifier toPlanSpecifier = new PlanSpecifier(plan.getProduct().getName(),
-                plan.getProduct().getCategory(),
-                plan.getBillingPeriod(),
+        PlanSpecifier toPlanSpecifier = new PlanSpecifier(nextPlan.getProduct().getName(),
+                nextPlan.getProduct().getCategory(),
+                nextPlan.getBillingPeriod(),
                 priceList);
 
         DateTime planStartDate = null;
-
-        PlanAlignmentChange alignment = null;
-        alignment = catalog.planChangeAlignment(fromPlanPhaseSpecifier, toPlanSpecifier, requestedDate);
+        PlanAlignmentChange alignment = catalog.planChangeAlignment(fromPlanPhaseSpecifier, toPlanSpecifier, requestedDate);
         switch(alignment) {
         case START_OF_SUBSCRIPTION:
-            planStartDate = subscription.getStartDate();
+            planStartDate = subscriptionStartDate;
             break;
         case START_OF_BUNDLE:
-            planStartDate = subscription.getBundleStartDate();
+            planStartDate = bundleStartDate;
             break;
         case CHANGE_OF_PLAN:
             planStartDate = requestedDate;
@@ -194,7 +244,7 @@ public class PlanAligner  {
         default:
             throw new EntitlementError(String.format("Unknwon PlanAlignmentChange %s", alignment));
         }
-        List<TimedPhase> timedPhases = getPhaseAlignments(plan, null, planStartDate);
+        List<TimedPhase> timedPhases = getPhaseAlignments(nextPlan, null, planStartDate);
         return getTimedPhase(timedPhases, effectiveDate, which);
     }
 
@@ -209,6 +259,7 @@ public class PlanAligner  {
         DateTime curPhaseStart = (initialPhase == null) ? initialPhaseStartDate : null;
         DateTime nextPhaseStart = null;
         for (PlanPhase cur : plan.getAllPhases()) {
+            // For create we can specify the phase so skip any phase until we reach initialPhase
             if (curPhaseStart == null) {
                 if (initialPhase != cur.getPhaseType()) {
                     continue;
@@ -252,7 +303,7 @@ public class PlanAligner  {
         case NEXT:
             return next;
         default:
-            throw new EntitlementError(String.format("Unepected %s TimedPhase", which));
+            throw new EntitlementError(String.format("Unexpected %s TimedPhase", which));
         }
     }
 }
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 7a1f9a9..0b372e8 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
@@ -18,20 +18,15 @@ package com.ning.billing.entitlement.api.migration;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
 
+import com.google.common.collect.Lists;
 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.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.alignment.MigrationPlanAligner;
 import com.ning.billing.entitlement.alignment.TimedMigration;
@@ -52,7 +47,6 @@ 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 {
 
@@ -60,19 +54,16 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
     private final EntitlementDao dao;
     private final MigrationPlanAligner migrationAligner;
     private final SubscriptionFactory factory;
-    private final CatalogService catalogService;
     private final Clock clock;
 
     @Inject
     public DefaultEntitlementMigrationApi(MigrationPlanAligner migrationAligner,
             SubscriptionFactory factory,
-            CatalogService catalogService,
             EntitlementDao dao,
             Clock clock) {
         this.dao = dao;
         this.migrationAligner = migrationAligner;
         this.factory = factory;
-        this.catalogService = catalogService;
         this.clock = clock;
     }
 
@@ -101,20 +92,39 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
             SubscriptionBundleData bundleData = new SubscriptionBundleData(curBundle.getBundleKey(), accountId);
             List<SubscriptionMigrationData> bundleSubscriptionData = new LinkedList<AccountMigrationData.SubscriptionMigrationData>();
 
-            for (EntitlementSubscriptionMigration curSub : curBundle.getSubscriptions()) {
+
+            List<EntitlementSubscriptionMigration> sortedSubscriptions = Lists.newArrayList(curBundle.getSubscriptions());
+            // Make sure we have first mpp or legacy, then addon and for each category order by CED
+            Collections.sort(sortedSubscriptions, new Comparator<EntitlementSubscriptionMigration>() {
+                @Override
+                public int compare(EntitlementSubscriptionMigration o1,
+                        EntitlementSubscriptionMigration o2) {
+                    if (o1.getCategory().equals(o2.getCategory())) {
+                        return o1.getSubscriptionCases()[0].getEffectiveDate().compareTo(o2.getSubscriptionCases()[0].getEffectiveDate());
+                    } else {
+                        if (o1.getCategory().equals("mpp")) {
+                            return -1;
+                        } else if (o2.getCategory().equals("mpp")) {
+                            return 1;
+                        } else if (o1.getCategory().equals("legacy")) {
+                            return -1;
+                        } else if (o2.getCategory().equals("legacy")) {
+                            return 1;
+                        } else {
+                            return 0;
+                        }
+                    }
+                }
+            });
+
+            DateTime bundleStartDate = null;
+            for (EntitlementSubscriptionMigration curSub : sortedSubscriptions) {
                 SubscriptionMigrationData data = null;
-                switch (curSub.getCategory()) {
-                case BASE:
-                    data = createBaseSubscriptionMigrationData(bundleData.getId(), curSub.getCategory(), curSub.getSubscriptionCases(), now);
-                    break;
-                case ADD_ON:
-                    // Not implemented yet
-                    break;
-                case STANDALONE:
-                    data = createStandaloneSubscriptionMigrationData(bundleData.getId(), curSub.getCategory(), curSub.getSubscriptionCases(), now);
-                    break;
-                default:
-                    throw new EntitlementMigrationApiException(String.format("Unkown product type ", curSub.getCategory()));
+                if (bundleStartDate == null) {
+                    data = createInitialSubscription(bundleData.getId(), curSub.getCategory(), curSub.getSubscriptionCases(), now);
+                    bundleStartDate = data.getInitialEvents().get(0).getEffectiveDate();
+                } else {
+                    data = createSubscriptionMigrationDataWithBundleDate(bundleData.getId(), curSub.getCategory(), curSub.getSubscriptionCases(), now, bundleStartDate);
                 }
                 if (data != null) {
                     bundleSubscriptionData.add(data);
@@ -127,7 +137,7 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
         return accountMigrationData;
     }
 
-    private SubscriptionMigrationData createBaseSubscriptionMigrationData(UUID bundleId, ProductCategory productCategory,
+    private SubscriptionMigrationData createInitialSubscription(UUID bundleId, ProductCategory productCategory,
             EntitlementSubscriptionMigrationCase [] input, DateTime now)
         throws EntitlementMigrationApiException {
 
@@ -144,8 +154,8 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
         return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, events));
     }
 
-    private SubscriptionMigrationData createStandaloneSubscriptionMigrationData(UUID bundleId, ProductCategory productCategory,
-            EntitlementSubscriptionMigrationCase [] input, DateTime now)
+    private SubscriptionMigrationData createSubscriptionMigrationDataWithBundleDate(UUID bundleId, ProductCategory productCategory,
+            EntitlementSubscriptionMigrationCase [] input, DateTime now, DateTime bundleStartDate)
     throws EntitlementMigrationApiException {
         TimedMigration [] events = migrationAligner.getEventsMigration(input, now);
         DateTime migrationStartDate= events[0].getEventTime();
@@ -154,7 +164,7 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
             .setId(UUID.randomUUID())
             .setBundleId(bundleId)
             .setCategory(productCategory)
-            .setBundleStartDate(migrationStartDate)
+            .setBundleStartDate(bundleStartDate)
             .setStartDate(migrationStartDate),
             emptyEvents);
         return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, events));
@@ -179,7 +189,8 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
                 .setActiveVersion(subscriptionData.getActiveVersion())
                 .setEffectiveDate(cur.getEventTime())
                 .setProcessedDate(now)
-                .setRequestedDate(now);
+                .setRequestedDate(now)
+                .setFromDisk(true);
 
                 switch(cur.getApiEventType()) {
                 case MIGRATE_ENTITLEMENT:
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
index ed05b02..1f312fd 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
@@ -27,7 +27,10 @@ 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.Product;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
 import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.engine.addon.AddonUtils;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 import com.ning.billing.util.clock.Clock;
@@ -39,14 +42,17 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
     private final EntitlementDao dao;
     private final CatalogService catalogService;
     private final SubscriptionApiService apiService;
+    private final AddonUtils addonUtils;
 
     @Inject
-    public DefaultEntitlementUserApi(Clock clock, EntitlementDao dao, CatalogService catalogService, SubscriptionApiService apiService) {
+    public DefaultEntitlementUserApi(Clock clock, EntitlementDao dao, CatalogService catalogService,
+            SubscriptionApiService apiService, AddonUtils addonUtils) {
         super();
         this.clock = clock;
         this.apiService = apiService;
         this.dao = dao;
         this.catalogService = catalogService;
+        this.addonUtils = addonUtils;
     }
 
     @Override
@@ -87,6 +93,7 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
         return dao.createSubscriptionBundle(bundle);
     }
 
+
     @Override
     public Subscription createSubscription(UUID bundleId, PlanPhaseSpecifier spec, DateTime requestedDate) throws EntitlementUserApiException {
         try {
@@ -96,12 +103,11 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
             if (requestedDate != null && requestedDate.isAfter(now)) {
                 throw new EntitlementUserApiException(ErrorCode.ENT_INVALID_REQUESTED_DATE, requestedDate.toString());
             }
-            requestedDate = (requestedDate == null) ? now : requestedDate;
             DateTime effectiveDate = requestedDate;
 
             Plan plan = catalogService.getFullCatalog().findPlan(spec.getProductName(), spec.getBillingPeriod(), realPriceList, requestedDate);
 
-            PlanPhase phase = (plan.getInitialPhases() != null) ? plan.getInitialPhases()[0] : plan.getFinalPhase();
+            PlanPhase phase = plan.getAllPhases()[0];
             if (phase == null) {
                 throw new EntitlementError(String.format("No initial PlanPhase for Product %s, term %s and set %s does not exist in the catalog",
                         spec.getProductName(), spec.getBillingPeriod().toString(), realPriceList));
@@ -113,12 +119,17 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
             }
 
             DateTime bundleStartDate = null;
-            Subscription baseSubscription = dao.getBaseSubscription(bundleId);
-
+            SubscriptionData baseSubscription = (SubscriptionData) dao.getBaseSubscription(bundleId);
             switch(plan.getProduct().getCategory()) {
             case BASE:
                 if (baseSubscription != null) {
-                    throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_BP_EXISTS, bundleId);
+                    if (baseSubscription.getState() == SubscriptionState.ACTIVE) {
+                        throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_BP_EXISTS, bundleId);
+                    } else {
+                        // If we do create on an existing CANCELLED BP, this is equivalent to call recreate on that Subscription.
+                        baseSubscription.recreate(spec, requestedDate);
+                        return baseSubscription;
+                    }
                 }
                 bundleStartDate = requestedDate;
                 break;
@@ -126,19 +137,27 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
                 if (baseSubscription == null) {
                     throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_NO_BP, bundleId);
                 }
+                checkAddonCreationRights(baseSubscription, plan);
                 bundleStartDate = baseSubscription.getStartDate();
                 break;
+            case STANDALONE:
+                if (baseSubscription != null) {
+                    throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_BP_EXISTS, bundleId);
+                }
+                // Not really but we don't care, there is no alignment for STANDALONE subscriptions
+                bundleStartDate = requestedDate;
+                break;
             default:
                 throw new EntitlementError(String.format("Can't create subscription of type %s",
                         plan.getProduct().getCategory().toString()));
             }
 
-            SubscriptionData subscription = apiService.createBasePlan(new SubscriptionBuilder()
-            .setId(UUID.randomUUID())
-            .setBundleId(bundleId)
-            .setCategory(plan.getProduct().getCategory())
-            .setBundleStartDate(bundleStartDate)
-            .setStartDate(effectiveDate),
+            SubscriptionData subscription = apiService.createPlan(new SubscriptionBuilder()
+                .setId(UUID.randomUUID())
+                .setBundleId(bundleId)
+                .setCategory(plan.getProduct().getCategory())
+                .setBundleStartDate(bundleStartDate)
+                .setStartDate(effectiveDate),
             plan, spec.getPhaseType(), realPriceList, requestedDate, effectiveDate, now);
 
             return subscription;
@@ -147,6 +166,26 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
         }
     }
 
+
+    private void checkAddonCreationRights(SubscriptionData baseSubscription, Plan targetAddOnPlan)
+        throws EntitlementUserApiException, CatalogApiException {
+
+        if (baseSubscription.getState() != SubscriptionState.ACTIVE) {
+            throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_AO_BP_NON_ACTIVE, targetAddOnPlan.getName());
+        }
+
+        Product baseProduct = baseSubscription.getCurrentPlan().getProduct();
+        if (addonUtils.isAddonIncluded(baseProduct, targetAddOnPlan)) {
+            throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_AO_ALREADY_INCLUDED,
+                    targetAddOnPlan.getName(), baseSubscription.getCurrentPlan().getProduct().getName());
+        }
+
+        if (!addonUtils.isAddonAvailable(baseProduct, targetAddOnPlan)) {
+            throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_AO_NOT_AVAILABLE,
+                    targetAddOnPlan.getName(), baseSubscription.getCurrentPlan().getProduct().getName());
+        }
+    }
+
 	@Override
 	public DateTime getNextBillingDate(UUID accountId) {
 		List<SubscriptionBundle> bundles = getBundlesForAccount(accountId);
@@ -155,7 +194,7 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
 			List<Subscription> subscriptions = getSubscriptionsForBundle(bundle.getId());
 			for(Subscription subscription : subscriptions) {
 				DateTime chargedThruDate = subscription.getChargedThroughDate();
-				if(result == null || 
+				if(result == null ||
 						(chargedThruDate != null && chargedThruDate.isBefore(result))) {
 					result = subscription.getChargedThroughDate();
 				}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
index d825e12..72cc142 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
@@ -28,6 +28,7 @@ import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.events.phase.PhaseEvent;
 import com.ning.billing.entitlement.events.phase.PhaseEventData;
 import com.ning.billing.entitlement.events.user.*;
+import com.ning.billing.entitlement.exceptions.EntitlementError;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.DefaultClock;
 import org.joda.time.DateTime;
@@ -52,14 +53,50 @@ public class SubscriptionApiService {
 
 
 
-    public SubscriptionData createBasePlan(SubscriptionBuilder builder, Plan plan, PhaseType initialPhase,
+    public SubscriptionData createPlan(SubscriptionBuilder builder, Plan plan, PhaseType initialPhase,
             String realPriceList, DateTime requestedDate, DateTime effectiveDate, DateTime processedDate)
         throws EntitlementUserApiException {
 
+        SubscriptionData subscription = new SubscriptionData(builder, this, clock);
+        createFromSubscription(subscription, plan, initialPhase, realPriceList, requestedDate, effectiveDate, processedDate, false);
+        return subscription;
+    }
+
+    public void recreatePlan(SubscriptionData subscription, PlanPhaseSpecifier spec, DateTime requestedDate)
+        throws EntitlementUserApiException {
+
+        SubscriptionState currentState = subscription.getState();
+        if (currentState != SubscriptionState.CANCELLED) {
+            throw new EntitlementUserApiException(ErrorCode.ENT_RECREATE_BAD_STATE, subscription.getId(), currentState);
+        }
+        DateTime now = clock.getUTCNow();
+        requestedDate = (requestedDate != null) ? DefaultClock.truncateMs(requestedDate) : now;
+        validateRequestedDate(subscription, now, requestedDate);
+
         try {
-            SubscriptionData subscription = new SubscriptionData(builder, this, clock);
+            String realPriceList = (spec.getPriceListName() == null) ? PriceListSet.DEFAULT_PRICELIST_NAME : spec.getPriceListName();
+            Plan plan = catalogService.getFullCatalog().findPlan(spec.getProductName(), spec.getBillingPeriod(), realPriceList, requestedDate);
+            PlanPhase phase = plan.getAllPhases()[0];
+            if (phase == null) {
+                throw new EntitlementError(String.format("No initial PlanPhase for Product %s, term %s and set %s does not exist in the catalog",
+                        spec.getProductName(), spec.getBillingPeriod().toString(), realPriceList));
+            }
+
+            DateTime effectiveDate = requestedDate;
+            DateTime processedDate = now;
 
+            createFromSubscription(subscription, plan, spec.getPhaseType(), realPriceList, requestedDate, effectiveDate, processedDate, true);
+        } catch (CatalogApiException e) {
+            throw new EntitlementUserApiException(e);
+        }
+    }
 
+
+    private void createFromSubscription(SubscriptionData subscription, Plan plan, PhaseType initialPhase,
+            String realPriceList, DateTime requestedDate, DateTime effectiveDate, DateTime processedDate, boolean reCreate)
+        throws EntitlementUserApiException {
+
+        try {
             TimedPhase [] curAndNextPhases = planAligner.getCurrentAndNextTimedPhaseOnCreate(subscription, plan, initialPhase, realPriceList, requestedDate, effectiveDate);
             ApiEventCreate creationEvent = new ApiEventCreate(new ApiEventBuilder()
             .setSubscriptionId(subscription.getId())
@@ -69,7 +106,8 @@ public class SubscriptionApiService {
             .setActiveVersion(subscription.getActiveVersion())
             .setProcessedDate(processedDate)
             .setEffectiveDate(effectiveDate)
-            .setRequestedDate(requestedDate));
+            .setRequestedDate(requestedDate)
+            .setFromDisk(true));
 
             TimedPhase nextTimedPhase = curAndNextPhases[1];
             PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
@@ -80,14 +118,20 @@ public class SubscriptionApiService {
             if (nextPhaseEvent != null) {
                 events.add(nextPhaseEvent);
             }
-            dao.createSubscription(subscription, events);
+            if (reCreate) {
+                dao.recreateSubscription(subscription.getId(), events);
+            } else {
+                dao.createSubscription(subscription, events);
+            }
             subscription.rebuildTransitions(events, catalogService.getFullCatalog());
-            return subscription;
         } catch (CatalogApiException e) {
             throw new EntitlementUserApiException(e);
         }
     }
 
+
+
+
     public void cancel(SubscriptionData subscription, DateTime requestedDate, boolean eot)
         throws EntitlementUserApiException {
 
@@ -99,7 +143,7 @@ public class SubscriptionApiService {
 
             DateTime now = clock.getUTCNow();
             requestedDate = (requestedDate != null) ? DefaultClock.truncateMs(requestedDate) : now;
-            validateRequestedDateOnChangeOrCancel(subscription, now, requestedDate);
+            validateRequestedDate(subscription, now, requestedDate);
 
             Plan currentPlan = subscription.getCurrentPlan();
             PlanPhaseSpecifier planPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
@@ -117,7 +161,8 @@ public class SubscriptionApiService {
             .setActiveVersion(subscription.getActiveVersion())
             .setProcessedDate(now)
             .setEffectiveDate(effectiveDate)
-            .setRequestedDate(now));
+            .setRequestedDate(now)
+            .setFromDisk(true));
 
             dao.cancelSubscription(subscription.getId(), cancelEvent);
             subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
@@ -128,7 +173,7 @@ public class SubscriptionApiService {
 
 
     public void uncancel(SubscriptionData subscription)
-    throws EntitlementUserApiException {
+        throws EntitlementUserApiException {
 
         if (!subscription.isSubscriptionFutureCancelled()) {
             throw new EntitlementUserApiException(ErrorCode.ENT_UNCANCEL_BAD_STATE, subscription.getId().toString());
@@ -140,13 +185,13 @@ public class SubscriptionApiService {
         .setActiveVersion(subscription.getActiveVersion())
         .setProcessedDate(now)
         .setRequestedDate(now)
-        .setEffectiveDate(now));
+        .setEffectiveDate(now)
+        .setFromDisk(true));
 
         List<EntitlementEvent> uncancelEvents = new ArrayList<EntitlementEvent>();
         uncancelEvents.add(uncancelEvent);
 
-        DateTime planStartDate = subscription.getCurrentPlanStart();
-        TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription.getCurrentPlan(), subscription.getInitialPhaseOnCurrentPlan().getPhaseType(), now, planStartDate);
+        TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, now, now);
         PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
                 PhaseEventData.createNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
                     null;
@@ -164,10 +209,9 @@ public class SubscriptionApiService {
 
         try {
 
-
             DateTime now = clock.getUTCNow();
             requestedDate = (requestedDate != null) ? DefaultClock.truncateMs(requestedDate) : now;
-            validateRequestedDateOnChangeOrCancel(subscription, now, requestedDate);
+            validateRequestedDate(subscription, now, requestedDate);
 
             String currentPriceList = subscription.getCurrentPriceList();
 
@@ -214,7 +258,8 @@ public class SubscriptionApiService {
             .setActiveVersion(subscription.getActiveVersion())
             .setProcessedDate(now)
             .setEffectiveDate(effectiveDate)
-            .setRequestedDate(now));
+            .setRequestedDate(now)
+            .setFromDisk(true));
 
             TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), requestedDate, effectiveDate);
             PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
@@ -233,7 +278,11 @@ public class SubscriptionApiService {
         }
     }
 
-    private void validateRequestedDateOnChangeOrCancel(SubscriptionData subscription, DateTime now, DateTime requestedDate)
+    public void commitCustomFields(SubscriptionData subscription) {
+        dao.saveCustomFields(subscription);
+    }
+
+    private void validateRequestedDate(SubscriptionData subscription, DateTime now, DateTime requestedDate)
         throws EntitlementUserApiException {
 
         if (requestedDate.isAfter(now) ) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundleData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundleData.java
index 8bdd584..8cc2573 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundleData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundleData.java
@@ -54,6 +54,7 @@ public class SubscriptionBundleData implements SubscriptionBundle {
         return accountId;
     }
 
+
     // STEPH do we need it ? and should we return that and when is that populated/updated?
     @Override
     public DateTime getStartDate() {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
index 48f24df..2efe1c5 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
@@ -16,8 +16,14 @@
 
 package com.ning.billing.entitlement.api.user;
 
-import com.ning.billing.ErrorCode;
-import com.ning.billing.catalog.api.*;
+import com.ning.billing.catalog.api.ActionPolicy;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Catalog;
+import com.ning.billing.catalog.api.CatalogApiException;
+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.ProductCategory;
 import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
@@ -26,6 +32,9 @@ import com.ning.billing.entitlement.events.user.ApiEvent;
 import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.customfield.CustomizableEntityBase;
+
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -37,7 +46,7 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
 
-public class SubscriptionData implements Subscription {
+public class SubscriptionData extends CustomizableEntityBase implements Subscription {
 
     private final static Logger log = LoggerFactory.getLogger(SubscriptionData.class);
 
@@ -46,7 +55,6 @@ public class SubscriptionData implements Subscription {
     //
     // Final subscription fields
     //
-    private final UUID id;
     private final UUID bundleId;
     private final DateTime startDate;
     private final DateTime bundleStartDate;
@@ -72,10 +80,9 @@ public class SubscriptionData implements Subscription {
     }
 
     public SubscriptionData(SubscriptionBuilder builder, SubscriptionApiService apiService, Clock clock) {
-        super();
+        super(builder.getId());
         this.apiService = apiService;
         this.clock = clock;
-        this.id = builder.getId();
         this.bundleId = builder.getBundleId();
         this.startDate = builder.getStartDate();
         this.bundleStartDate = builder.getBundleStartDate();
@@ -86,8 +93,27 @@ public class SubscriptionData implements Subscription {
     }
 
     @Override
-    public UUID getId() {
-        return id;
+    public String getObjectName() {
+        return "Subscription";
+    }
+
+
+    @Override
+    public void setFieldValue(String fieldName, String fieldValue) {
+        super.setFieldValue(fieldName, fieldValue);
+        apiService.commitCustomFields(this);
+    }
+
+    @Override
+    public void addFields(List<CustomField> fields) {
+        super.addFields(fields);
+        apiService.commitCustomFields(this);
+    }
+
+    @Override
+    public void clearFields() {
+        super.clearFields();
+        apiService.commitCustomFields(this);
     }
 
     @Override
@@ -151,13 +177,9 @@ public class SubscriptionData implements Subscription {
     }
 
     @Override
-    public void pause() throws EntitlementUserApiException {
-        throw new EntitlementUserApiException(ErrorCode.NOT_IMPLEMENTED);
-    }
-
-    @Override
-    public void resume() throws EntitlementUserApiException  {
-        throw new EntitlementUserApiException(ErrorCode.NOT_IMPLEMENTED);
+    public void recreate(PlanPhaseSpecifier spec, DateTime requestedDate)
+            throws EntitlementUserApiException {
+        apiService.recreatePlan(this, spec, requestedDate);
     }
 
     @Override
@@ -184,7 +206,7 @@ public class SubscriptionData implements Subscription {
         List<SubscriptionTransition> result = new ArrayList<SubscriptionTransition>();
         for (SubscriptionTransition cur : transitions) {
             result.add(cur);
-               }
+        }
         return result;
     }
 
@@ -208,9 +230,11 @@ public class SubscriptionData implements Subscription {
         }
 
         // ensure that the latestSubscription is always set; prevents NPEs
-        SubscriptionTransition latestSubscription = transitions.get(0);
-        for (SubscriptionTransition cur : transitions) {
-            if (cur.getEffectiveTransitionTime().isAfter(clock.getUTCNow())) {
+        SubscriptionTransitionData latestSubscription = transitions.get(0);
+        for (SubscriptionTransitionData cur : transitions) {
+            if (cur.getEffectiveTransitionTime().isAfter(clock.getUTCNow()) ||
+                    // We are not looking at events that were patched on the fly-- such as future ADDON cancelation from Base Plan
+                   !cur.isFromDisk()) {
                 break;
             }
             latestSubscription = cur;
@@ -235,6 +259,7 @@ public class SubscriptionData implements Subscription {
         return activeVersion;
     }
 
+    @Override
     public ProductCategory getCategory() {
         return category;
     }
@@ -253,15 +278,7 @@ public class SubscriptionData implements Subscription {
         return paidThroughDate;
     }
 
-    public DateTime getCurrentPlanStart() {
-        return getInitialTransitionForCurrentPlan().getEffectiveTransitionTime();
-    }
-
-    public PlanPhase getInitialPhaseOnCurrentPlan() {
-        return getInitialTransitionForCurrentPlan().getNextPhase();
-    }
-
-    private SubscriptionTransitionData getInitialTransitionForCurrentPlan() {
+    public SubscriptionTransitionData getInitialTransitionForCurrentPlan() {
         if (transitions == null) {
             throw new EntitlementError(String.format("No transitions for subscription %s", getId()));
         }
@@ -274,7 +291,8 @@ public class SubscriptionData implements Subscription {
                 continue;
             }
             if (cur.getEventType() == EventType.API_USER &&
-                    cur.getApiEventType() == ApiEventType.CHANGE) {
+                    (cur.getApiEventType() == ApiEventType.CHANGE ||
+                            cur.getApiEventType() == ApiEventType.RE_CREATE)) {
                 return cur;
             }
         }
@@ -329,11 +347,12 @@ public class SubscriptionData implements Subscription {
                 continue;
             }
             if (cur.getEventType() == EventType.PHASE
-                    || (cur.getEventType() == EventType.API_USER && cur.getApiEventType() == ApiEventType.CHANGE)) {
+                    || (cur.getEventType() == EventType.API_USER &&
+                            (cur.getApiEventType() == ApiEventType.CHANGE ||
+                                    cur.getApiEventType() == ApiEventType.RE_CREATE))) {
                 return cur.getEffectiveTransitionTime();
             }
         }
-
         // CREATE event
         return transitions.get(0).getEffectiveTransitionTime();
     }
@@ -366,6 +385,8 @@ public class SubscriptionData implements Subscription {
 
             ApiEventType apiEventType = null;
 
+            boolean isFromDisk = true;
+
             switch (cur.getType()) {
 
             case PHASE:
@@ -376,9 +397,11 @@ public class SubscriptionData implements Subscription {
             case API_USER:
                 ApiEvent userEV = (ApiEvent) cur;
                 apiEventType = userEV.getEventType();
+                isFromDisk = userEV.isFromDisk();
                 switch(apiEventType) {
                 case MIGRATE_ENTITLEMENT:
                 case CREATE:
+                case RE_CREATE:
                     nextState = SubscriptionState.ACTIVE;
                     nextPlanName = userEV.getEventPlan();
                     nextPhaseName = userEV.getEventPlanPhase();
@@ -389,12 +412,6 @@ public class SubscriptionData implements Subscription {
                     nextPhaseName = userEV.getEventPlanPhase();
                     nextPriceList = userEV.getPriceList();
                     break;
-                case PAUSE:
-                    nextState = SubscriptionState.PAUSED;
-                    break;
-                case RESUME:
-                    nextState = SubscriptionState.ACTIVE;
-                    break;
                 case CANCEL:
                     nextState = SubscriptionState.CANCELLED;
                     nextPlanName = null;
@@ -437,7 +454,8 @@ public class SubscriptionData implements Subscription {
                         nextState,
                         nextPlan,
                         nextPhase,
-                        nextPriceList);
+                        nextPriceList,
+                        isFromDisk);
             transitions.add(transition);
 
             previousState = nextState;
@@ -446,4 +464,5 @@ public class SubscriptionData implements Subscription {
             previousPriceList = nextPriceList;
         }
     }
+
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java
index 5a0eba0..fb8e2f2 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java
@@ -44,11 +44,12 @@ public class SubscriptionTransitionData implements SubscriptionTransition {
     private final String nextPriceList;
     private final Plan nextPlan;
     private final PlanPhase nextPhase;
+    private final boolean isFromDisk;
 
     public SubscriptionTransitionData(UUID eventId, UUID subscriptionId, UUID bundleId, EventType eventType,
             ApiEventType apiEventType, DateTime requestedTransitionTime, DateTime effectiveTransitionTime,
             SubscriptionState previousState, Plan previousPlan, PlanPhase previousPhase, String previousPriceList,
-            SubscriptionState nextState, Plan nextPlan, PlanPhase nextPhase, String nextPriceList) {
+            SubscriptionState nextState, Plan nextPlan, PlanPhase nextPhase, String nextPriceList, boolean isFromDisk) {
         super();
         this.eventId = eventId;
         this.subscriptionId = subscriptionId;
@@ -65,6 +66,7 @@ public class SubscriptionTransitionData implements SubscriptionTransition {
         this.nextPlan = nextPlan;
         this.nextPriceList = nextPriceList;
         this.nextPhase = nextPhase;
+        this.isFromDisk = isFromDisk;
     }
 
     @Override
@@ -136,14 +138,6 @@ public class SubscriptionTransitionData implements SubscriptionTransition {
         }
     }
 
-    public ApiEventType getApiEventType() {
-        return apiEventType;
-    }
-
-    public EventType getEventType() {
-        return eventType;
-    }
-
     @Override
     public DateTime getRequestedTransitionTime() {
         return requestedTransitionTime;
@@ -154,6 +148,19 @@ public class SubscriptionTransitionData implements SubscriptionTransition {
         return effectiveTransitionTime;
     }
 
+    public boolean isFromDisk() {
+        return isFromDisk;
+    }
+
+    public ApiEventType getApiEventType() {
+        return apiEventType;
+    }
+
+    public EventType getEventType() {
+        return eventType;
+    }
+
+
 
     @Override
     public String toString() {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/addon/AddonUtils.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/addon/AddonUtils.java
new file mode 100644
index 0000000..b2c9405
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/addon/AddonUtils.java
@@ -0,0 +1,89 @@
+/*
+ * 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.engine.addon;
+
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.Product;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.entitlement.exceptions.EntitlementError;
+
+public class AddonUtils {
+
+    private static final Logger logger = LoggerFactory.getLogger(AddonUtils.class);
+
+    private final CatalogService catalogService;
+
+    @Inject
+    public AddonUtils(CatalogService catalogService) {
+        this.catalogService = catalogService;
+    }
+
+
+    public boolean isAddonAvailable(final String basePlanName, final DateTime requestedDate, final Plan targetAddOnPlan) {
+        try {
+            Plan plan = catalogService.getFullCatalog().findPlan(basePlanName, requestedDate);
+            Product product = plan.getProduct();
+            return isAddonAvailable(product, targetAddOnPlan);
+        } catch (CatalogApiException e) {
+            throw new EntitlementError(e);
+        }
+    }
+
+    public boolean isAddonAvailable(final Product baseProduct, final Plan targetAddOnPlan) {
+        Product targetAddonProduct = targetAddOnPlan.getProduct();
+        Product[] availableAddOns = baseProduct.getAvailable();
+
+        for (Product curAv : availableAddOns) {
+            if (curAv.getName().equals(targetAddonProduct.getName())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean isAddonIncluded(final String basePlanName,  final DateTime requestedDate, final Plan targetAddOnPlan) {
+        try {
+            Plan plan = catalogService.getFullCatalog().findPlan(basePlanName, requestedDate);
+            Product product = plan.getProduct();
+            return isAddonIncluded(product, targetAddOnPlan);
+        } catch (CatalogApiException e) {
+            throw new EntitlementError(e);
+        }
+    }
+
+    public boolean isAddonIncluded(final Product baseProduct, final Plan targetAddOnPlan) {
+        Product targetAddonProduct = targetAddOnPlan.getProduct();
+        Product[] includedAddOns = baseProduct.getIncluded();
+        for (Product curAv : includedAddOns) {
+            if (curAv.getName().equals(targetAddonProduct.getName())) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
index 6832ed9..55ea2cf 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
@@ -16,13 +16,22 @@
 
 package com.ning.billing.entitlement.engine.core;
 
+
+
+import java.util.Iterator;
+import java.util.List;
 import java.util.UUID;
 
+
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
+
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.Product;
+import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.config.EntitlementConfig;
 import com.ning.billing.entitlement.alignment.PlanAligner;
 import com.ning.billing.entitlement.alignment.TimedPhase;
@@ -33,12 +42,18 @@ import com.ning.billing.entitlement.api.migration.DefaultEntitlementMigrationApi
 import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
 import com.ning.billing.entitlement.api.user.DefaultEntitlementUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.engine.addon.AddonUtils;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
 import com.ning.billing.entitlement.events.phase.PhaseEvent;
 import com.ning.billing.entitlement.events.phase.PhaseEventData;
+import com.ning.billing.entitlement.events.user.ApiEvent;
+import com.ning.billing.entitlement.events.user.ApiEventBuilder;
+import com.ning.billing.entitlement.events.user.ApiEventCancel;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 import com.ning.billing.lifecycle.LifecycleHandlerType;
 import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel;
@@ -64,7 +79,9 @@ public class Engine implements EventListener, EntitlementService {
     private final EntitlementUserApi userApi;
     private final EntitlementBillingApi billingApi;
     private final EntitlementMigrationApi migrationApi;
+    private final AddonUtils addonUtils;
     private final Bus eventBus;
+
     private final EntitlementConfig config;
     private final NotificationQueueService notificationQueueService;
 
@@ -74,7 +91,7 @@ public class Engine implements EventListener, EntitlementService {
     public Engine(Clock clock, EntitlementDao dao, PlanAligner planAligner,
             EntitlementConfig config, DefaultEntitlementUserApi userApi,
             DefaultEntitlementBillingApi billingApi,
-            DefaultEntitlementMigrationApi migrationApi, Bus eventBus,
+            DefaultEntitlementMigrationApi migrationApi, AddonUtils addonUtils, Bus eventBus,
             NotificationQueueService notificationQueueService) {
         super();
         this.clock = clock;
@@ -83,6 +100,7 @@ public class Engine implements EventListener, EntitlementService {
         this.userApi = userApi;
         this.billingApi = billingApi;
         this.migrationApi = migrationApi;
+        this.addonUtils = addonUtils;
         this.config = config;
         this.eventBus = eventBus;
         this.notificationQueueService = notificationQueueService;
@@ -172,8 +190,14 @@ public class Engine implements EventListener, EntitlementService {
             log.warn("Failed to retrieve subscription for id %s", event.getSubscriptionId());
             return;
         }
+        //
+        // Do any internal processing on that event before we send the event to the bus
+        //
         if (event.getType() == EventType.PHASE) {
-            insertNextPhaseEvent(subscription);
+            onPhaseEvent(subscription);
+        } else if (event.getType() == EventType.API_USER &&
+                subscription.getCategory() == ProductCategory.BASE) {
+            onBasePlanEvent(subscription, (ApiEvent) event);
         }
         try {
             eventBus.post(subscription.getTransitionFromEvent(event));
@@ -182,10 +206,11 @@ public class Engine implements EventListener, EntitlementService {
         }
     }
 
-    private void insertNextPhaseEvent(SubscriptionData subscription) {
+
+    private void onPhaseEvent(SubscriptionData subscription) {
         try {
             DateTime now = clock.getUTCNow();
-            TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription.getCurrentPlan(), subscription.getInitialPhaseOnCurrentPlan().getPhaseType(), now, subscription.getCurrentPlanStart());
+            TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, now, now);
             PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
                     PhaseEventData.createNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
                         null;
@@ -197,4 +222,38 @@ public class Engine implements EventListener, EntitlementService {
         }
     }
 
+    private void onBasePlanEvent(SubscriptionData baseSubscription, ApiEvent event) {
+
+        DateTime now = clock.getUTCNow();
+
+        Product baseProduct = (baseSubscription.getState() == SubscriptionState.CANCELLED ) ?
+                null : baseSubscription.getCurrentPlan().getProduct();
+
+        List<Subscription> subscriptions = dao.getSubscriptions(baseSubscription.getBundleId());
+
+        Iterator<Subscription> it = subscriptions.iterator();
+        while (it.hasNext()) {
+            SubscriptionData cur = (SubscriptionData) it.next();
+            if (cur.getState() == SubscriptionState.CANCELLED ||
+                    cur.getCategory() != ProductCategory.ADD_ON) {
+                continue;
+            }
+            Plan addonCurrentPlan = cur.getCurrentPlan();
+            if (baseProduct == null ||
+                    addonUtils.isAddonIncluded(baseProduct, addonCurrentPlan) ||
+                    ! addonUtils.isAddonAvailable(baseProduct, addonCurrentPlan)) {
+                //
+                // Perform AO cancellation using the effectiveDate of the BP
+                //
+                EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
+                .setSubscriptionId(cur.getId())
+                .setActiveVersion(cur.getActiveVersion())
+                .setProcessedDate(now)
+                .setEffectiveDate(event.getEffectiveDate())
+                .setRequestedDate(now)
+                .setFromDisk(true));
+                dao.cancelSubscription(cur.getId(), cancelEvent);
+            }
+        }
+    }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
index 5a04a47..fba1b3d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
@@ -21,12 +21,16 @@ import java.util.UUID;
 
 import org.joda.time.DateTime;
 
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.dao.AccountSqlDao;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.customfield.dao.FieldStoreDao;
 
 public interface EntitlementDao {
 
@@ -67,6 +71,8 @@ public interface EntitlementDao {
     // Subscription creation, cancellation, changePlan apis
     public void createSubscription(SubscriptionData subscription, List<EntitlementEvent> initialEvents);
 
+    public void recreateSubscription(UUID subscriptionId, List<EntitlementEvent> recreateEvents);
+
     public void cancelSubscription(UUID subscriptionId, EntitlementEvent cancelEvent);
 
     public void uncancelSubscription(UUID subscriptionId, List<EntitlementEvent> uncancelEvents);
@@ -77,4 +83,7 @@ public interface EntitlementDao {
 
     public void undoMigration(UUID accountId);
 
-	}
+    // Custom Fields
+    public void saveCustomFields(SubscriptionData subscription);
+}
+
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
index a1a3dc5..e34d48c 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
@@ -17,7 +17,9 @@
 package com.ning.billing.entitlement.engine.dao;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.Date;
 import java.util.List;
 import java.util.UUID;
@@ -30,8 +32,14 @@ import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
 import com.google.inject.Inject;
 import com.ning.billing.ErrorCode;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.dao.AccountSqlDao;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.Product;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
@@ -42,13 +50,19 @@ import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.api.user.SubscriptionFactory;
 import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.engine.addon.AddonUtils;
 import com.ning.billing.entitlement.engine.core.Engine;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
 import com.ning.billing.entitlement.events.user.ApiEvent;
+import com.ning.billing.entitlement.events.user.ApiEventBuilder;
+import com.ning.billing.entitlement.events.user.ApiEventCancel;
+import com.ning.billing.entitlement.events.user.ApiEventChange;
 import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.customfield.dao.FieldStoreDao;
 import com.ning.billing.util.notificationq.NotificationKey;
 import com.ning.billing.util.notificationq.NotificationQueue;
 import com.ning.billing.util.notificationq.NotificationQueueService;
@@ -65,16 +79,18 @@ public class EntitlementSqlDao implements EntitlementDao {
     private final EventSqlDao eventsDao;
     private final SubscriptionFactory factory;
     private final NotificationQueueService notificationQueueService;
+    private final AddonUtils addonUtils;
 
     @Inject
     public EntitlementSqlDao(final IDBI dbi, final Clock clock, final SubscriptionFactory factory,
-                             final NotificationQueueService notificationQueueService) {
+            final AddonUtils addonUtils, final NotificationQueueService notificationQueueService) {
         this.clock = clock;
         this.factory = factory;
         this.subscriptionsDao = dbi.onDemand(SubscriptionSqlDao.class);
         this.eventsDao = dbi.onDemand(EventSqlDao.class);
         this.bundlesDao = dbi.onDemand(BundleSqlDao.class);
         this.notificationQueueService = notificationQueueService;
+        this.addonUtils = addonUtils;
     }
 
     @Override
@@ -104,10 +120,6 @@ public class EntitlementSqlDao implements EntitlementDao {
         });
     }
 
-    @Override
-    public Subscription getSubscriptionFromId(final UUID subscriptionId) {
-        return buildSubscription(subscriptionsDao.getSubscriptionFromId(subscriptionId.toString()));
-    }
 
     @Override
     public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId) {
@@ -134,19 +146,18 @@ public class EntitlementSqlDao implements EntitlementDao {
 
     @Override
     public Subscription getBaseSubscription(final UUID bundleId) {
+        return getBaseSubscription(bundleId, true);
+    }
 
-        List<Subscription> subscriptions = subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString());
-        for (Subscription cur : subscriptions) {
-            if (((SubscriptionData)cur).getCategory() == ProductCategory.BASE) {
-                return  buildSubscription(cur);
-            }
-        }
-        return null;
+
+    @Override
+    public Subscription getSubscriptionFromId(final UUID subscriptionId) {
+        return buildSubscription(subscriptionsDao.getSubscriptionFromId(subscriptionId.toString()));
     }
 
     @Override
     public List<Subscription> getSubscriptions(UUID bundleId) {
-        return buildSubscription(subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString()));
+        return buildBundleSubscriptions(subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString()));
     }
 
     @Override
@@ -155,16 +166,29 @@ public class EntitlementSqlDao implements EntitlementDao {
         if (bundle == null) {
             return Collections.emptyList();
         }
-        return buildSubscription(subscriptionsDao.getSubscriptionsFromBundleId(bundle.getId().toString()));
+        return getSubscriptions(bundle.getId());
     }
 
     @Override
-    public void updateSubscription(SubscriptionData subscription) {
-        Date ctd = (subscription.getChargedThroughDate() != null)  ? subscription.getChargedThroughDate().toDate() : null;
-        Date ptd = (subscription.getPaidThroughDate() != null)  ? subscription.getPaidThroughDate().toDate() : null;
-        subscriptionsDao.updateSubscription(subscription.getId().toString(), subscription.getActiveVersion(), ctd, ptd);
+    public void updateSubscription(final SubscriptionData subscription) {
+
+        final Date ctd = (subscription.getChargedThroughDate() != null)  ? subscription.getChargedThroughDate().toDate() : null;
+        final Date ptd = (subscription.getPaidThroughDate() != null)  ? subscription.getPaidThroughDate().toDate() : null;
+
+
+        subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
+
+            @Override
+            public Void inTransaction(SubscriptionSqlDao transactionalDao,
+                    TransactionStatus status) throws Exception {
+                transactionalDao.updateSubscription(subscription.getId().toString(), subscription.getActiveVersion(), ctd, ptd);
+                return null;
+            }
+        });
     }
 
+
+
     @Override
     public void createNextPhaseEvent(final UUID subscriptionId, final EntitlementEvent nextPhase) {
         eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
@@ -237,12 +261,38 @@ public class EntitlementSqlDao implements EntitlementDao {
     }
 
     @Override
+    public void recreateSubscription(final UUID subscriptionId,
+            final List<EntitlementEvent> recreateEvents) {
+
+        eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
+            @Override
+            public Void inTransaction(EventSqlDao dao,
+                    TransactionStatus status) throws Exception {
+
+                for (final EntitlementEvent cur : recreateEvents) {
+                    dao.insertEvent(cur);
+                    recordFutureNotificationFromTransaction(dao,
+                            cur.getEffectiveDate(),
+                            new NotificationKey() {
+                        @Override
+                        public String toString() {
+                            return cur.getId().toString();
+                        }
+                    });
+                }
+                return null;
+            }
+        });
+    }
+
+    @Override
     public void cancelSubscription(final UUID subscriptionId, final EntitlementEvent cancelEvent) {
 
         eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
             @Override
             public Void inTransaction(EventSqlDao dao,
                     TransactionStatus status) throws Exception {
+                cancelNextCancelEventFromTransaction(subscriptionId, dao);
                 cancelNextChangeEventFromTransaction(subscriptionId, dao);
                 cancelNextPhaseEventFromTransaction(subscriptionId, dao);
                 dao.insertEvent(cancelEvent);
@@ -332,6 +382,10 @@ public class EntitlementSqlDao implements EntitlementDao {
         cancelFutureEventFromTransaction(subscriptionId, dao, EventType.API_USER, ApiEventType.CHANGE);
     }
 
+    private void cancelNextCancelEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao) {
+        cancelFutureEventFromTransaction(subscriptionId, dao, EventType.API_USER, ApiEventType.CANCEL);
+    }
+
     private void cancelFutureEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao, EventType type, ApiEventType apiType) {
 
         UUID futureEventId = null;
@@ -354,18 +408,110 @@ public class EntitlementSqlDao implements EntitlementDao {
         }
     }
 
+    private void updateCustomFieldsFromTransaction(SubscriptionSqlDao transactionalDao, final SubscriptionData subscription) {
+
+        String SubscriptionId = subscription.getId().toString();
+        String objectType = subscription.getObjectName();
+
+        FieldStoreDao fieldStoreDao = transactionalDao.become(FieldStoreDao.class);
+        fieldStoreDao.clear(SubscriptionId, objectType);
+
+        List<CustomField> fieldList = subscription.getFieldList();
+        if (fieldList != null) {
+            fieldStoreDao.batchSaveFromTransaction(SubscriptionId, objectType, fieldList);
+        }
+    }
+
     private Subscription buildSubscription(Subscription input) {
         if (input == null) {
             return null;
         }
-        return buildSubscription(Collections.singletonList(input)).get(0);
+        List<Subscription> bundleInput = new ArrayList<Subscription>();
+        Subscription baseSubscription = null;
+        if (input.getCategory() == ProductCategory.ADD_ON) {
+            baseSubscription = getBaseSubscription(input.getBundleId(), false);
+            bundleInput.add(baseSubscription);
+            bundleInput.add(input);
+        } else {
+            bundleInput.add(input);
+        }
+        List<Subscription> reloadedSubscriptions = buildBundleSubscriptions(bundleInput);
+        for (Subscription cur : reloadedSubscriptions) {
+            if (cur.getId().equals(input.getId())) {
+                return cur;
+            }
+         }
+         throw new EntitlementError(String.format("Unexpected code path in buildSubscription"));
     }
 
-    private List<Subscription> buildSubscription(List<Subscription> input) {
-        List<Subscription> result = new ArrayList<Subscription>(input.size());
+
+
+    private List<Subscription> buildBundleSubscriptions(List<Subscription> input) {
+
+        // Make sure BasePlan -- if exists-- is first
+        Collections.sort(input, new Comparator<Subscription>() {
+            @Override
+            public int compare(Subscription o1, Subscription o2) {
+                if (o1.getCategory() == ProductCategory.BASE) {
+                    return -1;
+                } else if (o2.getCategory() == ProductCategory.BASE) {
+                    return 1;
+                } else {
+                    return o1.getStartDate().compareTo(o2.getStartDate());
+                }
+            }
+        });
+
+        EntitlementEvent futureBaseEvent = null;
+                List<Subscription> result = new ArrayList<Subscription>(input.size());
         for (Subscription cur : input) {
+
             List<EntitlementEvent> events = eventsDao.getEventsForSubscription(cur.getId().toString());
-            Subscription reloaded =   factory.createSubscription(new SubscriptionBuilder((SubscriptionData) cur), events);
+            Subscription reloaded = factory.createSubscription(new SubscriptionBuilder((SubscriptionData) cur), events);
+
+            switch (cur.getCategory()) {
+            case BASE:
+                Collection<EntitlementEvent> futureApiEvents = Collections2.filter(events, new Predicate<EntitlementEvent>() {
+                    @Override
+                    public boolean apply(EntitlementEvent input) {
+                        return (input.getEffectiveDate().isAfter(clock.getUTCNow()) &&
+                                ((input instanceof ApiEventCancel) || (input instanceof ApiEventChange)));
+                    }
+                });
+                futureBaseEvent = (futureApiEvents.size() == 0) ? null : futureApiEvents.iterator().next();
+                break;
+
+            case ADD_ON:
+                Plan targetAddOnPlan = reloaded.getCurrentPlan();
+                String baseProductName = (futureBaseEvent instanceof ApiEventChange) ?
+                        ((ApiEventChange) futureBaseEvent).getEventPlan() : null;
+
+                boolean createCancelEvent = (futureBaseEvent != null) &&
+                    ((futureBaseEvent instanceof ApiEventCancel) ||
+                            ((! addonUtils.isAddonAvailable(baseProductName, futureBaseEvent.getEffectiveDate(), targetAddOnPlan)) ||
+                                    (addonUtils.isAddonIncluded(baseProductName, futureBaseEvent.getEffectiveDate(), targetAddOnPlan))));
+
+                if (createCancelEvent) {
+                    DateTime now = clock.getUTCNow();
+                    EntitlementEvent addOnCancelEvent = new ApiEventCancel(new ApiEventBuilder()
+                    .setSubscriptionId(reloaded.getId())
+                    .setActiveVersion(((SubscriptionData) reloaded).getActiveVersion())
+                    .setProcessedDate(now)
+                    .setEffectiveDate(futureBaseEvent.getEffectiveDate())
+                    .setRequestedDate(now)
+                    // This event is only there to indicate the ADD_ON is future canceled, but it is not there
+                    // on disk until the base plan cancellation becomes effective
+                    .setFromDisk(false));
+
+                    events.add(addOnCancelEvent);
+                    // Finally reload subscription with full set of events
+                    reloaded = factory.createSubscription(new SubscriptionBuilder((SubscriptionData) cur), events);
+                }
+                break;
+            default:
+                break;
+            }
+            loadCustomFields(reloaded);
             result.add(reloaded);
         }
         return result;
@@ -411,6 +557,16 @@ public class EntitlementSqlDao implements EntitlementDao {
     }
 
 
+    public Subscription getBaseSubscription(final UUID bundleId, boolean rebuildSubscription) {
+        List<Subscription> subscriptions = subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString());
+        for (Subscription cur : subscriptions) {
+            if (((SubscriptionData)cur).getCategory() == ProductCategory.BASE) {
+                return  rebuildSubscription ? buildSubscription(cur) : cur;
+            }
+        }
+        return null;
+    }
+
     @Override
     public void undoMigration(final UUID accountId) {
 
@@ -449,4 +605,26 @@ public class EntitlementSqlDao implements EntitlementDao {
             throw new RuntimeException(e);
         }
     }
+
+    @Override
+    public void saveCustomFields(final SubscriptionData subscription) {
+        subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
+            @Override
+            public Void inTransaction(SubscriptionSqlDao transactionalDao,
+                    TransactionStatus status) throws Exception {
+                updateCustomFieldsFromTransaction(transactionalDao, subscription);
+                return null;
+            }
+        });
+    }
+
+
+    private void loadCustomFields(final Subscription subscription) {
+        FieldStoreDao fieldStoreDao = subscriptionsDao.become(FieldStoreDao.class);
+        List<CustomField> fields = fieldStoreDao.load(subscription.getId().toString(), subscription.getObjectName());
+        subscription.clearFields();
+        if (fields != null) {
+            subscription.addFields(fields);
+        }
+    }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
index 5f485e5..67c60c0 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
@@ -139,20 +139,21 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
                     .setEventPlan(planName)
                     .setEventPlanPhase(phaseName)
                     .setEventPriceList(priceListName)
-                    .setEventType(userType);
+                    .setEventType(userType)
+                    .setFromDisk(true);
 
                 if (userType == ApiEventType.CREATE) {
                     result = new ApiEventCreate(builder);
+                } else if (userType == ApiEventType.RE_CREATE) {
+                    result = new ApiEventReCreate(builder);
                 } else if (userType == ApiEventType.MIGRATE_ENTITLEMENT) {
                     result = new ApiEventMigrate(builder);
                 } else if (userType == ApiEventType.CHANGE) {
                     result = new ApiEventChange(builder);
                 } else if (userType == ApiEventType.CANCEL) {
                     result = new ApiEventCancel(builder);
-                } else if (userType == ApiEventType.PAUSE) {
-                    result = new ApiEventPause(builder);
-                } else if (userType == ApiEventType.RESUME) {
-                    result = new ApiEventResume(builder);
+                } else if (userType == ApiEventType.RE_CREATE) {
+                    result = new ApiEventReCreate(builder);
                 } else if (userType == ApiEventType.UNCANCEL) {
                     result = new ApiEventUncancel(builder);
                 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEvent.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEvent.java
index ecd5aa1..c26b168 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEvent.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEvent.java
@@ -29,4 +29,6 @@ public interface ApiEvent extends EntitlementEvent {
 
     public String getPriceList();
 
+    public boolean isFromDisk();
+
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java
index 2438ddf..f67c6b7 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java
@@ -28,7 +28,7 @@ public class ApiEventBase extends EventBase implements ApiEvent {
     private final String eventPlan;
     private final String eventPlanPhase;
     private final String eventPriceList;
-
+    private final boolean fromDisk;
 
     public ApiEventBase(ApiEventBuilder builder) {
         super(builder);
@@ -36,9 +36,10 @@ public class ApiEventBase extends EventBase implements ApiEvent {
         this.eventPriceList = builder.getEventPriceList();
         this.eventPlan = builder.getEventPlan();
         this.eventPlanPhase = builder.getEventPlanPhase();
+        this.fromDisk = builder.isFromDisk();
     }
 
-
+/*
     public ApiEventBase(UUID subscriptionId, DateTime bundleStartDate, DateTime processed, String planName, String phaseName,
             String priceList, DateTime requestedDate,  ApiEventType eventType, DateTime effectiveDate, long activeVersion) {
         super(subscriptionId, requestedDate, effectiveDate, processed, activeVersion, true);
@@ -56,7 +57,7 @@ public class ApiEventBase extends EventBase implements ApiEvent {
         this.eventPlan = null;
         this.eventPlanPhase = null;
     }
-
+*/
 
     @Override
     public ApiEventType getEventType() {
@@ -83,6 +84,11 @@ public class ApiEventBase extends EventBase implements ApiEvent {
         return eventPriceList;
     }
 
+    @Override
+    public boolean isFromDisk() {
+        return fromDisk;
+    }
+
 
     @Override
     public String toString() {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBuilder.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBuilder.java
index 2ff026b..b7e9764 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBuilder.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBuilder.java
@@ -24,6 +24,8 @@ public class ApiEventBuilder extends EventBaseBuilder<ApiEventBuilder> {
     private String eventPlan;
     private String eventPlanPhase;
     private String eventPriceList;
+    private boolean fromDisk;
+
 
     public ApiEventBuilder() {
         super();
@@ -49,6 +51,15 @@ public class ApiEventBuilder extends EventBaseBuilder<ApiEventBuilder> {
         return eventPriceList;
     }
 
+    public boolean isFromDisk() {
+        return fromDisk;
+    }
+
+    public ApiEventBuilder setFromDisk(boolean fromDisk) {
+        this.fromDisk = fromDisk;
+        return this;
+    }
+
     public ApiEventBuilder setEventType(ApiEventType eventType) {
         this.eventType = eventType;
         return this;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
index b994899..ec56655 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
@@ -32,13 +32,9 @@ public enum ApiEventType {
         @Override
         public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.CHANGE; }
     },
-    PAUSE {
+    RE_CREATE {
         @Override
-        public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.PAUSE; }
-    },
-    RESUME {
-        @Override
-        public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.RESUME; }
+        public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.RE_CREATE; }
     },
     CANCEL {
         @Override
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java b/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
index 4f0dfec..704b765 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
@@ -30,6 +30,7 @@ import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
 import com.ning.billing.entitlement.api.user.DefaultEntitlementUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.SubscriptionApiService;
+import com.ning.billing.entitlement.engine.addon.AddonUtils;
 import com.ning.billing.entitlement.engine.core.Engine;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.engine.dao.EntitlementSqlDao;
@@ -54,6 +55,7 @@ public class EntitlementModule extends AbstractModule {
         bind(EntitlementService.class).to(Engine.class).asEagerSingleton();
         bind(Engine.class).asEagerSingleton();
         bind(PlanAligner.class).asEagerSingleton();
+        bind(AddonUtils.class).asEagerSingleton();
         bind(MigrationPlanAligner.class).asEagerSingleton();
         bind(EntitlementUserApi.class).to(DefaultEntitlementUserApi.class).asEagerSingleton();
         bind(EntitlementBillingApi.class).to(DefaultEntitlementBillingApi.class).asEagerSingleton();
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java
index 39f6c48..5e8e1ef 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java
@@ -38,10 +38,9 @@ public class ApiTestListener {
     public enum NextEvent {
         MIGRATE_ENTITLEMENT,
         CREATE,
+        RE_CREATE,
         CHANGE,
         CANCEL,
-        PAUSE,
-        RESUME,
         PHASE
     }
 
@@ -59,18 +58,15 @@ public class ApiTestListener {
         case CREATE:
             subscriptionCreated(event);
             break;
+        case RE_CREATE:
+            subscriptionReCreated(event);
+            break;
         case CANCEL:
             subscriptionCancelled(event);
             break;
         case CHANGE:
             subscriptionChanged(event);
             break;
-        case PAUSE:
-            subscriptionPaused(event);
-            break;
-        case RESUME:
-            subscriptionResumed(event);
-            break;
         case UNCANCEL:
             break;
         case PHASE:
@@ -151,6 +147,12 @@ public class ApiTestListener {
         notifyIfStackEmpty();
     }
 
+    public void subscriptionReCreated(SubscriptionTransition recreated) {
+        log.debug("-> Got event RE_CREATED");
+        assertEqualsNicely(NextEvent.RE_CREATE);
+        notifyIfStackEmpty();
+    }
+
 
     public void subscriptionCancelled(SubscriptionTransition cancelled) {
         log.debug("-> Got event CANCEL");
@@ -166,20 +168,6 @@ public class ApiTestListener {
     }
 
 
-    public void subscriptionPaused(SubscriptionTransition paused) {
-        log.debug("-> Got event PAUSE");
-        assertEqualsNicely(NextEvent.PAUSE);
-        notifyIfStackEmpty();
-    }
-
-
-    public void subscriptionResumed(SubscriptionTransition resumed) {
-        log.debug("-> Got event RESUME");
-        assertEqualsNicely(NextEvent.RESUME);
-        notifyIfStackEmpty();
-    }
-
-
     public void subscriptionPhaseChanged(
             SubscriptionTransition phaseChanged) {
         log.debug("-> Got event PHASE");
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultBillingEvent.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultBillingEvent.java
index 100d184..fc3362a 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultBillingEvent.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultBillingEvent.java
@@ -40,6 +40,8 @@ import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionTransition.SubscriptionTransitionType;
+import com.ning.billing.mock.BrainDeadProxyFactory;
+import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
 
 public class TestDefaultBillingEvent {
 	public static final UUID ID_ZERO = new UUID(0L,0L);
@@ -48,88 +50,88 @@ public class TestDefaultBillingEvent {
 
 	@Test(groups={"fast"})
 	public void testEventOrderingSubscription() {
-	
+
 		BillingEvent event0 = createEvent(subscription(ID_ZERO), new DateTime("2012-01-31T00:02:04.000Z"), SubscriptionTransitionType.CREATE);
 		BillingEvent event1 = createEvent(subscription(ID_ONE), new DateTime("2012-01-31T00:02:04.000Z"), SubscriptionTransitionType.CREATE);
 		BillingEvent event2 = createEvent(subscription(ID_TWO), new DateTime("2012-01-31T00:02:04.000Z"), SubscriptionTransitionType.CREATE);
-		
+
 		SortedSet<BillingEvent> set = new TreeSet<BillingEvent>();
 		set.add(event2);
 		set.add(event1);
 		set.add(event0);
-		
+
 		Iterator<BillingEvent> it = set.iterator();
-		
+
 		Assert.assertEquals(event0, it.next());
 		Assert.assertEquals(event1, it.next());
 		Assert.assertEquals(event2, it.next());
 	}
-	
+
 	@Test(groups={"fast"})
 	public void testEventOrderingDate() {
-	
+
 		BillingEvent event0 = createEvent(subscription(ID_ZERO), new DateTime("2012-01-01T00:02:04.000Z"), SubscriptionTransitionType.CREATE);
 		BillingEvent event1 = createEvent(subscription(ID_ZERO), new DateTime("2012-02-01T00:02:04.000Z"), SubscriptionTransitionType.CREATE);
 		BillingEvent event2 = createEvent(subscription(ID_ZERO), new DateTime("2012-03-01T00:02:04.000Z"), SubscriptionTransitionType.CREATE);
-		
+
 		SortedSet<BillingEvent> set = new TreeSet<BillingEvent>();
 		set.add(event2);
 		set.add(event1);
 		set.add(event0);
-		
+
 		Iterator<BillingEvent> it = set.iterator();
-		
+
 		Assert.assertEquals(event0, it.next());
 		Assert.assertEquals(event1, it.next());
 		Assert.assertEquals(event2, it.next());
 	}
-	
+
 	@Test(groups={"fast"})
 	public void testEventOrderingType() {
-	
+
 		BillingEvent event0 = createEvent(subscription(ID_ZERO), new DateTime("2012-01-01T00:02:04.000Z"), SubscriptionTransitionType.CREATE);
 		BillingEvent event1 = createEvent(subscription(ID_ZERO), new DateTime("2012-01-01T00:02:04.000Z"), SubscriptionTransitionType.CHANGE);
 		BillingEvent event2 = createEvent(subscription(ID_ZERO), new DateTime("2012-01-01T00:02:04.000Z"), SubscriptionTransitionType.CANCEL);
-		
+
 		SortedSet<BillingEvent> set = new TreeSet<BillingEvent>();
 		set.add(event2);
 		set.add(event1);
 		set.add(event0);
-		
+
 		Iterator<BillingEvent> it = set.iterator();
-		
+
 		Assert.assertEquals(event0, it.next());
 		Assert.assertEquals(event1, it.next());
 		Assert.assertEquals(event2, it.next());
 	}
-	
+
 	@Test(groups={"fast"})
 	public void testEventOrderingMix() {
-	
+
 		BillingEvent event0 = createEvent(subscription(ID_ZERO), new DateTime("2012-01-01T00:02:04.000Z"), SubscriptionTransitionType.CREATE);
 		BillingEvent event1 = createEvent(subscription(ID_ZERO), new DateTime("2012-01-02T00:02:04.000Z"), SubscriptionTransitionType.CHANGE);
 		BillingEvent event2 = createEvent(subscription(ID_ONE), new DateTime("2012-01-01T00:02:04.000Z"), SubscriptionTransitionType.CANCEL);
-		
+
 		SortedSet<BillingEvent> set = new TreeSet<BillingEvent>();
 		set.add(event2);
 		set.add(event1);
 		set.add(event0);
-		
+
 		Iterator<BillingEvent> it = set.iterator();
-		
+
 		Assert.assertEquals(event0, it.next());
 		Assert.assertEquals(event1, it.next());
 		Assert.assertEquals(event2, it.next());
 	}
 
-	
+
 	private BillingEvent createEvent(Subscription sub, DateTime effectiveDate, SubscriptionTransitionType type) {
 		InternationalPrice zeroPrice = new MockInternationalPrice(new DefaultPrice(BigDecimal.ZERO, Currency.USD));
 		int billCycleDay = 1;
 
 		Plan shotgun = new MockPlan();
 		PlanPhase shotgunMonthly = createMockMonthlyPlanPhase(null, BigDecimal.ZERO, PhaseType.TRIAL);
-		
+
 		return new DefaultBillingEvent(sub , effectiveDate,
 				shotgun, shotgunMonthly,
 				zeroPrice, null, BillingPeriod.NO_BILLING_PERIOD, billCycleDay,
@@ -142,13 +144,11 @@ public class TestDefaultBillingEvent {
 				new MockInternationalPrice(new DefaultPrice(fixedRate, Currency.USD)),
 				BillingPeriod.MONTHLY, phaseType);
 	}
-	
+
 	private Subscription subscription(final UUID id) {
-		return new BrainDeadSubscription() {
-			public UUID getId() {
-				return id;
-			}
-		};
+	    Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
+	    ((ZombieControl) subscription).addResult("getId", id);
+	    return subscription;
 	}
 
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
index 2d99f8d..21ed6fa 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
@@ -29,6 +29,7 @@ import org.joda.time.DateTimeZone;
 import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.BeforeSuite;
 import org.testng.annotations.Test;
 
 import com.google.inject.Guice;
@@ -66,100 +67,72 @@ public class TestDefaultEntitlementBillingApi {
 	private static final UUID zeroId = new UUID(0L,0L);
 	private static final UUID oneId = new UUID(1L,0L);
 	private static final UUID twoId = new UUID(2L,0L);
-	
+
 	private CatalogService catalogService;
 	private ArrayList<SubscriptionBundle> bundles;
 	private ArrayList<Subscription> subscriptions;
 	private ArrayList<SubscriptionTransition> transitions;
-	private BrainDeadMockEntitlementDao dao;
+	private EntitlementDao dao;
 
 	private Clock clock;
 	private SubscriptionData subscription;
 	private DateTime subscriptionStartDate;
 
-	@BeforeClass(groups={"setup"})
+	@BeforeSuite(alwaysRun=true)
 	public void setup() throws ServiceException {
 		TestApiBase.loadSystemPropertiesFromClasspath("/entitlement.properties");
         final Injector g = Guice.createInjector(Stage.PRODUCTION, new CatalogModule(), new ClockModule());
 
-        
+
         catalogService = g.getInstance(CatalogService.class);
         clock = g.getInstance(Clock.class);
-        
+
         ((DefaultCatalogService)catalogService).loadCatalog();
 	}
-	
+
 	@BeforeMethod(alwaysRun=true)
 	public void setupEveryTime() {
 		bundles = new ArrayList<SubscriptionBundle>();
 		final SubscriptionBundle bundle = new SubscriptionBundleData( zeroId,"TestKey", oneId,  clock.getUTCNow().minusDays(4));
 		bundles.add(bundle);
-		
-		
+
+
 		transitions = new ArrayList<SubscriptionTransition>();
 		subscriptions = new ArrayList<Subscription>();
-		
+
 		SubscriptionBuilder builder = new SubscriptionBuilder();
 		subscriptionStartDate = clock.getUTCNow().minusDays(3);
 		builder.setStartDate(subscriptionStartDate).setId(oneId);
 		subscription = new SubscriptionData(builder) {
-		    public List<SubscriptionTransition> getAllTransitions() {
+		    @Override
+            public List<SubscriptionTransition> getAllTransitions() {
 		    	return transitions;
 		    }
 		};
 
 		subscriptions.add(subscription);
-		
-		dao = new BrainDeadMockEntitlementDao() {
-			public List<SubscriptionBundle> getSubscriptionBundleForAccount(
-					UUID accountId) {
-				return bundles;
-				
-			}
-
-			public List<Subscription> getSubscriptions(UUID bundleId) {
-				return subscriptions;
-			}
-
-			public Subscription getSubscriptionFromId(UUID subscriptionId) {
-				return subscription;
-
-			}
-
-            @Override
-            public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId) {
-                throw new UnsupportedOperationException();
-            }
-
-            @Override
-			public SubscriptionBundle getSubscriptionBundleFromId(UUID bundleId) {
-				return bundle;
-			}
-		};
+
+        dao = BrainDeadProxyFactory.createBrainDeadProxyFor(EntitlementDao.class);
+        ((ZombieControl) dao).addResult("getSubscriptionBundleForAccount", bundles);
+        ((ZombieControl) dao).addResult("getSubscriptions", subscriptions);
+        ((ZombieControl) dao).addResult("getSubscriptionFromId", subscription);
+        ((ZombieControl) dao).addResult("getSubscriptionBundleFromId", bundle);
 
         assertTrue(true);
 	}
-	
+
     @Test(enabled=true, groups="fast")
 	public void testBillingEventsEmpty() {
-		EntitlementDao dao = new BrainDeadMockEntitlementDao() {
-			public List<SubscriptionBundle> getSubscriptionBundleForAccount(
-					UUID accountId) {
-				return new ArrayList<SubscriptionBundle>();
-			}
-
-            @Override
-            public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId) {
-                throw new UnsupportedOperationException();
-            }
-
-        };
+
+        dao = BrainDeadProxyFactory.createBrainDeadProxyFor(EntitlementDao.class);
+        ((ZombieControl) dao).addResult("getSubscriptionBundleForAccount", new ArrayList<SubscriptionBundle>());
+
 		AccountUserApi accountApi = new BrainDeadAccountUserApi() ;
 		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
 		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
 		Assert.assertEquals(events.size(), 0);
 	}
-	
+
     @Test(enabled=true, groups="fast")
 	public void testBillingEventsNoBillingPeriod() throws CatalogApiException {
 		DateTime now = clock.getUTCNow();
@@ -168,9 +141,9 @@ public class TestDefaultEntitlementBillingApi {
 		PlanPhase nextPhase = nextPlan.getAllPhases()[0]; // The trial has no billing period
 		String nextPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
 		SubscriptionTransition t = new SubscriptionTransitionData(
-				zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList);
+				zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, true);
 		transitions.add(t);
-		
+
 		AccountUserApi accountApi = new BrainDeadAccountUserApi(){
 
 			@Override
@@ -193,20 +166,20 @@ public class TestDefaultEntitlementBillingApi {
 		PlanPhase nextPhase = nextPlan.getAllPhases()[1];
 		String nextPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
 		SubscriptionTransition t = new SubscriptionTransitionData(
-				zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList);
+				zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, true);
 		transitions.add(t);
-		
-		Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class); 
+
+		Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class);
 		((ZombieControl)account).addResult("getBillCycleDay", 1).addResult("getTimeZone", DateTimeZone.UTC);
-		
-		AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);			
+
+		AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
 		((ZombieControl)accountApi).addResult("getAccountById", account);
-				
+
 		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
 		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
 		checkFirstEvent(events, nextPlan, subscription.getStartDate().getDayOfMonth(), oneId, now, nextPhase, ApiEventType.CREATE.toString());
 	}
-	
+
     @Test(enabled=true, groups="fast")
 	public void testBillingEventsMonthly() throws CatalogApiException {
 		DateTime now = clock.getUTCNow();
@@ -215,9 +188,9 @@ public class TestDefaultEntitlementBillingApi {
 		PlanPhase nextPhase = nextPlan.getAllPhases()[1];
 		String nextPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
 		SubscriptionTransition t = new SubscriptionTransitionData(
-				zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList);
+				zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, true);
 		transitions.add(t);
-		
+
 		AccountUserApi accountApi = new BrainDeadAccountUserApi(){
 
 			@Override
@@ -231,7 +204,7 @@ public class TestDefaultEntitlementBillingApi {
 		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
 		checkFirstEvent(events, nextPlan, 32, oneId, now, nextPhase, ApiEventType.CREATE.toString());
 	}
-	
+
     @Test(enabled=true, groups="fast")
 	public void testBillingEventsAddOn() throws CatalogApiException {
 		DateTime now = clock.getUTCNow();
@@ -240,15 +213,15 @@ public class TestDefaultEntitlementBillingApi {
 		PlanPhase nextPhase = nextPlan.getAllPhases()[0];
 		String nextPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
 		SubscriptionTransition t = new SubscriptionTransitionData(
-				zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList);
+				zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, true);
 		transitions.add(t);
-		
-		Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class); 
+
+		Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class);
 		((ZombieControl)account).addResult("getBillCycleDay", 1).addResult("getTimeZone", DateTimeZone.UTC);
-		
-		AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);			
+
+		AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
 		((ZombieControl)accountApi).addResult("getAccountById", account);
-				
+
 		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
 		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
 		checkFirstEvent(events, nextPlan, bundles.get(0).getStartDate().getDayOfMonth(), oneId, now, nextPhase, ApiEventType.CREATE.toString());
@@ -265,7 +238,7 @@ public class TestDefaultEntitlementBillingApi {
 		if(nextPhase.getRecurringPrice() != null) {
 			Assert.assertEquals(nextPhase.getRecurringPrice().getPrice(Currency.USD), event.getRecurringPrice().getPrice(Currency.USD));
 		}
-		
+
 		Assert.assertEquals(BCD, event.getBillCycleDay());
 		Assert.assertEquals(id, event.getSubscription().getId());
 		Assert.assertEquals(time, event.getEffectiveDate());
@@ -277,6 +250,6 @@ public class TestDefaultEntitlementBillingApi {
 		Assert.assertEquals(nextPhase.getFixedPrice(), event.getFixedPrice());
 		Assert.assertEquals(nextPhase.getRecurringPrice(), event.getRecurringPrice());
 	}
-	
-	
+
+
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
index 7ef459d..acd64ea 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
@@ -29,6 +29,7 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.testng.Assert;
 
+import com.google.common.collect.Lists;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Duration;
 import com.ning.billing.catalog.api.PhaseType;
@@ -78,6 +79,48 @@ public abstract class TestMigration extends TestApiBase {
     }
 
 
+    public void testPlanWithAddOn() {
+        try {
+            DateTime beforeMigration = clock.getUTCNow();
+            final DateTime initalAddonStart = clock.getUTCNow().minusMonths(1).plusDays(7);
+            EntitlementAccountMigration toBeMigrated = createAccountWithRegularBasePlanAndAddons(initalAddonStart);
+            DateTime afterMigration = clock.getUTCNow();
+
+            testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+            migrationApi.migrate(toBeMigrated);
+            assertTrue(testListener.isCompleted(5000));
+
+            List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
+            assertEquals(bundles.size(), 1);
+            SubscriptionBundle bundle = bundles.get(0);
+
+            List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(bundle.getId());
+            assertEquals(subscriptions.size(), 2);
+
+            Subscription baseSubscription = (subscriptions.get(0).getCurrentPlan().getProduct().getCategory() == ProductCategory.BASE) ?
+                    subscriptions.get(0) : subscriptions.get(1);
+            assertDateWithin(baseSubscription.getStartDate(), beforeMigration, afterMigration);
+            assertEquals(baseSubscription.getEndDate(), null);
+            assertEquals(baseSubscription.getCurrentPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME);
+            assertEquals(baseSubscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+            assertEquals(baseSubscription.getState(), SubscriptionState.ACTIVE);
+            assertEquals(baseSubscription.getCurrentPlan().getName(), "assault-rifle-annual");
+
+            Subscription aoSubscription = (subscriptions.get(0).getCurrentPlan().getProduct().getCategory() == ProductCategory.ADD_ON) ?
+                    subscriptions.get(0) : subscriptions.get(1);
+            assertEquals(aoSubscription.getStartDate(), initalAddonStart);
+            assertEquals(aoSubscription.getEndDate(), null);
+            assertEquals(aoSubscription.getCurrentPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME);
+            assertEquals(aoSubscription.getCurrentPhase().getPhaseType(), PhaseType.DISCOUNT);
+            assertEquals(aoSubscription.getState(), SubscriptionState.ACTIVE);
+            assertEquals(aoSubscription.getCurrentPlan().getName(), "telescopic-scope-monthly");
+
+        } catch (EntitlementMigrationApiException e) {
+            Assert.fail("", e);
+        }
+    }
+
+
     public void testSingleBasePlanFutureCancelled() {
 
         try {
@@ -212,7 +255,7 @@ public abstract class TestMigration extends TestApiBase {
     }
 
 
-    private EntitlementAccountMigration createAccountWithSingleBasePlan(final List<EntitlementSubscriptionMigrationCase> cases) {
+    private EntitlementAccountMigration createAccountWithSingleBasePlan(final List<List<EntitlementSubscriptionMigrationCase>> cases) {
 
         return new EntitlementAccountMigration() {
 
@@ -225,18 +268,24 @@ public abstract class TestMigration extends TestApiBase {
 
                     @Override
                     public EntitlementSubscriptionMigration[] getSubscriptions() {
-                        EntitlementSubscriptionMigration subscription = new EntitlementSubscriptionMigration() {
-                            @Override
-                            public EntitlementSubscriptionMigrationCase[] getSubscriptionCases() {
-                                return cases.toArray(new EntitlementSubscriptionMigrationCase[cases.size()]);
-                            }
-                            @Override
-                            public ProductCategory getCategory() {
-                                return ProductCategory.BASE;
-                            }
-                        };
-                        EntitlementSubscriptionMigration[] result = new EntitlementSubscriptionMigration[1];
-                        result[0] = subscription;
+
+                        EntitlementSubscriptionMigration[] result = new EntitlementSubscriptionMigration[cases.size()];
+
+                        for (int i = 0; i < cases.size(); i++) {
+
+                            final List<EntitlementSubscriptionMigrationCase> curCases = cases.get(i);
+                            EntitlementSubscriptionMigration subscription = new EntitlementSubscriptionMigration() {
+                                @Override
+                                public EntitlementSubscriptionMigrationCase[] getSubscriptionCases() {
+                                    return curCases.toArray(new EntitlementSubscriptionMigrationCase[curCases.size()]);
+                                }
+                                @Override
+                                public ProductCategory getCategory() {
+                                    return ProductCategory.BASE;
+                                }
+                            };
+                            result[i] = subscription;
+                        }
                         return result;
                     }
                     @Override
@@ -255,6 +304,61 @@ public abstract class TestMigration extends TestApiBase {
         };
     }
 
+    private EntitlementAccountMigration createAccountWithRegularBasePlanAndAddons(final DateTime initalAddonStart) {
+
+        List<EntitlementSubscriptionMigrationCase> cases = new LinkedList<EntitlementSubscriptionMigrationCase>();
+        cases.add(new EntitlementSubscriptionMigrationCase() {
+            @Override
+            public PlanPhaseSpecifier getPlanPhaseSpecifer() {
+                return new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+            }
+            @Override
+            public DateTime getEffectiveDate() {
+                return clock.getUTCNow().minusMonths(3);
+            }
+            @Override
+            public DateTime getCancelledDate() {
+                return null;
+            }
+        });
+
+        List<EntitlementSubscriptionMigrationCase> firstAddOnCases = new LinkedList<EntitlementSubscriptionMigrationCase>();
+
+        firstAddOnCases.add(new EntitlementSubscriptionMigrationCase() {
+            @Override
+            public PlanPhaseSpecifier getPlanPhaseSpecifer() {
+                return new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.DISCOUNT);
+            }
+            @Override
+            public DateTime getEffectiveDate() {
+                return initalAddonStart;
+            }
+            @Override
+            public DateTime getCancelledDate() {
+                return initalAddonStart.plusMonths(1);
+            }
+        });
+        firstAddOnCases.add(new EntitlementSubscriptionMigrationCase() {
+            @Override
+            public PlanPhaseSpecifier getPlanPhaseSpecifer() {
+                return new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+            }
+            @Override
+            public DateTime getEffectiveDate() {
+                return initalAddonStart.plusMonths(1);
+            }
+            @Override
+            public DateTime getCancelledDate() {
+                return null;
+            }
+        });
+
+        List<List<EntitlementSubscriptionMigrationCase>> input = new ArrayList<List<EntitlementSubscriptionMigrationCase>>();
+        input.add(cases);
+        input.add(firstAddOnCases);
+        return createAccountWithSingleBasePlan(input);
+    }
+
     private EntitlementAccountMigration createAccountWithRegularBasePlan() {
         List<EntitlementSubscriptionMigrationCase> cases = new LinkedList<EntitlementSubscriptionMigrationCase>();
         cases.add(new EntitlementSubscriptionMigrationCase() {
@@ -271,7 +375,9 @@ public abstract class TestMigration extends TestApiBase {
                 return null;
             }
         });
-        return createAccountWithSingleBasePlan(cases);
+        List<List<EntitlementSubscriptionMigrationCase>> input = new ArrayList<List<EntitlementSubscriptionMigrationCase>>();
+        input.add(cases);
+        return createAccountWithSingleBasePlan(input);
     }
 
     private EntitlementAccountMigration createAccountWithRegularBasePlanFutreCancelled() {
@@ -291,7 +397,9 @@ public abstract class TestMigration extends TestApiBase {
                 return effectiveDate.plusYears(1);
             }
         });
-        return createAccountWithSingleBasePlan(cases);
+        List<List<EntitlementSubscriptionMigrationCase>> input = new ArrayList<List<EntitlementSubscriptionMigrationCase>>();
+        input.add(cases);
+        return createAccountWithSingleBasePlan(input);
     }
 
 
@@ -325,7 +433,9 @@ public abstract class TestMigration extends TestApiBase {
                 return null;
             }
         });
-        return createAccountWithSingleBasePlan(cases);
+        List<List<EntitlementSubscriptionMigrationCase>> input = new ArrayList<List<EntitlementSubscriptionMigrationCase>>();
+        input.add(cases);
+        return createAccountWithSingleBasePlan(input);
     }
 
     private EntitlementAccountMigration createAccountFuturePendingChange() {
@@ -359,7 +469,9 @@ public abstract class TestMigration extends TestApiBase {
                 return null;
             }
         });
-        return createAccountWithSingleBasePlan(cases);
+        List<List<EntitlementSubscriptionMigrationCase>> input = new ArrayList<List<EntitlementSubscriptionMigrationCase>>();
+        input.add(cases);
+        return createAccountWithSingleBasePlan(input);
     }
 
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationSql.java
index b3eb168..84744d8 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationSql.java
@@ -31,25 +31,31 @@ public class TestMigrationSql extends TestMigration {
     }
 
     @Override
-    @Test(enabled=true, groups="sql")
+    @Test(enabled=true, groups="slow")
     public void testSingleBasePlan() {
         super.testSingleBasePlan();
     }
 
     @Override
-    @Test(enabled=true, groups="sql")
+    @Test(enabled=true, groups="slow")
+    public void testPlanWithAddOn() {
+        super.testPlanWithAddOn();
+    }
+
+    @Override
+    @Test(enabled=true, groups="slow")
     public void testSingleBasePlanFutureCancelled() {
         super.testSingleBasePlanFutureCancelled();
     }
 
     @Override
-    @Test(enabled=true, groups="sql")
+    @Test(enabled=true, groups="slow")
     public void testSingleBasePlanWithPendingPhase() {
         super.testSingleBasePlanWithPendingPhase();
     }
 
     @Override
-    @Test(enabled=true, groups="sql")
+    @Test(enabled=true, groups="slow")
     public void testSingleBasePlanWithPendingChange() {
         super.testSingleBasePlanWithPendingChange();
     }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
index 912f186..094faba 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
@@ -26,6 +26,7 @@ import java.net.URL;
 import java.util.List;
 import java.util.UUID;
 
+import org.apache.commons.io.IOUtils;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.slf4j.Logger;
@@ -33,8 +34,10 @@ import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.AfterMethod;
+import org.testng.annotations.AfterSuite;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.BeforeSuite;
 
 import com.google.inject.Injector;
 import com.ning.billing.account.api.AccountData;
@@ -49,6 +52,7 @@ import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.catalog.api.TimeUnit;
 import com.ning.billing.config.EntitlementConfig;
+import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
 import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
 import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
@@ -60,11 +64,11 @@ import com.ning.billing.entitlement.api.user.SubscriptionTransition;
 import com.ning.billing.entitlement.engine.core.Engine;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.engine.dao.MockEntitlementDao;
+import com.ning.billing.entitlement.engine.dao.MockEntitlementDaoMemory;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.events.phase.PhaseEvent;
 import com.ning.billing.entitlement.events.user.ApiEvent;
 import com.ning.billing.entitlement.events.user.ApiEventType;
-import com.ning.billing.lifecycle.KillbillService.ServiceException;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.bus.DefaultBusService;
@@ -94,8 +98,9 @@ public abstract class TestApiBase {
     protected ApiTestListener testListener;
     protected SubscriptionBundle bundle;
 
-    public static void loadSystemPropertiesFromClasspath(final String resource)
-    {
+    private MysqlTestingHelper helper;
+
+    public static void loadSystemPropertiesFromClasspath(final String resource) {
         final URL url = TestApiBase.class.getResource(resource);
         assertNotNull(url);
 
@@ -106,7 +111,9 @@ public abstract class TestApiBase {
         }
     }
 
-    @AfterClass(groups={"setup"})
+    protected abstract Injector getInjector();
+
+    @AfterClass(alwaysRun=true)
     public void tearDown() {
         try {
             busService.getBus().register(testListener);
@@ -117,7 +124,7 @@ public abstract class TestApiBase {
 
     }
 
-    @BeforeClass(groups={"setup"})
+    @BeforeClass(alwaysRun=true)
     public void setup() {
 
         loadSystemPropertiesFromClasspath("/entitlement.properties");
@@ -129,21 +136,35 @@ public abstract class TestApiBase {
         config = g.getInstance(EntitlementConfig.class);
         dao = g.getInstance(EntitlementDao.class);
         clock = (ClockMock) g.getInstance(Clock.class);
+        helper = (isSqlTest(dao)) ? g.getInstance(MysqlTestingHelper.class) : null;
+
         try {
             ((DefaultCatalogService) catalogService).loadCatalog();
             ((DefaultBusService) busService).startBus();
             ((Engine) entitlementService).initialize();
             init();
-        } catch (EntitlementUserApiException e) {
-            Assert.fail(e.getMessage());
-        } catch (ServiceException e) {
-            Assert.fail(e.getMessage());
+        } catch (Exception e) {
         }
     }
 
-    protected abstract Injector getInjector();
+    private static boolean isSqlTest(EntitlementDao theDao) {
+        return (! (theDao instanceof MockEntitlementDaoMemory));
+    }
+
+    private void setupMySQL() throws IOException {
+        if (helper != null) {
+            final String entitlementDdl = IOUtils.toString(TestApiBase.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
+            final String utilDdl = IOUtils.toString(TestApiBase.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
+            helper.startMysql();
+            helper.initDb(entitlementDdl);
+            helper.initDb(utilDdl);
+        }
+    }
+
+    private void init() throws Exception {
+
+        setupMySQL();
 
-    private void init() throws EntitlementUserApiException {
         accountData = getAccountData();
         assertNotNull(accountData);
 
@@ -155,10 +176,9 @@ public abstract class TestApiBase {
         entitlementApi = entitlementService.getUserApi();
         billingApi = entitlementService.getBillingApi();
         migrationApi = entitlementService.getMigrationApi();
-
     }
 
-    @BeforeMethod(groups={"setup"})
+    @BeforeMethod(alwaysRun=true)
     public void setupTest() {
 
         log.warn("\n");
@@ -180,17 +200,20 @@ public abstract class TestApiBase {
         ((Engine)entitlementService).start();
     }
 
-    @AfterMethod(groups={"setup"})
+    @AfterMethod(alwaysRun=true)
     public void cleanupTest() {
-
-
         ((Engine)entitlementService).stop();
         log.warn("DONE WITH TEST\n");
     }
 
     protected SubscriptionData createSubscription(final String productName, final BillingPeriod term, final String planSet) throws EntitlementUserApiException {
+        return createSubscriptionWithBundle(bundle.getId(), productName, term, planSet);
+    }
+
+
+    protected SubscriptionData createSubscriptionWithBundle(final UUID bundleId, final String productName, final BillingPeriod term, final String planSet) throws EntitlementUserApiException {
         testListener.pushExpectedEvent(NextEvent.CREATE);
-        SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
+        SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundleId,
                 new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSet, null),
                 clock.getUTCNow());
         assertNotNull(subscription);
@@ -342,12 +365,12 @@ public abstract class TestApiBase {
 
             @Override
             public String getAddress1() {
-                return null;  
+                return null;
             }
 
             @Override
             public String getAddress2() {
-                return null;  
+                return null;
             }
 
             @Override
@@ -357,22 +380,22 @@ public abstract class TestApiBase {
 
             @Override
             public String getCity() {
-                return null;  
+                return null;
             }
 
             @Override
             public String getStateOrProvince() {
-                return null;  
+                return null;
             }
 
             @Override
             public String getPostalCode() {
-                return null;  
+                return null;
             }
 
             @Override
             public String getCountry() {
-                return null;  
+                return null;
             }
         };
         return accountData;
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
new file mode 100644
index 0000000..7db938e
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
@@ -0,0 +1,385 @@
+/*
+ * 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.user;
+
+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.CatalogApiException;
+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.PlanAlignmentCreate;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.catalog.api.PlanSpecifier;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.TestApiBase;
+import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition.SubscriptionTransitionType;
+import com.ning.billing.entitlement.glue.MockEngineModuleSql;
+import com.ning.billing.util.clock.DefaultClock;
+
+public class TestUserApiAddOn extends TestApiBase {
+
+    @Override
+    public Injector getInjector() {
+        return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
+    }
+
+
+    @Test(enabled=true, groups={"slow"})
+    public void testCreateCancelAddon() {
+
+        try {
+            String baseProduct = "Shotgun";
+            BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+            String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+            SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+            String aoProduct = "Telescopic-Scope";
+            BillingPeriod aoTerm = BillingPeriod.MONTHLY;
+            String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+            SubscriptionData aoSubscription = createSubscription(aoProduct, aoTerm, aoPriceList);
+            assertEquals(aoSubscription.getState(), SubscriptionState.ACTIVE);
+
+            DateTime now = clock.getUTCNow();
+            aoSubscription.cancel(now, false);
+
+            testListener.reset();
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
+            assertTrue(testListener.isCompleted(5000));
+
+            assertEquals(aoSubscription.getState(), SubscriptionState.CANCELLED);
+
+        } catch (Exception e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+
+    @Test(enabled=true, groups={"slow"})
+    public void testCancelBPWthAddon() {
+        try {
+
+            String baseProduct = "Shotgun";
+            BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+            String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+            // CREATE BP
+            SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+            String aoProduct = "Telescopic-Scope";
+            BillingPeriod aoTerm = BillingPeriod.MONTHLY;
+            String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+            SubscriptionData aoSubscription = createSubscription(aoProduct, aoTerm, aoPriceList);
+
+            testListener.reset();
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+
+            // MOVE CLOCK AFTER TRIAL + AO DISCOUNT
+            Duration twoMonths = getDurationMonth(2);
+            clock.setDeltaFromReality(twoMonths, DAY_IN_MS);
+            assertTrue(testListener.isCompleted(5000));
+
+            // SET CTD TO CANCEL IN FUTURE
+            DateTime now = clock.getUTCNow();
+            Duration ctd = getDurationMonth(1);
+            DateTime newChargedThroughDate = DefaultClock.addDuration(now, ctd);
+            billingApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate);
+            baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+
+            // FUTURE CANCELLATION
+            baseSubscription.cancel(now, false);
+
+            // REFETCH AO SUBSCRIPTION AND CHECK THIS IS ACTIVE
+            aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+            assertEquals(aoSubscription.getState(), SubscriptionState.ACTIVE);
+            assertTrue(aoSubscription.isSubscriptionFutureCancelled());
+
+
+            // MOVE AFTER CANCELLATION
+            testListener.reset();
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
+            clock.addDeltaFromReality(ctd);
+            now = clock.getUTCNow();
+            assertTrue(testListener.isCompleted(5000));
+
+            // REFETCH AO SUBSCRIPTION AND CHECK THIS IS CANCELLED
+            aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+            assertEquals(aoSubscription.getState(), SubscriptionState.CANCELLED);
+
+        } catch (Exception e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+
+
+    @Test(enabled=true, groups={"slow"})
+    public void testChangeBPWthAddonNonIncluded() {
+        try {
+
+            String baseProduct = "Shotgun";
+            BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+            String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+            // CREATE BP
+            SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+            String aoProduct = "Telescopic-Scope";
+            BillingPeriod aoTerm = BillingPeriod.MONTHLY;
+            String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+            SubscriptionData aoSubscription = createSubscription(aoProduct, aoTerm, aoPriceList);
+
+            testListener.reset();
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+
+            // MOVE CLOCK AFTER TRIAL + AO DISCOUNT
+            Duration twoMonths = getDurationMonth(2);
+            clock.setDeltaFromReality(twoMonths, DAY_IN_MS);
+            assertTrue(testListener.isCompleted(5000));
+
+            // SET CTD TO CHANGE IN FUTURE
+            DateTime now = clock.getUTCNow();
+            Duration ctd = getDurationMonth(1);
+            DateTime newChargedThroughDate = DefaultClock.addDuration(now, ctd);
+            billingApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate);
+            baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+
+            // CHANGE IMMEDIATELY WITH TO BP WITH NON INCLUDED ADDON
+            String newBaseProduct = "Assault-Rifle";
+            BillingPeriod newBaseTerm = BillingPeriod.MONTHLY;
+            String newBasePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+            testListener.reset();
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
+            baseSubscription.changePlan(newBaseProduct, newBaseTerm, newBasePriceList, now);
+            assertTrue(testListener.isCompleted(5000));
+
+            // REFETCH AO SUBSCRIPTION AND CHECK THIS CANCELLED
+            aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+            assertEquals(aoSubscription.getState(), SubscriptionState.CANCELLED);
+
+        } catch (Exception e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+
+    @Test(enabled=true, groups={"slow"})
+    public void testChangeBPWthAddonNonAvailable() {
+        try {
+
+            String baseProduct = "Shotgun";
+            BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+            String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+            // CREATE BP
+            SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+            String aoProduct = "Telescopic-Scope";
+            BillingPeriod aoTerm = BillingPeriod.MONTHLY;
+            String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+            SubscriptionData aoSubscription = createSubscription(aoProduct, aoTerm, aoPriceList);
+
+            testListener.reset();
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+
+            // MOVE CLOCK AFTER TRIAL + AO DISCOUNT
+            Duration twoMonths = getDurationMonth(2);
+            clock.setDeltaFromReality(twoMonths, DAY_IN_MS);
+            assertTrue(testListener.isCompleted(5000));
+
+            // SET CTD TO CANCEL IN FUTURE
+            DateTime now = clock.getUTCNow();
+            Duration ctd = getDurationMonth(1);
+            DateTime newChargedThroughDate = DefaultClock.addDuration(now, ctd);
+            billingApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate);
+            baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+
+            // CHANGE IMMEDIATELY WITH TO BP WITH NON AVAILABLE ADDON
+            String newBaseProduct = "Pistol";
+            BillingPeriod newBaseTerm = BillingPeriod.MONTHLY;
+            String newBasePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+            baseSubscription.changePlan(newBaseProduct, newBaseTerm, newBasePriceList, now);
+
+
+            // REFETCH AO SUBSCRIPTION AND CHECK THIS IS ACTIVE
+            aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+            assertEquals(aoSubscription.getState(), SubscriptionState.ACTIVE);
+            assertTrue(aoSubscription.isSubscriptionFutureCancelled());
+
+            // MOVE AFTER CHANGE
+            testListener.reset();
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
+            clock.addDeltaFromReality(ctd);
+            assertTrue(testListener.isCompleted(5000));
+
+
+            // REFETCH AO SUBSCRIPTION AND CHECK THIS CANCELLED
+            aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+            assertEquals(aoSubscription.getState(), SubscriptionState.CANCELLED);
+
+        } catch (Exception e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+
+
+    @Test(enabled=true, groups={"slow"})
+    public void testAddonCreateWithBundleAlign() {
+        try {
+            String aoProduct = "Telescopic-Scope";
+            BillingPeriod aoTerm = BillingPeriod.MONTHLY;
+            String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+            // This is just to double check our test catalog gives us what we want before we start the test
+            PlanSpecifier planSpecifier = new PlanSpecifier(aoProduct,
+                    ProductCategory.ADD_ON,
+                    aoTerm,
+                    aoPriceList);
+            PlanAlignmentCreate alignement = catalog.planCreateAlignment(planSpecifier, clock.getUTCNow());
+            assertEquals(alignement, PlanAlignmentCreate.START_OF_BUNDLE);
+
+            testAddonCreateInternal(aoProduct, aoTerm, aoPriceList, alignement);
+
+        } catch (CatalogApiException e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+
+    @Test(enabled=true, groups={"slow"})
+    public void testAddonCreateWithSubscriptionAlign() {
+
+        try {
+            String aoProduct = "Laser-Scope";
+            BillingPeriod aoTerm = BillingPeriod.MONTHLY;
+            String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+            // This is just to double check our test catalog gives us what we want before we start the test
+            PlanSpecifier planSpecifier = new PlanSpecifier(aoProduct,
+                    ProductCategory.ADD_ON,
+                    aoTerm,
+                    aoPriceList);
+            PlanAlignmentCreate alignement = catalog.planCreateAlignment(planSpecifier, clock.getUTCNow());
+            assertEquals(alignement, PlanAlignmentCreate.START_OF_SUBSCRIPTION);
+
+            testAddonCreateInternal(aoProduct, aoTerm, aoPriceList, alignement);
+
+            } catch (CatalogApiException e) {
+                Assert.fail(e.getMessage());
+            }
+    }
+
+
+    private void testAddonCreateInternal(String aoProduct, BillingPeriod aoTerm, String aoPriceList, PlanAlignmentCreate expAlignement) {
+        try {
+
+            String baseProduct = "Shotgun";
+            BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+            String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+            // CREATE BP
+            SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+            // MOVE CLOCK 14 DAYS LATER
+            Duration someTimeLater = getDurationDay(13);
+            clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+
+            // CREATE ADDON
+            DateTime beforeAOCreation = clock.getUTCNow();
+            SubscriptionData aoSubscription = createSubscription(aoProduct, aoTerm, aoPriceList);
+            DateTime afterAOCreation = clock.getUTCNow();
+
+            // CHECK EVERYTHING
+            Plan aoCurrentPlan = aoSubscription.getCurrentPlan();
+            assertNotNull(aoCurrentPlan);
+            assertEquals(aoCurrentPlan.getProduct().getName(),aoProduct);
+            assertEquals(aoCurrentPlan.getProduct().getCategory(), ProductCategory.ADD_ON);
+            assertEquals(aoCurrentPlan.getBillingPeriod(), aoTerm);
+
+            PlanPhase aoCurrentPhase = aoSubscription.getCurrentPhase();
+            assertNotNull(aoCurrentPhase);
+            assertEquals(aoCurrentPhase.getPhaseType(), PhaseType.DISCOUNT);
+
+           assertDateWithin(aoSubscription.getStartDate(), beforeAOCreation, afterAOCreation);
+           assertEquals(aoSubscription.getBundleStartDate(), baseSubscription.getBundleStartDate());
+
+           // CHECK next AO PHASE EVENT IS INDEED A MONTH AFTER BP STARTED => BUNDLE ALIGNMENT
+           SubscriptionTransition aoPendingTranstion = aoSubscription.getPendingTransition();
+
+           if (expAlignement == PlanAlignmentCreate.START_OF_BUNDLE) {
+               assertEquals(aoPendingTranstion.getEffectiveTransitionTime(), baseSubscription.getStartDate().plusMonths(1));
+           } else {
+               assertEquals(aoPendingTranstion.getEffectiveTransitionTime(), aoSubscription.getStartDate().plusMonths(1));
+           }
+
+           // ADD TWO PHASE EVENTS (BP + AO)
+           testListener.reset();
+           testListener.pushExpectedEvent(NextEvent.PHASE);
+           testListener.pushExpectedEvent(NextEvent.PHASE);
+
+           // MOVE THROUGH TIME TO GO INTO EVERGREEN
+           someTimeLater = aoCurrentPhase.getDuration();
+           clock.addDeltaFromReality(someTimeLater);
+           assertTrue(testListener.isCompleted(5000));
+
+
+           // CHECK EVERYTHING AGAIN
+           aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+
+           aoCurrentPlan = aoSubscription.getCurrentPlan();
+           assertNotNull(aoCurrentPlan);
+           assertEquals(aoCurrentPlan.getProduct().getName(),aoProduct);
+           assertEquals(aoCurrentPlan.getProduct().getCategory(), ProductCategory.ADD_ON);
+           assertEquals(aoCurrentPlan.getBillingPeriod(), aoTerm);
+
+           aoCurrentPhase = aoSubscription.getCurrentPhase();
+           assertNotNull(aoCurrentPhase);
+           assertEquals(aoCurrentPhase.getPhaseType(), PhaseType.EVERGREEN);
+
+
+           aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+           aoPendingTranstion = aoSubscription.getPendingTransition();
+           assertNull(aoPendingTranstion);
+
+        } catch (EntitlementUserApiException e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelSql.java
index 840f357..469d374 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelSql.java
@@ -49,25 +49,25 @@ public class TestUserApiCancelSql extends TestUserApiCancel {
     }
 
     @Override
-    @Test(enabled=true, groups={"sql"})
+    @Test(enabled=true, groups={"slow"})
     public void testCancelSubscriptionIMM() {
         super.testCancelSubscriptionIMM();
     }
 
     @Override
-    @Test(enabled=true, groups={"sql"})
+    @Test(enabled=true, groups={"slow"})
     public void testCancelSubscriptionEOTWithChargeThroughDate() throws EntitlementBillingApiException {
         super.testCancelSubscriptionEOTWithChargeThroughDate();
     }
 
     @Override
-    @Test(enabled=true, groups={"sql"})
+    @Test(enabled=true, groups={"slow"})
     public void testCancelSubscriptionEOTWithNoChargeThroughDate() {
         super.testCancelSubscriptionEOTWithNoChargeThroughDate();
     }
 
     @Override
-    @Test(enabled=true, groups={"sql"})
+    @Test(enabled=true, groups={"slow"})
     public void testUncancel() throws EntitlementBillingApiException {
         super.testUncancel();
     }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
index 78616be..4505ef0 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
@@ -428,4 +428,54 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
         }
     }
 
+
+    protected void testCorrectPhaseAlignmentOnChange() {
+        try {
+
+            SubscriptionData subscription = createSubscription("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+            PlanPhase trialPhase = subscription.getCurrentPhase();
+            assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
+
+            // MOVE 2 DAYS AHEAD
+            clock.setDeltaFromReality(getDurationDay(1), DAY_IN_MS);
+
+            // CHANGE IMMEDIATE TO A 3 PHASES PLAN
+            testListener.reset();
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
+            subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow());
+            assertTrue(testListener.isCompleted(3000));
+            testListener.reset();
+
+            // CHECK EVERYTHING LOOKS CORRECT
+            Plan currentPlan = subscription.getCurrentPlan();
+            assertNotNull(currentPlan);
+            assertEquals(currentPlan.getProduct().getName(), "Assault-Rifle");
+            assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+            assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.ANNUAL);
+
+            trialPhase = subscription.getCurrentPhase();
+            assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
+
+            // MOVE AFTER TRIAL PERIOD -> DISCOUNT
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            clock.addDeltaFromReality(trialPhase.getDuration());
+            assertTrue(testListener.isCompleted(3000));
+
+            trialPhase = subscription.getCurrentPhase();
+            assertEquals(trialPhase.getPhaseType(), PhaseType.DISCOUNT);
+
+            subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
+
+            DateTime expectedNextPhaseDate =  subscription.getStartDate().plusDays(30).plusMonths(6);
+            SubscriptionTransition nextPhase = subscription.getPendingTransition();
+            DateTime nextPhaseEffectiveDate = nextPhase.getEffectiveTransitionTime();
+
+            assertEquals(nextPhaseEffectiveDate, expectedNextPhaseDate);
+
+
+        } catch (EntitlementUserApiException e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
index 253da07..03b9d91 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
@@ -67,4 +67,10 @@ public class TestUserApiChangePlanMemory extends TestUserApiChangePlan {
     public void testChangePlanChangePlanAlignEOTWithChargeThroughDate() throws EntitlementBillingApiException {
         super.testChangePlanChangePlanAlignEOTWithChargeThroughDate();
     }
+
+    @Override
+    @Test(enabled=true, groups={"fast"})
+    public void testCorrectPhaseAlignmentOnChange() {
+        super.testCorrectPhaseAlignmentOnChange();
+    }
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
index 92aa652..735099c 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
@@ -54,38 +54,44 @@ public class TestUserApiChangePlanSql extends TestUserApiChangePlan {
     }
 
     @Override
-    @Test(enabled=true, groups={"sql"})
+    @Test(enabled=true, groups={"slow"})
+    public void testCorrectPhaseAlignmentOnChange() {
+        super.testCorrectPhaseAlignmentOnChange();
+    }
+
+    @Override
+    @Test(enabled=true, groups={"slow"})
     public void testChangePlanBundleAlignEOTWithNoChargeThroughDate() {
         super.testChangePlanBundleAlignEOTWithNoChargeThroughDate();
     }
 
     @Override
-    @Test(enabled=true, groups={"sql"})
+    @Test(enabled=true, groups={"slow"})
     public void testChangePlanBundleAlignEOTWithChargeThroughDate() throws EntitlementBillingApiException {
         super.testChangePlanBundleAlignEOTWithChargeThroughDate();
     }
 
     @Override
-    @Test(enabled=true, groups={"sql"})
+    @Test(enabled=true, groups={"slow"})
     public void testChangePlanBundleAlignIMM() {
         super.testChangePlanBundleAlignIMM();
     }
 
     @Override
-    @Test(enabled=true, groups={"sql"})
+    @Test(enabled=true, groups={"slow"})
     public void testMultipleChangeLastIMM() throws EntitlementBillingApiException {
         super.testMultipleChangeLastIMM();
     }
 
     @Override
-    @Test(enabled=true, groups={"sql"})
+    @Test(enabled=true, groups={"slow"})
     public void testMultipleChangeLastEOT() throws EntitlementBillingApiException {
         super.testMultipleChangeLastEOT();
     }
 
     // rescue not implemented yet
     @Override
-    @Test(enabled=false, groups={"sql"})
+    @Test(enabled=false, groups={"slow"})
     public void testChangePlanChangePlanAlignEOTWithChargeThroughDate() throws EntitlementBillingApiException {
         super.testChangePlanChangePlanAlignEOTWithChargeThroughDate();
     }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
index d5546d7..ee8490b 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
@@ -40,7 +40,8 @@ import com.ning.billing.entitlement.events.phase.PhaseEvent;
 import com.ning.billing.util.clock.DefaultClock;
 
 public abstract class TestUserApiCreate extends TestApiBase {
-	Logger log = LoggerFactory.getLogger(TestUserApiCreate.class);
+
+    private static Logger log = LoggerFactory.getLogger(TestUserApiCreate.class);
 
     public void testCreateWithRequestedDate() {
         log.info("Starting testCreateWithRequestedDate");
@@ -75,6 +76,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
         }
     }
 
+
     protected void testCreateWithInitialPhase() {
         log.info("Starting testCreateWithInitialPhase");
         try {
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateMemory.java
index f4474f9..3adf86e 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateMemory.java
@@ -36,21 +36,25 @@ public class TestUserApiCreateMemory extends TestUserApiCreate {
         super.testCreateWithRequestedDate();
     }
 
+    @Override
     @Test(enabled=true, groups={"fast"})
     public void testCreateWithInitialPhase() {
         super.testSimpleSubscriptionThroughPhases();
     }
 
+    @Override
     @Test(enabled=true, groups={"fast"})
     public void testSimpleCreateSubscription() {
         super.testSimpleCreateSubscription();
     }
 
+    @Override
     @Test(enabled=true, groups={"fast"})
     protected void testSimpleSubscriptionThroughPhases() {
         super.testSimpleSubscriptionThroughPhases();
     }
 
+    @Override
     @Test(enabled=false, groups={"fast"})
     protected void testSubscriptionWithAddOn() {
         super.testSubscriptionWithAddOn();
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateSql.java
index 8170b6d..6820fec 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateSql.java
@@ -30,31 +30,31 @@ public class TestUserApiCreateSql extends TestUserApiCreate {
     }
 
     @Override
-    @Test(enabled=true, groups={"sql"})
+    @Test(enabled=true, groups={"slow"})
     public void testCreateWithRequestedDate() {
         super.testCreateWithRequestedDate();
     }
 
     @Override
-    @Test(enabled=true, groups={"sql"})
+    @Test(enabled=true, groups={"slow"})
     public void testCreateWithInitialPhase() {
         super.testCreateWithInitialPhase();
     }
 
     @Override
-    @Test(enabled=true, groups={"sql"})
+    @Test(enabled=true, groups={"slow"})
     public void testSimpleCreateSubscription() {
         super.testSimpleCreateSubscription();
     }
 
     @Override
-    @Test(enabled=true, groups={"sql"})
+    @Test(enabled=true, groups={"slow"})
     protected void testSimpleSubscriptionThroughPhases() {
         super.testSimpleSubscriptionThroughPhases();
     }
 
     @Override
-    @Test(enabled=false, groups={"sql"})
+    @Test(enabled=false, groups={"slow"})
     protected void testSubscriptionWithAddOn() {
         super.testSubscriptionWithAddOn();
     }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
index e485420..7442298 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
@@ -36,6 +36,7 @@ import java.util.UUID;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
 
 public class TestUserApiError extends TestApiBase {
 
@@ -46,7 +47,7 @@ public class TestUserApiError extends TestApiBase {
     }
 
 
-    @Test(enabled=true)
+    @Test(enabled=true, groups={"fast"})
     public void testCreateSubscriptionBadCatalog() {
         // WRONG PRODUTCS
         tCreateSubscriptionInternal(bundle.getId(), null, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.CAT_NULL_PRODUCT_NAME);
@@ -62,17 +63,17 @@ public class TestUserApiError extends TestApiBase {
 
     }
 
-    @Test(enabled=true)
+    @Test(enabled=true, groups={"fast"})
     public void testCreateSubscriptionNoBundle() {
         tCreateSubscriptionInternal(null, "Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.ENT_CREATE_NO_BUNDLE);
     }
 
-    @Test(enabled=false)
+    @Test(enabled=true, groups={"fast"})
     public void testCreateSubscriptionNoBP() {
-        //tCreateSubscriptionInternal(bundle.getId(), "Shotgun", BillingPeriod.ANNUAL, IPriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.ENT_CREATE_NO_BP);
+        tCreateSubscriptionInternal(bundle.getId(), "Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.ENT_CREATE_NO_BP);
     }
 
-    @Test(enabled=true)
+    @Test(enabled=true, groups={"fast"})
     public void testCreateSubscriptionBPExists() {
         try {
             createSubscription("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME);
@@ -83,6 +84,49 @@ public class TestUserApiError extends TestApiBase {
         }
     }
 
+    @Test(enabled=true, groups={"fast"})
+    public void testRecreateSubscriptionBPNotCancelled() {
+        try {
+            SubscriptionData subscription = createSubscription("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME);
+            try {
+                subscription.recreate(getProductSpecifier("Pistol", PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, null), clock.getUTCNow());
+                Assert.assertFalse(true);
+            } catch (EntitlementUserApiException e) {
+                assertEquals(e.getCode(), ErrorCode.ENT_RECREATE_BAD_STATE.getCode());
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            Assert.assertFalse(true);
+        }
+    }
+
+    @Test(enabled=true, groups={"fast"})
+    public void testCreateSubscriptionAddOnNotAvailable() {
+        try {
+            UUID accountId = UUID.randomUUID();
+            SubscriptionBundle aoBundle = entitlementApi.createBundleForAccount(accountId, "myAOBundle");
+            createSubscriptionWithBundle(aoBundle.getId(), "Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+            tCreateSubscriptionInternal(aoBundle.getId(), "Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.ENT_CREATE_AO_NOT_AVAILABLE);
+        } catch (Exception e) {
+            e.printStackTrace();
+            Assert.assertFalse(true);
+        }
+    }
+
+    @Test(enabled=true, groups={"fast"})
+    public void testCreateSubscriptionAddOnIncluded() {
+        try {
+            UUID accountId = UUID.randomUUID();
+            SubscriptionBundle aoBundle = entitlementApi.createBundleForAccount(accountId, "myAOBundle");
+            createSubscriptionWithBundle(aoBundle.getId(), "Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+            tCreateSubscriptionInternal(aoBundle.getId(), "Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.ENT_CREATE_AO_ALREADY_INCLUDED);
+        } catch (Exception e) {
+            e.printStackTrace();
+            Assert.assertFalse(true);
+        }
+    }
+
+
     private void tCreateSubscriptionInternal(UUID bundleId, String productName,
             BillingPeriod term, String planSet, ErrorCode expected)  {
         try {
@@ -101,7 +145,7 @@ public class TestUserApiError extends TestApiBase {
     }
 
 
-    @Test(enabled=true)
+    @Test(enabled=true, groups={"fast"})
     public void testChangeSubscriptionNonActive() {
         try {
             Subscription subscription = createSubscription("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME);
@@ -125,17 +169,25 @@ public class TestUserApiError extends TestApiBase {
     }
 
 
-    @Test(enabled=true)
+    @Test(enabled=true, groups={"fast"})
     public void testChangeSubscriptionFutureCancelled() {
         try {
             Subscription subscription = createSubscription("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+            PlanPhase trialPhase = subscription.getCurrentPhase();
+
+            // MOVE TO NEXT PHASE
+            PlanPhase currentPhase = subscription.getCurrentPhase();
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            clock.setDeltaFromReality(currentPhase.getDuration(), DAY_IN_MS);
+            assertTrue(testListener.isCompleted(3000));
+
 
             // SET CTD TO CANCEL IN FUTURE
-            PlanPhase trialPhase = subscription.getCurrentPhase();
             DateTime expectedPhaseTrialChange = DefaultClock.addDuration(subscription.getStartDate(), trialPhase.getDuration());
             Duration ctd = getDurationMonth(1);
             DateTime newChargedThroughDate = DefaultClock.addDuration(expectedPhaseTrialChange, ctd);
             billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate);
+
             subscription = entitlementApi.getSubscriptionFromId(subscription.getId());
 
             subscription.cancel(clock.getUTCNow(), false);
@@ -156,11 +208,11 @@ public class TestUserApiError extends TestApiBase {
     }
 
 
-    @Test(enabled=false)
+    @Test(enabled=false, groups={"fast"})
     public void testCancelBadState() {
     }
 
-    @Test(enabled=true)
+    @Test(enabled=true, groups={"fast"})
     public void testUncancelBadState() {
         try {
             Subscription subscription = createSubscription("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java
new file mode 100644
index 0000000..092e1ec
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java
@@ -0,0 +1,118 @@
+/*
+ * 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.user;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+
+import com.google.inject.Injector;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.entitlement.api.TestApiBase;
+import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
+
+public abstract class TestUserApiRecreate extends TestApiBase {
+
+    private static Logger log = LoggerFactory.getLogger(TestUserApiRecreate.class);
+
+
+    protected void testRecreateWithBPCanceledThroughSubscription() {
+        log.info("Starting testRecreateWithBPCanceled");
+        try {
+            testCreateAndRecreate(false);
+        } catch (EntitlementUserApiException e) {
+            log.error("Unexpected exception",e);
+            Assert.fail(e.getMessage());
+        }
+    }
+
+    protected void testCreateWithBPCanceledFromUserApi() {
+        log.info("Starting testCreateWithBPCanceled");
+        try {
+            testCreateAndRecreate(true);
+        } catch (EntitlementUserApiException e) {
+            log.error("Unexpected exception",e);
+            Assert.fail(e.getMessage());
+        }
+    }
+
+
+    private SubscriptionData testCreateAndRecreate(boolean fromUserAPi) throws EntitlementUserApiException {
+
+        DateTime init = clock.getUTCNow();
+        DateTime requestedDate = init.minusYears(1);
+
+        String productName = "Shotgun";
+        BillingPeriod term = BillingPeriod.MONTHLY;
+        String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        testListener.pushExpectedEvent(NextEvent.CREATE);
+        SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
+                getProductSpecifier(productName, planSetName, term, null), requestedDate);
+        assertNotNull(subscription);
+        assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+        assertEquals(subscription.getBundleId(), bundle.getId());
+        assertEquals(subscription.getStartDate(), requestedDate);
+        assertEquals(productName, subscription.getCurrentPlan().getProduct().getName());
+
+        assertTrue(testListener.isCompleted(5000));
+
+        // CREATE (AGAIN) WITH NEW PRODUCT
+        productName = "Pistol";
+        term = BillingPeriod.MONTHLY;
+        planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+        try {
+
+            if (fromUserAPi) {
+                subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
+                        getProductSpecifier(productName, planSetName, term, null), requestedDate);
+            } else {
+                subscription.recreate(getProductSpecifier(productName, planSetName, term, null), requestedDate);
+            }
+            Assert.fail("Expected Create API to fail since BP already exists");
+        } catch (EntitlementUserApiException e) {
+            assertTrue(true);
+        }
+
+        // NOW CANCEL ADN THIS SHOULD WORK
+        testListener.pushExpectedEvent(NextEvent.CANCEL);
+        subscription.cancel(null, false);
+
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        testListener.pushExpectedEvent(NextEvent.CREATE);
+
+        if (fromUserAPi) {
+            subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
+                    getProductSpecifier(productName, planSetName, term, null), requestedDate);
+        } else {
+            subscription.recreate(getProductSpecifier(productName, planSetName, term, null), clock.getUTCNow());
+        }
+        assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+        assertEquals(subscription.getBundleId(), bundle.getId());
+        assertEquals(subscription.getStartDate(), requestedDate);
+        assertEquals(productName, subscription.getCurrentPlan().getProduct().getName());
+
+        return subscription;
+    }
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreateSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreateSql.java
new file mode 100644
index 0000000..2c1db68
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreateSql.java
@@ -0,0 +1,44 @@
+/*
+ * 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.user;
+
+import org.testng.annotations.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.ning.billing.entitlement.glue.MockEngineModuleSql;
+
+public class TestUserApiRecreateSql extends TestUserApiRecreate {
+
+    @Override
+    protected Injector getInjector() {
+        return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
+    }
+
+    @Override
+    @Test(enabled=true, groups={"slow"})
+    protected void testRecreateWithBPCanceledThroughSubscription() {
+        super.testRecreateWithBPCanceledThroughSubscription();
+    }
+
+    @Override
+    @Test(enabled=true, groups={"slow"})
+    protected void testCreateWithBPCanceledFromUserApi() {
+        super.testRecreateWithBPCanceledThroughSubscription();
+    }
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserCustomFieldsSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserCustomFieldsSql.java
new file mode 100644
index 0000000..d888c14
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserCustomFieldsSql.java
@@ -0,0 +1,174 @@
+/*
+ * 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.user;
+
+import static org.testng.Assert.assertNotNull;
+
+import java.util.ArrayList;
+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.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.PriceListSet;
+import com.ning.billing.entitlement.api.TestApiBase;
+import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
+import com.ning.billing.entitlement.glue.MockEngineModuleSql;
+import com.ning.billing.util.customfield.CustomField;
+
+
+public class TestUserCustomFieldsSql extends TestApiBase {
+
+    @Override
+    protected Injector getInjector() {
+        return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
+    }
+
+
+
+    @Test(enabled=false, groups={"slow"})
+    public void stress() {
+        cleanupTest();
+        for (int i = 0; i < 20; i++) {
+            setupTest();
+            testOverwriteCustomFields();
+            cleanupTest();
+
+            setupTest();
+            testBasicCustomFields();
+            cleanupTest();
+        }
+    }
+
+    @Test(enabled=true, groups={"slow"})
+    public void testOverwriteCustomFields() {
+        log.info("Starting testCreateWithRequestedDate");
+        try {
+
+            DateTime init = clock.getUTCNow();
+            DateTime requestedDate = init.minusYears(1);
+
+            String productName = "Shotgun";
+            BillingPeriod term = BillingPeriod.MONTHLY;
+            String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            testListener.pushExpectedEvent(NextEvent.CREATE);
+            SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
+                    getProductSpecifier(productName, planSetName, term, null), requestedDate);
+            assertNotNull(subscription);
+
+            assertEquals(subscription.getFieldValue("nonExistent"), null);
+
+            subscription.setFieldValue("field1", "value1");
+            assertEquals(subscription.getFieldValue("field1"), "value1");
+            List<CustomField> allFields = subscription.getFieldList();
+            assertEquals(allFields.size(), 1);
+
+            subscription.setFieldValue("field1", "valueNew1");
+            assertEquals(subscription.getFieldValue("field1"), "valueNew1");
+            allFields = subscription.getFieldList();
+            assertEquals(allFields.size(), 1);
+
+            subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
+            assertEquals(subscription.getFieldValue("field1"), "valueNew1");
+            allFields = subscription.getFieldList();
+            assertEquals(allFields.size(), 1);
+
+            subscription.setFieldValue("field1", "valueSuperNew1");
+            assertEquals(subscription.getFieldValue("field1"), "valueSuperNew1");
+            allFields = subscription.getFieldList();
+            assertEquals(allFields.size(), 1);
+
+            subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
+            assertEquals(subscription.getFieldValue("field1"), "valueSuperNew1");
+            allFields = subscription.getFieldList();
+            assertEquals(allFields.size(), 1);
+
+            /*
+             * BROKEN
+            subscription.setFieldValue("field1", null);
+            subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
+            assertEquals(subscription.getFieldValue("field1"), null);
+            allFields = subscription.getFieldList();
+            assertEquals(allFields.size(), 1);
+             */
+        } catch (EntitlementUserApiException e) {
+            log.error("Unexpected exception",e);
+            Assert.fail(e.getMessage());
+        }
+    }
+
+    @Test(enabled=true, groups={"slow"})
+    public void testBasicCustomFields() {
+        log.info("Starting testCreateWithRequestedDate");
+        try {
+
+            DateTime init = clock.getUTCNow();
+            DateTime requestedDate = init.minusYears(1);
+
+            String productName = "Shotgun";
+            BillingPeriod term = BillingPeriod.MONTHLY;
+            String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            testListener.pushExpectedEvent(NextEvent.CREATE);
+            SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
+                    getProductSpecifier(productName, planSetName, term, null), requestedDate);
+            assertNotNull(subscription);
+
+
+            subscription.setFieldValue("field1", "value1");
+            assertEquals(subscription.getFieldValue("field1"), "value1");
+            List<CustomField> allFields = subscription.getFieldList();
+            assertEquals(allFields.size(), 1);
+
+            subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
+            assertEquals(subscription.getFieldValue("field1"), "value1");
+            assertEquals(allFields.size(), 1);
+
+            subscription.clearFields();
+
+            subscription.setFieldValue("field2", "value2");
+            subscription.setFieldValue("field3", "value3");
+            assertEquals(subscription.getFieldValue("field2"), "value2");
+            assertEquals(subscription.getFieldValue("field3"), "value3");
+            allFields = subscription.getFieldList();
+            assertEquals(allFields.size(), 2);
+
+            subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
+            assertEquals(subscription.getFieldValue("field2"), "value2");
+            assertEquals(subscription.getFieldValue("field3"), "value3");
+            allFields = subscription.getFieldList();
+            assertEquals(allFields.size(), 2);
+
+        } catch (EntitlementUserApiException e) {
+            log.error("Unexpected exception",e);
+            Assert.fail(e.getMessage());
+        }
+    }
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
index 6395470..085afb9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
@@ -23,6 +23,8 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.TreeSet;
 import java.util.UUID;
+
+import org.apache.commons.lang.NotImplementedException;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.slf4j.Logger;
@@ -169,6 +171,23 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
     @Override
+    public void recreateSubscription(final UUID subscriptionId,
+            final List<EntitlementEvent> recreateEvents) {
+
+        synchronized(events) {
+            events.addAll(recreateEvents);
+            for (final EntitlementEvent cur : recreateEvents) {
+                recordFutureNotificationFromTransaction(null, cur.getEffectiveDate(), new NotificationKey() {
+                    @Override
+                    public String toString() {
+                        return cur.getId().toString();
+                    }
+                });
+            }
+        }
+    }
+
+    @Override
     public List<Subscription> getSubscriptions(final UUID bundleId) {
 
         List<Subscription> results = new ArrayList<Subscription>();
@@ -429,4 +448,10 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
             throw new RuntimeException(e);
         }
     }
+
+    @Override
+    public void saveCustomFields(SubscriptionData subscription) {
+        throw new NotImplementedException();
+    }
+
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java
index c5881f9..cb87dc0 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java
@@ -25,6 +25,7 @@ import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
 
 import com.google.inject.Inject;
 import com.ning.billing.entitlement.api.user.SubscriptionFactory;
+import com.ning.billing.entitlement.engine.addon.AddonUtils;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.notificationq.NotificationQueueService;
 
@@ -33,8 +34,8 @@ public class MockEntitlementDaoSql extends EntitlementSqlDao implements MockEnti
     private final ResetSqlDao resetDao;
 
     @Inject
-    public MockEntitlementDaoSql(IDBI dbi, Clock clock, SubscriptionFactory factory, NotificationQueueService notificationQueueService) {
-        super(dbi, clock, factory, notificationQueueService);
+    public MockEntitlementDaoSql(IDBI dbi, Clock clock, SubscriptionFactory factory, AddonUtils addonUtils, NotificationQueueService notificationQueueService) {
+        super(dbi, clock, factory, addonUtils, notificationQueueService);
         this.resetDao = dbi.onDemand(ResetSqlDao.class);
     }
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
index e9e6134..c452c3b 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
@@ -18,6 +18,7 @@ package com.ning.billing.entitlement.glue;
 
 import com.ning.billing.dbi.DBIProvider;
 import com.ning.billing.dbi.DbiConfig;
+import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.engine.dao.MockEntitlementDaoSql;
 import com.ning.billing.util.clock.Clock;
@@ -36,9 +37,16 @@ public class MockEngineModuleSql extends MockEngineModule {
     }
 
     protected void installDBI() {
-        bind(IDBI.class).toProvider(DBIProvider.class).asEagerSingleton();
-        final DbiConfig config = new ConfigurationObjectFactory(System.getProperties()).build(DbiConfig.class);
-        bind(DbiConfig.class).toInstance(config);
+        final MysqlTestingHelper helper = new MysqlTestingHelper();
+        bind(MysqlTestingHelper.class).toInstance(helper);
+        if (helper.isUsingLocalInstance()) {
+            bind(IDBI.class).toProvider(DBIProvider.class).asEagerSingleton();
+            final DbiConfig config = new ConfigurationObjectFactory(System.getProperties()).build(DbiConfig.class);
+            bind(DbiConfig.class).toInstance(config);
+        } else {
+            final IDBI dbi = helper.getDBI();
+            bind(IDBI.class).toInstance(dbi);
+        }
     }
 
     @Override
diff --git a/entitlement/src/test/resources/entitlement.properties b/entitlement/src/test/resources/entitlement.properties
index d149d78..af1c3fc 100644
--- a/entitlement/src/test/resources/entitlement.properties
+++ b/entitlement/src/test/resources/entitlement.properties
@@ -2,5 +2,5 @@ killbill.catalog.uri=file:src/test/resources/testInput.xml
 killbill.entitlement.dao.claim.time=60000
 killbill.entitlement.dao.ready.max=1
 killbill.entitlement.engine.notifications.sleep=500
-
+user.timezone=UTC
 
diff --git a/entitlement/src/test/resources/testInput.xml b/entitlement/src/test/resources/testInput.xml
index ce3b250..8a97d57 100644
--- a/entitlement/src/test/resources/testInput.xml
+++ b/entitlement/src/test/resources/testInput.xml
@@ -51,13 +51,13 @@ Use Cases to do:
 	<products>
 		<product name="Pistol">
 			<category>BASE</category>
-			<available>
-				<addonProduct>Telescopic-Scope</addonProduct>
-				<addonProduct>Laser-Scope</addonProduct>
-			</available>
 		</product>
 		<product name="Shotgun">
 			<category>BASE</category>
+            <available>
+                <addonProduct>Telescopic-Scope</addonProduct>
+                <addonProduct>Laser-Scope</addonProduct>
+            </available>
 		</product>
 		<product name="Assault-Rifle">
 			<category>BASE</category>
@@ -91,33 +91,18 @@ Use Cases to do:
 				<phaseType>TRIAL</phaseType>
 				<policy>IMMEDIATE</policy>
 			</changePolicyCase>
-			<changePolicyCase> 
-				<toProduct>Pistol</toProduct>
-				<policy>END_OF_TERM</policy>
-			</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> 
-				<fromProduct>Pistol</fromProduct>
-				<toProduct>Shotgun</toProduct>
-				<policy>IMMEDIATE</policy>
-			</changePolicyCase>
-			<changePolicyCase> 
-				<fromProduct>Assault-Rifle</fromProduct>
-				<toProduct>Shotgun</toProduct>
-				<policy>END_OF_TERM</policy>
-			</changePolicyCase>
-			<changePolicyCase> 
-				<fromBillingPeriod>MONTHLY</fromBillingPeriod>
-				<toProduct>Assault-Rifle</toProduct>
-				<toBillingPeriod>MONTHLY</toBillingPeriod>
-				<policy>END_OF_TERM</policy>
-			</changePolicyCase>
-			<changePolicyCase> 
-				<toProduct>Assault-Rifle</toProduct>
-				<policy>IMMEDIATE</policy>
 			</changePolicyCase>
 			<changePolicyCase> 
 				<fromBillingPeriod>MONTHLY</fromBillingPeriod>
@@ -135,9 +120,6 @@ Use Cases to do:
 		</changePolicy>
 		<changeAlignment>
 			<changeAlignmentCase>
-				<alignment>START_OF_SUBSCRIPTION</alignment>
-			</changeAlignmentCase>
-			<changeAlignmentCase>
 				<toPriceList>rescue</toPriceList>
 				<alignment>CHANGE_OF_PLAN</alignment>
 			</changeAlignmentCase>
@@ -146,20 +128,27 @@ Use Cases to do:
 				<toPriceList>rescue</toPriceList>
 				<alignment>CHANGE_OF_PRICELIST</alignment>
 			</changeAlignmentCase>
+            <changeAlignmentCase>
+                <alignment>START_OF_SUBSCRIPTION</alignment>
+            </changeAlignmentCase>
 		</changeAlignment>
 		<cancelPolicy>
 			<cancelPolicyCase>
-				<policy>END_OF_TERM</policy>
-			</cancelPolicyCase>
-			<cancelPolicyCase>
 				<phaseType>TRIAL</phaseType>
 				<policy>IMMEDIATE</policy>
 			</cancelPolicyCase>
+            <cancelPolicyCase>
+                <policy>END_OF_TERM</policy>
+            </cancelPolicyCase>
 		</cancelPolicy>
 		<createAlignment>
-			<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>
@@ -447,6 +436,20 @@ Use Cases to do:
 		</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>
 			<finalPhase type="EVERGREEN">
 				<duration>
 					<unit>UNLIMITED</unit>
@@ -461,6 +464,20 @@ Use Cases to do:
 		</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>
 			<finalPhase type="EVERGREEN">
 				<duration>
 					<unit>UNLIMITED</unit>
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
index 4f936fd..b46247a 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
@@ -39,6 +39,8 @@ import com.ning.billing.invoice.model.DefaultInvoicePayment;
 import com.ning.billing.invoice.model.InvoiceGenerator;
 import com.ning.billing.invoice.model.InvoiceItemList;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
+import com.ning.billing.mock.BrainDeadProxyFactory;
+import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.DefaultClock;
 import org.joda.time.DateTime;
@@ -508,7 +510,9 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         MockPlanPhase phase1 = new MockPlanPhase(recurringPrice, null, BillingPeriod.MONTHLY, PhaseType.TRIAL);
         MockPlan plan1 = new MockPlan(phase1);
 
-        Subscription subscription = new MockSubscription();
+        Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
+        ((ZombieControl) subscription).addResult("getId", UUID.randomUUID());
+
         DateTime effectiveDate1 = new DateTime(2011, 2, 1, 0, 0, 0, 0);
         BillingEvent event1 = new DefaultBillingEvent(subscription, effectiveDate1, plan1, phase1, null,
                 recurringPrice, BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
@@ -556,7 +560,8 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         MockPlanPhase phase = new MockPlanPhase(recurringPrice, null);
         MockPlan plan = new MockPlan(phase);
 
-        Subscription subscription = new MockSubscription();
+        Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
+        ((ZombieControl) subscription).addResult("getId", UUID.randomUUID());
         DateTime effectiveDate = buildDateTime(2011, 1, 1);
 
         BillingEvent event = new DefaultBillingEvent(subscription, effectiveDate, plan, phase, null,
@@ -586,7 +591,9 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         MockPlanPhase phase2 = new MockPlanPhase(recurringPrice, null);
 
         MockPlan plan = new MockPlan();
-        Subscription subscription = new MockSubscription();
+
+        Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
+        ((ZombieControl) subscription).addResult("getId", UUID.randomUUID());
         DateTime effectiveDate1 = buildDateTime(2011, 1, 1);
 
         BillingEvent event1 = new DefaultBillingEvent(subscription, effectiveDate1, plan, phase1, fixedPrice,
@@ -643,7 +650,9 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         MockPlanPhase phase2 = new MockPlanPhase(recurringPrice, null);
 
         MockPlan plan = new MockPlan();
-        Subscription subscription = new MockSubscription();
+
+        Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
+        ((ZombieControl) subscription).addResult("getId", UUID.randomUUID());
         DateTime effectiveDate1 = buildDateTime(2011, 1, 1);
 
         BillingEvent event1 = new DefaultBillingEvent(subscription, effectiveDate1, plan, phase1, fixedPrice,
diff --git a/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java b/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
index 67c27fa..fdc5076 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
@@ -57,6 +57,8 @@ import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.invoice.InvoiceListener;
 import com.ning.billing.invoice.dao.DefaultInvoiceDao;
 import com.ning.billing.lifecycle.KillbillService.ServiceException;
+import com.ning.billing.mock.BrainDeadProxyFactory;
+import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.bus.InMemoryBus;
 import com.ning.billing.util.clock.Clock;
@@ -73,7 +75,7 @@ public class TestNextBillingDateNotifier {
 	private DummySqlTest dao;
 	private Bus eventBus;
 	private MysqlTestingHelper helper;
-	private InvoiceListenerMock listener = new InvoiceListenerMock();
+	private final InvoiceListenerMock listener = new InvoiceListenerMock();
 	private NotificationQueueService notificationQueueService;
 
 	private static final class InvoiceListenerMock extends InvoiceListener {
@@ -83,7 +85,7 @@ public class TestNextBillingDateNotifier {
 		public InvoiceListenerMock() {
 			super(null);
 		}
-		
+
 
 		@Override
 		public void handleNextBillingDateEvent(UUID subscriptionId,
@@ -91,146 +93,24 @@ public class TestNextBillingDateNotifier {
 			eventCount++;
 			latestSubscriptionId=subscriptionId;
 		}
-		
+
 		public int getEventCount() {
 			return eventCount;
 		}
-		
+
 		public UUID getLatestSubscriptionId(){
 			return latestSubscriptionId;
 		}
-		
-	}
-	
-	private class MockEntitlementDao implements EntitlementDao {
-
-		@Override
-		public List<SubscriptionBundle> getSubscriptionBundleForAccount(
-				UUID accountId) {
-			throw new UnsupportedOperationException();
-		}
-
-		@Override
-		public SubscriptionBundle getSubscriptionBundleFromKey(String bundleKey) {
-			throw new UnsupportedOperationException();
-		}
-
-		@Override
-		public SubscriptionBundle getSubscriptionBundleFromId(UUID bundleId) {
-			throw new UnsupportedOperationException();
-		}
-
-		@Override
-		public SubscriptionBundle createSubscriptionBundle(
-				SubscriptionBundleData bundle) {
-			throw new UnsupportedOperationException();
-
-		}
-
-		@Override
-		public Subscription getSubscriptionFromId(UUID subscriptionId) {
-			return new BrainDeadSubscription();
-
-		}
-
-		@Override
-		public UUID getAccountIdFromSubscriptionId(UUID subscriptionId) {
-			throw new UnsupportedOperationException();
-
-		}
-
-		@Override
-		public Subscription getBaseSubscription(UUID bundleId) {
-			throw new UnsupportedOperationException();
-
-		}
-
-		@Override
-		public List<Subscription> getSubscriptions(UUID bundleId) {
-			throw new UnsupportedOperationException();
-
-		}
-
-		@Override
-		public List<Subscription> getSubscriptionsForKey(String bundleKey) {
-			throw new UnsupportedOperationException();
-
-		}
-
-		@Override
-		public void updateSubscription(SubscriptionData subscription) {
-			throw new UnsupportedOperationException();
-		}
 
-		@Override
-		public void createNextPhaseEvent(UUID subscriptionId,
-				EntitlementEvent nextPhase) {
-			throw new UnsupportedOperationException();
-		}
-
-		@Override
-		public EntitlementEvent getEventById(UUID eventId) {
-			throw new UnsupportedOperationException();
-
-		}
-
-		@Override
-		public List<EntitlementEvent> getEventsForSubscription(
-				UUID subscriptionId) {
-			throw new UnsupportedOperationException();
-
-		}
-
-		@Override
-		public List<EntitlementEvent> getPendingEventsForSubscription(
-				UUID subscriptionId) {
-			throw new UnsupportedOperationException();
-
-		}
-
-		@Override
-		public void createSubscription(SubscriptionData subscription,
-				List<EntitlementEvent> initialEvents) {
-			throw new UnsupportedOperationException();
-		}
-
-		@Override
-		public void cancelSubscription(UUID subscriptionId,
-				EntitlementEvent cancelEvent) {
-			throw new UnsupportedOperationException();
-
-		}
-
-		@Override
-		public void uncancelSubscription(UUID subscriptionId,
-				List<EntitlementEvent> uncancelEvents) {
-			throw new UnsupportedOperationException();
-	
-		}
-
-		@Override
-		public void changePlan(UUID subscriptionId,
-				List<EntitlementEvent> changeEvents) {
-			throw new UnsupportedOperationException();
-		}
+	}
 
-		@Override
-		public void migrate(UUID acountId, AccountMigrationData data) {
-			throw new UnsupportedOperationException();
-		}
 
-		@Override
-		public void undoMigration(UUID accountId) {
-			throw new UnsupportedOperationException();
-		}
-		
-	}
-	
 	@BeforeClass(groups={"setup"})
 	public void setup() throws ServiceException, IOException, ClassNotFoundException, SQLException {
 		//TestApiBase.loadSystemPropertiesFromClasspath("/entitlement.properties");
         final Injector g = Guice.createInjector(Stage.PRODUCTION,  new AbstractModule() {
-			protected void configure() {
+			@Override
+            protected void configure() {
 				 bind(Clock.class).to(ClockMock.class).asEagerSingleton();
 				 bind(Bus.class).to(InMemoryBus.class).asEagerSingleton();
 				 bind(NotificationQueueService.class).to(DefaultNotificationQueueService.class).asEagerSingleton();
@@ -253,7 +133,13 @@ public class TestNextBillingDateNotifier {
         eventBus = g.getInstance(Bus.class);
         helper = g.getInstance(MysqlTestingHelper.class);
         notificationQueueService = g.getInstance(NotificationQueueService.class);
-        notifier = new DefaultNextBillingDateNotifier(notificationQueueService,g.getInstance(InvoiceConfig.class), new MockEntitlementDao(), listener);
+
+
+        Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
+        EntitlementDao entitlementDao = BrainDeadProxyFactory.createBrainDeadProxyFor(EntitlementDao.class);
+        ((ZombieControl) entitlementDao).addResult("getSubscriptionFromId", subscription);
+
+        notifier = new DefaultNextBillingDateNotifier(notificationQueueService,g.getInstance(InvoiceConfig.class), entitlementDao, listener);
         startMysql();
 	}
 
@@ -273,12 +159,12 @@ public class TestNextBillingDateNotifier {
 		final UUID subscriptionId = new UUID(0L,1L);
 		final DateTime now = new DateTime();
 		final DateTime readyTime = now.plusMillis(2000);
-		final NextBillingDatePoster poster = new DefaultNextBillingDatePoster(notificationQueueService); 
+		final NextBillingDatePoster poster = new DefaultNextBillingDatePoster(notificationQueueService);
 
 		eventBus.start();
 		notifier.initialize();
 		notifier.start();
-		
+
 
 		dao.inTransaction(new Transaction<Void, DummySqlTest>() {
 			@Override
@@ -289,8 +175,8 @@ public class TestNextBillingDateNotifier {
 				return null;
 			}
 		});
-		
-		
+
+
 		// Move time in the future after the notification effectiveDate
 		((ClockMock) clock).setDeltaFromReality(3000);
 
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
index 136eeb5..e633755 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
@@ -35,13 +35,14 @@ import com.ning.billing.entitlement.api.user.SubscriptionTransition.Subscription
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoiceItem;
-import com.ning.billing.invoice.dao.MockSubscription;
 import com.ning.billing.invoice.model.BillingEventSet;
 import com.ning.billing.invoice.model.DefaultInvoiceGenerator;
 import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
 import com.ning.billing.invoice.model.InvoiceGenerator;
 import com.ning.billing.invoice.model.InvoiceItemList;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
+import com.ning.billing.mock.BrainDeadProxyFactory;
+import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.DefaultClock;
 
@@ -452,7 +453,9 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
     @Test
     public void testFixedPriceLifeCycle() throws InvoiceApiException {
         UUID accountId = UUID.randomUUID();
-        Subscription subscription = new MockSubscription();
+        Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
+        ((ZombieControl) subscription).addResult("getId", UUID.randomUUID());
+
         Plan plan = new MockPlan("plan 1");
         MockInternationalPrice zeroPrice = new MockInternationalPrice(new DefaultPrice(ZERO, Currency.USD));
         MockInternationalPrice cheapPrice = new MockInternationalPrice(new DefaultPrice(ONE, Currency.USD));
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
index 4dd5040..3d8740a 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
@@ -154,7 +154,13 @@ public class DefaultPaymentApi implements PaymentApi {
                                                         paymentAttempt.getInvoiceId()));
                 }
                 else {
-                    return processPayment(getPaymentProviderPlugin(account), account, invoice, paymentAttempt);
+                    PaymentAttempt newPaymentAttempt = new PaymentAttempt.Builder(paymentAttempt)
+                                                                         .setRetryCount(paymentAttempt.getRetryCount() + 1)
+                                                                         .setPaymentAttemptId(UUID.randomUUID())
+                                                                         .build();
+
+                    paymentDao.createPaymentAttempt(newPaymentAttempt);
+                    return processPayment(getPaymentProviderPlugin(account), account, invoice, newPaymentAttempt);
                 }
             }
         }
@@ -258,7 +264,6 @@ public class DefaultPaymentApi implements PaymentApi {
             }
 
             retryService.scheduleRetry(paymentAttempt, nextRetryDate);
-            paymentDao.updatePaymentAttemptWithRetryInfo(paymentAttempt.getPaymentAttemptId(), retryCount + 1, nextRetryDate);
         }
         else if (retryCount == retryDays.size()) {
             log.info("Last payment retry failed for {} ", paymentAttempt);
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
index 49a4165..00cdb31 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
@@ -16,11 +16,9 @@
 
 package com.ning.billing.payment.dao;
 
-import java.util.Date;
 import java.util.List;
 import java.util.UUID;
 
-import org.joda.time.DateTime;
 import org.skife.jdbi.v2.IDBI;
 
 import com.google.common.collect.ImmutableList;
@@ -98,13 +96,6 @@ public class DefaultPaymentDao implements PaymentDao {
     }
 
     @Override
-    public void updatePaymentAttemptWithRetryInfo(UUID paymentAttemptId, int retryCount, DateTime nextRetryDate) {
-
-        final Date retryDate = nextRetryDate == null ? null : nextRetryDate.toDate();
-        sqlDao.updatePaymentAttemptWithRetryInfo(paymentAttemptId.toString(), retryCount, retryDate, clock.getUTCNow().toDate());
-    }
-
-    @Override
     public PaymentAttempt getPaymentAttemptById(UUID paymentAttemptId) {
         return sqlDao.getPaymentAttemptById(paymentAttemptId.toString());
     }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
index de2d8cc..2c9ee06 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
@@ -19,8 +19,6 @@ package com.ning.billing.payment.dao;
 import java.util.List;
 import java.util.UUID;
 
-import org.joda.time.DateTime;
-
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.payment.api.PaymentAttempt;
 import com.ning.billing.payment.api.PaymentInfo;
@@ -36,7 +34,6 @@ public interface PaymentDao {
     List<PaymentAttempt> getPaymentAttemptsForInvoiceIds(List<String> invoiceIds);
 
     void updatePaymentAttemptWithPaymentId(UUID paymentAttemptId, String paymentId);
-    void updatePaymentAttemptWithRetryInfo(UUID paymentAttemptId, int retryCount, DateTime nextRetryDate);
 
     PaymentAttempt getPaymentAttemptForInvoiceId(String invoiceId);
 
@@ -46,5 +43,4 @@ public interface PaymentDao {
 
     PaymentAttempt getPaymentAttemptById(UUID paymentAttemptId);
     PaymentInfo getPaymentInfoForPaymentAttemptId(String paymentAttemptId);
-
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
index ea564f3..69117fb 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
@@ -77,11 +77,9 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Tr
     @SqlUpdate
     void updatePaymentAttemptWithRetryInfo(@Bind("payment_attempt_id") String paymentAttemptId,
                                            @Bind("retry_count") int retryCount,
-                                           @Bind("next_retry_dt") Date nextRetryDate,
                                            @Bind("updated_dt") Date updatedDate);
 
     @SqlUpdate
-
     void updatePaymentInfo(@Bind("payment_method") String paymentMethod,
                            @Bind("payment_id") String paymentId,
                            @Bind("card_type") String cardType,
@@ -112,7 +110,6 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Tr
             stmt.bind("payment_attempt_dt", getDate(paymentAttempt.getPaymentAttemptDate()));
             stmt.bind("payment_id", paymentAttempt.getPaymentId());
             stmt.bind("retry_count", paymentAttempt.getRetryCount());
-            stmt.bind("next_retry_dt", getDate(paymentAttempt.getNextRetryDate()));
             stmt.bind("created_dt", getDate(paymentAttempt.getCreatedDate()));
             stmt.bind("updated_dt", getDate(paymentAttempt.getUpdatedDate()));
         }
@@ -137,7 +134,6 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Tr
             DateTime paymentAttemptDate = getDate(rs, "payment_attempt_dt");
             String paymentId = rs.getString("payment_id");
             Integer retryCount = rs.getInt("retry_count");
-            DateTime nextRetryDate = getDate(rs, "next_retry_dt");
             DateTime createdDate = getDate(rs, "created_dt");
             DateTime updatedDate = getDate(rs, "updated_dt");
 
@@ -150,7 +146,6 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Tr
                                       paymentAttemptDate,
                                       paymentId,
                                       retryCount,
-                                      nextRetryDate,
                                       createdDate,
                                       updatedDate);
         }
diff --git a/payment/src/main/java/com/ning/billing/payment/RetryService.java b/payment/src/main/java/com/ning/billing/payment/RetryService.java
index ee57397..b4ceb5f 100644
--- a/payment/src/main/java/com/ning/billing/payment/RetryService.java
+++ b/payment/src/main/java/com/ning/billing/payment/RetryService.java
@@ -96,7 +96,10 @@ public class RetryService implements KillbillService {
     private void retry(String paymentAttemptId) {
         PaymentInfo paymentInfo = paymentApi.getPaymentInfoForPaymentAttemptId(paymentAttemptId);
 
-        if (paymentInfo == null || !PaymentStatus.Processed.equals(PaymentStatus.valueOf(paymentInfo.getStatus()))) {
+        if (paymentInfo != null && PaymentStatus.Processed.equals(PaymentStatus.valueOf(paymentInfo.getStatus()))) {
+            // update payment attempt with success and notify invoice api of payment
+        }
+        else {
             paymentApi.createPaymentForPaymentAttempt(UUID.fromString(paymentAttemptId));
         }
     }
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
index b349b5e..91eeb22 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
@@ -10,7 +10,6 @@ paymentAttemptFields(prefix) ::= <<
     <prefix>payment_attempt_dt,
     <prefix>invoice_dt,
     <prefix>retry_count,
-    <prefix>next_retry_dt,
     <prefix>created_dt,
     <prefix>updated_dt
 >>
@@ -35,7 +34,7 @@ paymentInfoFields(prefix) ::= <<
 
 insertPaymentAttempt() ::= <<
     INSERT INTO payment_attempts (<paymentAttemptFields()>)
-    VALUES (:payment_attempt_id, :invoice_id, :account_id, :amount, :currency, :payment_id, :payment_attempt_dt, :invoice_dt, :retry_count, :next_retry_dt, :created_dt, :updated_dt);
+    VALUES (:payment_attempt_id, :invoice_id, :account_id, :amount, :currency, :payment_id, :payment_attempt_dt, :invoice_dt, :retry_count, :created_dt, :updated_dt);
 >>
 
 getPaymentAttemptForPaymentId() ::= <<
@@ -83,17 +82,16 @@ updatePaymentInfo() ::= <<
      WHERE payment_id = :payment_id
 >>
 
-updatePaymentAttemptWithRetryInfo() ::= <<
-    UPDATE payment_attempts
-       SET retry_count = :retry_count,
-           next_retry_dt = :next_retry_dt,
-           updated_dt = :updated_dt
-     WHERE payment_attempt_id = :payment_attempt_id
->>
-
 getPaymentInfos(invoiceIds) ::= <<
     SELECT <paymentInfoFields("p.")>
       FROM payments p, payment_attempts pa
      WHERE pa.invoice_id in (<invoiceIds>)
        AND pa.payment_id = p.payment_id
->>
\ No newline at end of file
+>>
+
+getPaymentInfoForPaymentAttemptId() ::= <<
+    SELECT <paymentInfoFields("p.")>
+      FROM payments p, payment_attempts pa
+     WHERE pa.payment_attempt_id = :payment_attempt_id
+       AND pa.payment_id = p.payment_id
+>>
diff --git a/payment/src/main/resources/com/ning/billing/payment/ddl.sql b/payment/src/main/resources/com/ning/billing/payment/ddl.sql
index abbd8a6..3d0cc1a 100644
--- a/payment/src/main/resources/com/ning/billing/payment/ddl.sql
+++ b/payment/src/main/resources/com/ning/billing/payment/ddl.sql
@@ -8,7 +8,6 @@ CREATE TABLE payment_attempts (
       payment_attempt_dt datetime NOT NULL,
       payment_id varchar(36) COLLATE utf8_bin,
       retry_count tinyint,
-      next_retry_dt datetime,
       invoice_dt datetime NOT NULL,
       created_dt datetime NOT NULL,
       updated_dt datetime NOT NULL,
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
index 10b81da..b4bfb51 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
@@ -23,13 +23,13 @@ import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.payment.api.PaymentAttempt;
 import com.ning.billing.payment.api.PaymentInfo;
-import org.joda.time.DateTimeZone;
 
 public class MockPaymentDao implements PaymentDao {
     private final Map<String, PaymentInfo> payments = new ConcurrentHashMap<String, PaymentInfo>();
@@ -127,15 +127,6 @@ public class MockPaymentDao implements PaymentDao {
     }
 
     @Override
-    public void updatePaymentAttemptWithRetryInfo(UUID paymentAttemptId, int retryCount, DateTime nextRetryDate) {
-        PaymentAttempt existingAttempt = paymentAttempts.get(paymentAttemptId);
-        if (existingAttempt != null) {
-            PaymentAttempt attempt = existingAttempt.cloner().setPaymentAttemptId(paymentAttemptId).setRetryCount(retryCount).setNextRetryDate(nextRetryDate).build();
-            paymentAttempts.put(paymentAttemptId, attempt);
-        }
-    }
-
-    @Override
     public PaymentAttempt getPaymentAttemptById(UUID paymentAttemptId) {
         return paymentAttempts.get(paymentAttemptId);
     }
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
index 6318d28..70708eb 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
@@ -16,18 +16,19 @@
 
 package com.ning.billing.payment.dao;
 
-import com.ning.billing.account.api.AccountApiException;
-import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.payment.api.PaymentAttempt;
-import com.ning.billing.payment.api.PaymentInfo;
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.UUID;
+
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import java.math.BigDecimal;
-import java.util.Arrays;
-import java.util.UUID;
+import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.payment.api.PaymentAttempt;
+import com.ning.billing.payment.api.PaymentInfo;
 
 public abstract class TestPaymentDao {
 
@@ -69,7 +70,6 @@ public abstract class TestPaymentDao {
         paymentDao.savePaymentInfo(paymentInfo);
 
         paymentDao.updatePaymentInfo("CreditCard", paymentInfo.getPaymentId(), "Visa", "US");
-
     }
 
     @Test
@@ -86,8 +86,6 @@ public abstract class TestPaymentDao {
                 .build();
 
         paymentDao.createPaymentAttempt(paymentAttempt);
-
-        paymentDao.updatePaymentAttemptWithRetryInfo(paymentAttempt.getPaymentAttemptId(), 1, paymentAttempt.getCreatedDate().plusDays(1));
     }
 
     @Test
@@ -101,7 +99,7 @@ public abstract class TestPaymentDao {
         // Move the clock backwards to test the updated_date field (see below)
         final DateTime now = new DateTime(DateTimeZone.UTC).minusDays(1);
 
-        PaymentAttempt originalPaymenAttempt = new PaymentAttempt(paymentAttemptId, invoiceId, accountId, invoiceAmount, Currency.USD, now, now, paymentId, null, null);
+        PaymentAttempt originalPaymenAttempt = new PaymentAttempt(paymentAttemptId, invoiceId, accountId, invoiceAmount, Currency.USD, now, now, paymentId, 0);
 
         PaymentAttempt attempt = paymentDao.createPaymentAttempt(originalPaymenAttempt);
 
diff --git a/payment/src/test/java/com/ning/billing/payment/TestRetryService.java b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
index 6a08eb5..75a21ee 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
@@ -134,12 +134,10 @@ public class TestRetryService {
 
         assertNotNull(paymentAttempt);
         assertEquals(notification.getNotificationKey(), paymentAttempt.getPaymentAttemptId().toString());
-        assertEquals(paymentAttempt.getRetryCount(), new Integer(1));
 
         DateTime expectedRetryDate = paymentAttempt.getPaymentAttemptDate().plusDays(paymentConfig.getPaymentRetryDays().get(0));
 
         assertEquals(notification.getEffectiveDate(), expectedRetryDate);
-        assertEquals(paymentAttempt.getNextRetryDate(), expectedRetryDate);
     }
 
     @Test
@@ -166,14 +164,13 @@ public class TestRetryService {
         PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice).cloner()
                                                                                       .setRetryCount(1)
                                                                                       .setPaymentAttemptDate(paymentAttemptDate)
-                                                                                      .setNextRetryDate(nextRetryDate)
                                                                                       .build();
 
         paymentDao.createPaymentAttempt(paymentAttempt);
         retryService.scheduleRetry(paymentAttempt, nextRetryDate);
 
         // wait a little to give the queue time to process
-        Thread.sleep(paymentConfig.getNotificationSleepTimeMs() * 2);
+        Thread.sleep(paymentConfig.getNotificationSleepTimeMs() * 10);
 
         List<Notification> pendingNotifications = mockNotificationQueue.getPendingEvents();
 
@@ -189,7 +186,6 @@ public class TestRetryService {
 
         PaymentAttempt updatedAttempt = paymentApi.getPaymentAttemptForInvoiceId(invoice.getId().toString());
 
-        assertEquals(updatedAttempt.getPaymentAttemptId(), paymentAttempt.getPaymentAttemptId());
         assertEquals(paymentInfo.getPaymentId(), updatedAttempt.getPaymentId());
 
     }
diff --git a/util/src/test/java/com/ning/billing/mock/BrainDeadProxyFactory.java b/util/src/test/java/com/ning/billing/mock/BrainDeadProxyFactory.java
index 31dcd30..2998d07 100644
--- a/util/src/test/java/com/ning/billing/mock/BrainDeadProxyFactory.java
+++ b/util/src/test/java/com/ning/billing/mock/BrainDeadProxyFactory.java
@@ -26,48 +26,48 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class BrainDeadProxyFactory {
-	private static final Logger log = LoggerFactory.getLogger(BrainDeadProxyFactory.class);
+    private static final Logger log = LoggerFactory.getLogger(BrainDeadProxyFactory.class);
 
-	public static interface ZombieControl {
-		
-		public ZombieControl addResult(String method, Object result);
-		
-		public ZombieControl clearResults();
-		
-	}
+    public static interface ZombieControl {
 
-	@SuppressWarnings("unchecked")
-	public static <T> T createBrainDeadProxyFor(final Class<T> clazz) {
-		return (T) Proxy.newProxyInstance(clazz.getClassLoader(),
+        public ZombieControl addResult(String method, Object result);
+
+        public ZombieControl clearResults();
+
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> T createBrainDeadProxyFor(final Class<T> clazz) {
+        return (T) Proxy.newProxyInstance(clazz.getClassLoader(),
                 new Class[] { clazz , ZombieControl.class},
                 new InvocationHandler() {
-					private Map<String,Object> results = new HashMap<String,Object>();
-			
-					@Override
-					public Object invoke(Object proxy, Method method, Object[] args)
-							throws Throwable {
-						
-						if(method.getDeclaringClass().equals(ZombieControl.class)) {
-							if(method.getName().equals("addResult")) {
-								results.put((String) args[0], args[1]);
-								return proxy;
-							} else if(method.getName().equals("clearResults")) {
-								results.clear();
-								return proxy;
-							}
+            private final Map<String,Object> results = new HashMap<String,Object>();
+
+            @Override
+            public Object invoke(Object proxy, Method method, Object[] args)
+                throws Throwable {
+
+                if (method.getDeclaringClass().equals(ZombieControl.class)) {
+                    if(method.getName().equals("addResult")) {
+                        results.put((String) args[0], args[1]);
+                        return proxy;
+                    } else if(method.getName().equals("clearResults")) {
+                        results.clear();
+                        return proxy;
+                    }
+
+                } else {
 
-						} else {
-							
-							Object result = results.get(method.getName());
-							if (result != null) {
-								return result;
-							} else {
-								log.error(String.format("No result for Method: '%s' on Class '%s'",method.getName(), method.getDeclaringClass().getName()));
-								throw new UnsupportedOperationException();
-							}
-						}
-						return (Void) null;
-					}
-				});
-	}
+                    Object result = results.get(method.getName());
+                    if (result != null) {
+                        return result;
+                    } else {
+                        log.error(String.format("No result for Method: '%s' on Class '%s'",method.getName(), method.getDeclaringClass().getName()));
+                        throw new UnsupportedOperationException();
+                    }
+                }
+                return null;
+            }
+        });
+    }
 }