killbill-aplcache
Changes
api/src/main/java/com/ning/billing/entitlement/api/transfer/EntitlementTransferApiException.java 45(+45 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java 4(+2 -2)
entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultEntitlementTimelineApi.java 92(+56 -36)
entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultSubscriptionTimeline.java 23(+21 -2)
entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java 282(+282 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/TransferCancelData.java 39(+39 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java 18(+12 -6)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionApiService.java 56(+28 -28)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionFactory.java 13(+7 -6)
entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/AuditedEntitlementDao.java 155(+93 -62)
entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.java 3(+3 -0)
entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java 20(+17 -3)
entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java 10(+10 -0)
entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg 10(+9 -1)
entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestApiBaseRepair.java 12(+11 -1)
entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithError.java 16(+8 -8)
entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java 28(+24 -4)
Details
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/SubscriptionTransitionType.java b/api/src/main/java/com/ning/billing/entitlement/api/SubscriptionTransitionType.java
index 8e75675..a788ee0 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/SubscriptionTransitionType.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/SubscriptionTransitionType.java
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2010-2011 Ning, Inc.
*
* Ning licenses this file to you under the Apache License, version 2.0
@@ -19,6 +19,7 @@ public enum SubscriptionTransitionType {
MIGRATE_ENTITLEMENT,
CREATE,
MIGRATE_BILLING,
+ TRANSFER,
CHANGE,
RE_CREATE,
CANCEL,
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/timeline/EntitlementTimelineApi.java b/api/src/main/java/com/ning/billing/entitlement/api/timeline/EntitlementTimelineApi.java
index 0bdf47f..18296be 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/timeline/EntitlementTimelineApi.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/timeline/EntitlementTimelineApi.java
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2010-2011 Ning, Inc.
*
* Ning licenses this file to you under the Apache License, version 2.0
@@ -17,11 +17,20 @@ package com.ning.billing.entitlement.api.timeline;
import java.util.UUID;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.util.callcontext.CallContext;
public interface EntitlementTimelineApi {
- public BundleTimeline getBundleRepair(final UUID bundleId) throws EntitlementRepairException;
+ public BundleTimeline getBundleTimeline(final SubscriptionBundle bundle)
+ throws EntitlementRepairException;
- public BundleTimeline repairBundle(final BundleTimeline input, final boolean dryRun, final CallContext context) throws EntitlementRepairException;
+ public BundleTimeline getBundleTimeline(final UUID accountId, final String bundleName)
+ throws EntitlementRepairException;
+
+ public BundleTimeline getBundleTimeline(final UUID bundleId)
+ throws EntitlementRepairException;
+
+ public BundleTimeline repairBundle(final BundleTimeline input, final boolean dryRun, final CallContext context)
+ throws EntitlementRepairException;
}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/timeline/SubscriptionTimeline.java b/api/src/main/java/com/ning/billing/entitlement/api/timeline/SubscriptionTimeline.java
index 9374c62..81ed298 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/timeline/SubscriptionTimeline.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/timeline/SubscriptionTimeline.java
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2010-2011 Ning, Inc.
*
* Ning licenses this file to you under the Apache License, version 2.0
@@ -33,6 +33,8 @@ public interface SubscriptionTimeline {
public List<ExistingEvent> getExistingEvents();
+ public long getActiveVersion();
+
public interface DeletedEvent {
public UUID getEventId();
}
@@ -48,5 +50,7 @@ public interface SubscriptionTimeline {
public interface ExistingEvent extends DeletedEvent, NewEvent {
public DateTime getEffectiveDate();
+
+ public String getPlanPhaseName();
}
}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/transfer/EntitlementTransferApi.java b/api/src/main/java/com/ning/billing/entitlement/api/transfer/EntitlementTransferApi.java
new file mode 100644
index 0000000..ce09e8c
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/transfer/EntitlementTransferApi.java
@@ -0,0 +1,29 @@
+/*
+ * 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.transfer;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.util.callcontext.CallContext;
+
+public interface EntitlementTransferApi {
+
+ public void transferBundle(final UUID sourceAccountId, final UUID destAccountId, final String bundleKey, final DateTime requestedDate, final boolean transferAddOn, final CallContext context)
+ throws EntitlementTransferApiException;
+
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/transfer/EntitlementTransferApiException.java b/api/src/main/java/com/ning/billing/entitlement/api/transfer/EntitlementTransferApiException.java
new file mode 100644
index 0000000..150d772
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/transfer/EntitlementTransferApiException.java
@@ -0,0 +1,45 @@
+/*
+ * 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.transfer;
+
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.entitlement.api.timeline.EntitlementRepairException;
+
+public class EntitlementTransferApiException extends BillingExceptionBase {
+
+ private static final long serialVersionUID = 17086131L;
+
+ public EntitlementTransferApiException(final CatalogApiException e) {
+ super(e, e.getCode(), e.getMessage());
+ }
+
+ public EntitlementTransferApiException(final EntitlementRepairException e) {
+ super(e, e.getCode(), e.getMessage());
+ }
+ public EntitlementTransferApiException(final Throwable e, final ErrorCode code, final Object... args) {
+ super(e, code, args);
+ }
+
+ public EntitlementTransferApiException(final Throwable e, final int code, final String message) {
+ super(e, code, message);
+ }
+
+ public EntitlementTransferApiException(final ErrorCode code, final Object... args) {
+ super(code, args);
+ }
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java b/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
index 0ea8cea..2a78f6c 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
@@ -29,28 +29,30 @@ import com.ning.billing.util.callcontext.CallContext;
public interface EntitlementUserApi {
- public SubscriptionBundle getBundleFromId(UUID id) throws EntitlementUserApiException;
+ public SubscriptionBundle getBundleFromId(final UUID id) throws EntitlementUserApiException;
- public Subscription getSubscriptionFromId(UUID id) throws EntitlementUserApiException;
+ public Subscription getSubscriptionFromId(final UUID id) throws EntitlementUserApiException;
- public SubscriptionBundle getBundleForKey(String bundleKey) throws EntitlementUserApiException;
+ public List<SubscriptionBundle> getBundlesForKey(final String bundleKey) throws EntitlementUserApiException;
- public List<SubscriptionBundle> getBundlesForAccount(UUID accountId);
+ public SubscriptionBundle getBundleForAccountAndKey(final UUID accountId, final String bundleKey) throws EntitlementUserApiException;
- public List<Subscription> getSubscriptionsForBundle(UUID bundleId);
+ public List<SubscriptionBundle> getBundlesForAccount(final UUID accountId);
- public List<Subscription> getSubscriptionsForKey(String bundleKey);
+ public List<Subscription> getSubscriptionsForBundle(final UUID bundleId);
- public Subscription getBaseSubscription(UUID bundleId) throws EntitlementUserApiException;
+ public List<Subscription> getSubscriptionsForAccountAndKey(final UUID accountId, final String bundleKey);
- public SubscriptionBundle createBundleForAccount(UUID accountId, String bundleKey, CallContext context)
+ public Subscription getBaseSubscription(final UUID bundleId) throws EntitlementUserApiException;
+
+ public SubscriptionBundle createBundleForAccount(final UUID accountId, final String bundleKey, final CallContext context)
throws EntitlementUserApiException;
- public Subscription createSubscription(UUID bundleId, PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
+ public Subscription createSubscription(final UUID bundleId, final PlanPhaseSpecifier spec, final DateTime requestedDate, final CallContext context)
throws EntitlementUserApiException;
- public List<SubscriptionStatusDryRun> getDryRunChangePlanStatus(UUID subscriptionId, @Nullable String productName, DateTime requestedDate)
+ public List<SubscriptionStatusDryRun> getDryRunChangePlanStatus(final UUID subscriptionId, @Nullable final String productName, final DateTime requestedDate)
throws EntitlementUserApiException;
- public DateTime getNextBillingDate(UUID account);
+ public DateTime getNextBillingDate(final UUID account);
}
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 1e61a99..4eaadff 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
@@ -34,6 +34,17 @@ import com.ning.billing.util.entity.Entity;
public interface Subscription extends Entity, Blockable {
+ public enum SubscriptionState {
+ ACTIVE,
+ CANCELLED
+ }
+
+ public enum SubscriptionSourceType {
+ NATIVE,
+ MIGRATED,
+ TRANSFERED
+ }
+
public boolean cancel(DateTime requestedDate, boolean eot, CallContext context)
throws EntitlementUserApiException;
@@ -50,15 +61,12 @@ public interface Subscription extends Entity, Blockable {
public boolean recreate(PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
throws EntitlementUserApiException;
- public enum SubscriptionState {
- ACTIVE,
- CANCELLED
- }
-
public UUID getBundleId();
public SubscriptionState getState();
+ public SubscriptionSourceType getSourceType();
+
public DateTime getStartDate();
public DateTime getEndDate();
@@ -84,4 +92,5 @@ public interface Subscription extends Entity, Blockable {
public List<EffectiveSubscriptionEvent> getBillingTransitions();
public List<EffectiveSubscriptionEvent> getAllTransitions();
+
}
diff --git a/api/src/main/java/com/ning/billing/glue/EntitlementModule.java b/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
index 421e68d..88d467c 100644
--- a/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
+++ b/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
@@ -22,6 +22,8 @@ public interface EntitlementModule {
public abstract void installEntitlementUserApi();
+ public abstract void installEntitlementTransferApi();
+
public abstract void installEntitlementMigrationApi();
public abstract void installChargeThruApi();
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java
index ae3ecc3..b349782 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2010-2011 Ning, Inc.
*
* Ning licenses this file to you under the Apache License, version 2.0
@@ -105,7 +105,7 @@ public class TestRepairIntegration extends TestIntegrationBase {
assertTrue(busHandler.isCompleted(DELAY));
- // MOVE CLOCK A LITTLE BIT MORE -- EITHER STAY IN TRIAL OR GET OUT
+ // MOVE CLOCK A LITTLE BIT MORE -- EITHER STAY IN TRIAL OR GET OUT
final int duration = inTrial ? 3 : 35;
it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(duration));
if (!inTrial) {
@@ -121,7 +121,7 @@ public class TestRepairIntegration extends TestIntegrationBase {
}
final boolean ifRepair = false;
if (ifRepair) {
- BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
// Quick check
@@ -194,6 +194,11 @@ public class TestRepairIntegration extends TestIntegrationBase {
public List<DeletedEvent> getDeletedEvents() {
return deletedEvents;
}
+
+ @Override
+ public long getActiveVersion() {
+ return 0;
+ }
};
}
@@ -252,6 +257,11 @@ public class TestRepairIntegration extends TestIntegrationBase {
public DateTime getEffectiveDate() {
return effectiveDateTime;
}
+
+ @Override
+ public String getPlanPhaseName() {
+ return null;
+ }
};
return ev;
}
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 3593fea..534160a 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
@@ -79,7 +79,7 @@ public class PlanAligner {
final String priceList,
final DateTime requestedDate,
final DateTime effectiveDate) throws CatalogApiException, EntitlementUserApiException {
- final List<TimedPhase> timedPhases = getTimedPhaseOnCreate(subscription.getStartDate(),
+ final List<TimedPhase> timedPhases = getTimedPhaseOnCreate(subscription.getAlignStartDate(),
subscription.getBundleStartDate(),
plan,
initialPhase,
@@ -154,7 +154,8 @@ public class PlanAligner {
case MIGRATE_ENTITLEMENT:
case CREATE:
case RE_CREATE:
- final List<TimedPhase> timedPhases = getTimedPhaseOnCreate(subscription.getStartDate(),
+ case TRANSFER:
+ final List<TimedPhase> timedPhases = getTimedPhaseOnCreate(subscription.getAlignStartDate(),
subscription.getBundleStartDate(),
lastPlanTransition.getNextPlan(),
lastPlanTransition.getNextPhase().getPhaseType(),
@@ -163,7 +164,7 @@ public class PlanAligner {
return getTimedPhase(timedPhases, effectiveDate, WhichPhase.NEXT);
// If we went through Plan changes, borrow the logic for changePlan alignment
case CHANGE:
- return getTimedPhaseOnChange(subscription.getStartDate(),
+ return getTimedPhaseOnChange(subscription.getAlignStartDate(),
subscription.getBundleStartDate(),
lastPlanTransition.getPreviousPhase(),
lastPlanTransition.getPreviousPlan(),
@@ -218,7 +219,7 @@ public class PlanAligner {
final DateTime requestedDate,
final DateTime effectiveDate,
final WhichPhase which) throws CatalogApiException, EntitlementUserApiException {
- return getTimedPhaseOnChange(subscription.getStartDate(),
+ return getTimedPhaseOnChange(subscription.getAlignStartDate(),
subscription.getBundleStartDate(),
subscription.getCurrentPhase(),
subscription.getCurrentPlan(),
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 fa79bda..21f7e7a 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
@@ -140,7 +140,7 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
.setBundleId(bundleId)
.setCategory(productCategory)
.setBundleStartDate(migrationStartDate)
- .setStartDate(migrationStartDate),
+ .setAlignStartDate(migrationStartDate),
emptyEvents);
return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, ctd, events, context));
}
@@ -156,7 +156,7 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
.setBundleId(bundleId)
.setCategory(productCategory)
.setBundleStartDate(bundleStartDate)
- .setStartDate(migrationStartDate),
+ .setAlignStartDate(migrationStartDate),
emptyEvents);
return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, ctd, events, context));
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultEntitlementTimelineApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultEntitlementTimelineApi.java
index 220c2ad..5e8cb20 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultEntitlementTimelineApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultEntitlementTimelineApi.java
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2010-2011 Ning, Inc.
*
* Ning licenses this file to you under the Apache License, version 2.0
@@ -42,7 +42,6 @@ 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.api.user.SubscriptionTransitionData;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
import com.ning.billing.entitlement.events.EntitlementEvent;
import com.ning.billing.entitlement.glue.DefaultEntitlementModule;
@@ -62,7 +61,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
@Inject
public DefaultEntitlementTimelineApi(@Named(DefaultEntitlementModule.REPAIR_NAMED) final SubscriptionFactory factory, final CatalogService catalogService,
- @Named(DefaultEntitlementModule.REPAIR_NAMED) final RepairEntitlementLifecycleDao repairDao, final EntitlementDao dao) {
+ @Named(DefaultEntitlementModule.REPAIR_NAMED) final RepairEntitlementLifecycleDao repairDao, final EntitlementDao dao) {
this.catalogService = catalogService;
this.dao = dao;
this.repairDao = repairDao;
@@ -70,24 +69,45 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
}
@Override
- public BundleTimeline getBundleRepair(final UUID bundleId) throws EntitlementRepairException {
+ public BundleTimeline getBundleTimeline(final SubscriptionBundle bundle)
+ throws EntitlementRepairException {
+ return getBundleTimelineInternal(bundle, bundle.getKey());
+ }
+
+
+ @Override
+ public BundleTimeline getBundleTimeline(final UUID accountId, final String bundleName)
+ throws EntitlementRepairException {
+ final SubscriptionBundle bundle = dao.getSubscriptionBundleFromAccountAndKey(accountId, bundleName);
+ return getBundleTimelineInternal(bundle, bundleName + " [accountId= " + accountId.toString() +"]");
+ }
+
+
+ @Override
+ public BundleTimeline getBundleTimeline(final UUID bundleId) throws EntitlementRepairException {
+
+ final SubscriptionBundle bundle = dao.getSubscriptionBundleFromId(bundleId);
+ return getBundleTimelineInternal(bundle, bundleId.toString());
+ }
+
+ private BundleTimeline getBundleTimelineInternal(final SubscriptionBundle bundle, String descBundle) throws EntitlementRepairException {
try {
- final SubscriptionBundle bundle = dao.getSubscriptionBundleFromId(bundleId);
if (bundle == null) {
- throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_UNKNOWN_BUNDLE, bundleId);
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_UNKNOWN_BUNDLE, descBundle);
}
- final List<Subscription> subscriptions = dao.getSubscriptions(factory, bundleId);
+ final List<Subscription> subscriptions = dao.getSubscriptions(factory, bundle.getId());
if (subscriptions.size() == 0) {
- throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NO_ACTIVE_SUBSCRIPTIONS, bundleId);
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NO_ACTIVE_SUBSCRIPTIONS, bundle.getId());
}
final String viewId = getViewId(((SubscriptionBundleData) bundle).getLastSysUpdateTime(), subscriptions);
final List<SubscriptionTimeline> repairs = createGetSubscriptionRepairList(subscriptions, Collections.<SubscriptionTimeline>emptyList());
- return createGetBundleRepair(bundleId, bundle.getKey(), viewId, repairs);
+ return createGetBundleRepair(bundle.getId(), bundle.getKey(), viewId, repairs);
} catch (CatalogApiException e) {
throw new EntitlementRepairException(e);
}
}
+
@Override
public BundleTimeline repairBundle(final BundleTimeline input, final boolean dryRun, final CallContext context) throws EntitlementRepairException {
try {
@@ -124,7 +144,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
final boolean isPlanRecreate = (curRepair.getNewEvents().size() > 0
&& (curRepair.getNewEvents().get(0).getSubscriptionTransitionType() == SubscriptionTransitionType.CREATE
- || curRepair.getNewEvents().get(0).getSubscriptionTransitionType() == SubscriptionTransitionType.RE_CREATE));
+ || curRepair.getNewEvents().get(0).getSubscriptionTransitionType() == SubscriptionTransitionType.RE_CREATE));
final DateTime newSubscriptionStartDate = isPlanRecreate ? curRepair.getNewEvents().get(0).getRequestedDate() : null;
@@ -156,7 +176,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
inRepair.add(curOutputRepair);
if (curOutputRepair.getCategory() == ProductCategory.ADD_ON) {
// Check if ADD_ON RE_CREATE is before BP start
- if (isPlanRecreate && subscriptions.get(0).getStartDate().isAfter(curRepair.getNewEvents().get(0).getRequestedDate())) {
+ if (isPlanRecreate && (subscriptions.get(0)).getStartDate().isAfter(curRepair.getNewEvents().get(0).getRequestedDate())) {
throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_AO_CREATE_BEFORE_BP_START, cur.getId(), cur.getBundleId());
}
addOnSubscriptionInRepair.add(curOutputRepair);
@@ -168,25 +188,25 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
final RepairType repairType = getRepairType(subscriptions.get(0), (baseSubscriptionRepair != null));
switch (repairType) {
- case BASE_REPAIR:
- // We need to add any existing addon that are not in the input repair list
- for (final Subscription cur : subscriptions) {
- if (cur.getCategory() == ProductCategory.ADD_ON && !inRepair.contains(cur)) {
- final SubscriptionDataRepair curOutputRepair = createSubscriptionDataRepair((SubscriptionDataRepair) cur, newBundleStartDate, null, ((SubscriptionDataRepair) cur).getEvents());
- repairDao.initializeRepair(curOutputRepair.getId(), ((SubscriptionDataRepair) cur).getEvents());
- inRepair.add(curOutputRepair);
- addOnSubscriptionInRepair.add(curOutputRepair);
- }
+ case BASE_REPAIR:
+ // We need to add any existing addon that are not in the input repair list
+ for (final Subscription cur : subscriptions) {
+ if (cur.getCategory() == ProductCategory.ADD_ON && !inRepair.contains(cur)) {
+ final SubscriptionDataRepair curOutputRepair = createSubscriptionDataRepair((SubscriptionDataRepair) cur, newBundleStartDate, null, ((SubscriptionDataRepair) cur).getEvents());
+ repairDao.initializeRepair(curOutputRepair.getId(), ((SubscriptionDataRepair) cur).getEvents());
+ inRepair.add(curOutputRepair);
+ addOnSubscriptionInRepair.add(curOutputRepair);
}
- break;
- case ADD_ON_REPAIR:
- // We need to set the baseSubscription as it is useful to calculate addon validity
- final SubscriptionDataRepair baseSubscription = (SubscriptionDataRepair) subscriptions.get(0);
- baseSubscriptionRepair = createSubscriptionDataRepair(baseSubscription, baseSubscription.getBundleStartDate(), baseSubscription.getStartDate(), baseSubscription.getEvents());
- break;
- case STANDALONE_REPAIR:
- default:
- break;
+ }
+ break;
+ case ADD_ON_REPAIR:
+ // We need to set the baseSubscription as it is useful to calculate addon validity
+ final SubscriptionDataRepair baseSubscription = (SubscriptionDataRepair) subscriptions.get(0);
+ baseSubscriptionRepair = createSubscriptionDataRepair(baseSubscription, baseSubscription.getBundleStartDate(), baseSubscription.getAlignStartDate(), baseSubscription.getEvents());
+ break;
+ case STANDALONE_REPAIR:
+ default:
+ break;
}
validateBasePlanRecreate(isBasePlanRecreate, subscriptions, input.getSubscriptions());
@@ -209,7 +229,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
return createGetBundleRepair(input.getBundleId(), bundle.getKey(), input.getViewId(), repairs);
} else {
dao.repair(bundle.getAccountId(), input.getBundleId(), inRepair, context);
- return getBundleRepair(input.getBundleId());
+ return getBundleTimeline(input.getBundleId());
}
} catch (CatalogApiException e) {
throw new EntitlementRepairException(e);
@@ -227,7 +247,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
}
private void validateBasePlanRecreate(final boolean isBasePlanRecreate, final List<Subscription> subscriptions, final List<SubscriptionTimeline> input)
- throws EntitlementRepairException {
+ throws EntitlementRepairException {
if (!isBasePlanRecreate) {
return;
}
@@ -237,14 +257,14 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
for (final SubscriptionTimeline cur : input) {
if (cur.getNewEvents().size() != 0
&& (cur.getNewEvents().get(0).getSubscriptionTransitionType() != SubscriptionTransitionType.CREATE
- && cur.getNewEvents().get(0).getSubscriptionTransitionType() != SubscriptionTransitionType.RE_CREATE)) {
+ && cur.getNewEvents().get(0).getSubscriptionTransitionType() != SubscriptionTransitionType.RE_CREATE)) {
throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_BP_RECREATE_MISSING_AO_CREATE, subscriptions.get(0).getBundleId());
}
}
}
private void validateInputSubscriptionsKnown(final List<Subscription> subscriptions, final List<SubscriptionTimeline> input)
- throws EntitlementRepairException {
+ throws EntitlementRepairException {
for (final SubscriptionTimeline cur : input) {
boolean found = false;
for (final Subscription s : subscriptions) {
@@ -260,7 +280,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
}
private void validateFirstNewEvent(final SubscriptionData data, final NewEvent firstNewEvent, final DateTime lastBPRemainingTime, final DateTime lastRemainingTime)
- throws EntitlementRepairException {
+ throws EntitlementRepairException {
if (lastBPRemainingTime != null &&
firstNewEvent.getRequestedDate().isBefore(lastBPRemainingTime)) {
throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NEW_EVENT_BEFORE_LAST_BP_REMAINING, firstNewEvent.getSubscriptionTransitionType(), data.getId());
@@ -289,7 +309,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
}
private List<EntitlementEvent> getRemainingEventsAndValidateDeletedEvents(final SubscriptionDataRepair data, final DateTime firstBPDeletedTime,
- final List<SubscriptionTimeline.DeletedEvent> deletedEvents)
+ final List<SubscriptionTimeline.DeletedEvent> deletedEvents)
throws EntitlementRepairException {
if (deletedEvents == null || deletedEvents.size() == 0) {
return data.getEvents();
@@ -420,7 +440,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
builder.setBundleStartDate(newBundleStartDate);
}
if (newSubscriptionStartDate != null) {
- builder.setStartDate(newSubscriptionStartDate);
+ builder.setAlignStartDate(newSubscriptionStartDate);
}
if (initialEvents.size() > 0) {
for (final EntitlementEvent cur : initialEvents) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultSubscriptionTimeline.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultSubscriptionTimeline.java
index f9a17f3..11d2a01 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultSubscriptionTimeline.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultSubscriptionTimeline.java
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2010-2011 Ning, Inc.
*
* Ning licenses this file to you under the Apache License, version 2.0
@@ -44,9 +44,11 @@ public class DefaultSubscriptionTimeline implements SubscriptionTimeline {
private final List<ExistingEvent> existingEvents;
private final List<NewEvent> newEvents;
private final List<DeletedEvent> deletedEvents;
+ private final long activeVersion;
- public DefaultSubscriptionTimeline(final UUID id) {
+ public DefaultSubscriptionTimeline(final UUID id, final long activeVersion) {
this.id = id;
+ this.activeVersion = activeVersion;
this.existingEvents = Collections.<SubscriptionTimeline.ExistingEvent>emptyList();
this.deletedEvents = Collections.<SubscriptionTimeline.DeletedEvent>emptyList();
this.newEvents = Collections.<SubscriptionTimeline.NewEvent>emptyList();
@@ -54,6 +56,7 @@ public class DefaultSubscriptionTimeline implements SubscriptionTimeline {
public DefaultSubscriptionTimeline(final SubscriptionTimeline input) {
this.id = input.getId();
+ this.activeVersion = input.getActiveVersion();
this.existingEvents = (input.getExistingEvents() != null) ? new ArrayList<SubscriptionTimeline.ExistingEvent>(input.getExistingEvents()) :
Collections.<SubscriptionTimeline.ExistingEvent>emptyList();
sortExistingEvent(this.existingEvents);
@@ -70,6 +73,7 @@ public class DefaultSubscriptionTimeline implements SubscriptionTimeline {
this.existingEvents = toExistingEvents(catalog, input.getActiveVersion(), input.getCategory(), input.getEvents());
this.deletedEvents = null;
this.newEvents = null;
+ this.activeVersion = input.getActiveVersion();
}
private List<ExistingEvent> toExistingEvents(final Catalog catalog, final long activeVersion, final ProductCategory category, final List<EntitlementEvent> events)
@@ -100,11 +104,13 @@ public class DefaultSubscriptionTimeline implements SubscriptionTimeline {
BillingPeriod billingPeriod = null;
String priceListName = null;
PhaseType phaseType = null;
+ String planPhaseName = null;
ApiEventType apiType = null;
switch (cur.getType()) {
case PHASE:
final PhaseEvent phaseEV = (PhaseEvent) cur;
+ planPhaseName = phaseEV.getPhase();
phaseType = catalog.findPhase(phaseEV.getPhase(), cur.getEffectiveDate(), startDate).getPhaseType();
productName = prevProductName;
billingPeriod = catalog.findPhase(phaseEV.getPhase(), cur.getEffectiveDate(), startDate).getBillingPeriod();
@@ -114,6 +120,7 @@ public class DefaultSubscriptionTimeline implements SubscriptionTimeline {
case API_USER:
final ApiEvent userEV = (ApiEvent) cur;
apiType = userEV.getEventType();
+ planPhaseName = userEV.getEventPlanPhase();
final Plan plan = (userEV.getEventPlan() != null) ? catalog.findPlan(userEV.getEventPlan(), cur.getRequestedDate(), startDate) : null;
phaseType = (userEV.getEventPlanPhase() != null) ? catalog.findPhase(userEV.getEventPlanPhase(), cur.getEffectiveDate(), startDate).getPhaseType() : prevPhaseType;
productName = (plan != null) ? plan.getProduct().getName() : prevProductName;
@@ -124,6 +131,7 @@ public class DefaultSubscriptionTimeline implements SubscriptionTimeline {
final SubscriptionTransitionType transitionType = SubscriptionTransitionData.toSubscriptionTransitionType(cur.getType(), apiType);
+ final String planPhaseNameWithClosure = planPhaseName;
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, category, billingPeriod, priceListName, phaseType);
result.add(new ExistingEvent() {
@Override
@@ -150,6 +158,11 @@ public class DefaultSubscriptionTimeline implements SubscriptionTimeline {
public DateTime getEffectiveDate() {
return cur.getEffectiveDate();
}
+
+ @Override
+ public String getPlanPhaseName() {
+ return planPhaseNameWithClosure;
+ }
});
prevProductName = productName;
@@ -265,6 +278,12 @@ public class DefaultSubscriptionTimeline implements SubscriptionTimeline {
return existingEvents;
}
+ @Override
+ public long getActiveVersion() {
+ return activeVersion;
+ }
+
+
private void sortExistingEvent(final List<ExistingEvent> events) {
if (events != null) {
Collections.sort(events, new Comparator<ExistingEvent>() {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java
new file mode 100644
index 0000000..6e3a0a9
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java
@@ -0,0 +1,282 @@
+/*
+ * 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.transfer;
+
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.google.common.collect.ImmutableList;
+import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.Catalog;
+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.PlanPhase;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
+import com.ning.billing.entitlement.api.migration.AccountMigrationData.SubscriptionMigrationData;
+import com.ning.billing.entitlement.api.timeline.BundleTimeline;
+import com.ning.billing.entitlement.api.timeline.EntitlementRepairException;
+import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
+import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline;
+import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline.ExistingEvent;
+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.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.engine.dao.EntitlementDao;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.entitlement.events.phase.PhaseEventData;
+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.ApiEventCreate;
+import com.ning.billing.entitlement.events.user.ApiEventTransfer;
+
+import com.ning.billing.entitlement.exceptions.EntitlementError;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.clock.Clock;
+
+public class DefaultEntitlementTransferApi implements EntitlementTransferApi {
+
+
+ private final Clock clock;
+ private final EntitlementDao dao;
+ private final CatalogService catalogService;
+ private final SubscriptionFactory subscriptionFactory;
+ private final EntitlementTimelineApi timelineApi;
+
+ @Inject
+ public DefaultEntitlementTransferApi(final Clock clock, final EntitlementDao dao, final EntitlementTimelineApi timelineApi, final CatalogService catalogService,
+ final SubscriptionFactory subscriptionFactory) {
+ this.clock = clock;
+ this.dao = dao;
+ this.catalogService = catalogService;
+ this.subscriptionFactory = subscriptionFactory;
+ this.timelineApi = timelineApi;
+ }
+
+
+ private EntitlementEvent createEvent(final boolean firstEvent, final ExistingEvent existingEvent, final SubscriptionData subscription, final DateTime transferDate, final CallContext context)
+ throws CatalogApiException {
+
+ EntitlementEvent newEvent = null;
+
+ final Catalog catalog = catalogService.getFullCatalog();
+
+
+ final DateTime effectiveDate = existingEvent.getEffectiveDate().isBefore(transferDate) ? transferDate : existingEvent.getEffectiveDate();
+
+ // STEPH Issue with catalog for NO_BILLING_PERIOD
+ /*
+ final PlanPhaseSpecifier spec = existingEvent.getPlanPhaseSpecifier();
+ final Plan currentPlan = catalog.findPlan(spec.getProductName(), spec.getBillingPeriod(), spec.getPriceListName(), existingEvent.getEffectiveDate());
+ PlanPhase currentPhase = null;
+ for (PlanPhase curPhase : currentPlan.getAllPhases()) {
+ if (curPhase.getPhaseType() == spec.getPhaseType()) {
+ currentPhase = curPhase;
+ break;
+ }
+ }
+
+ *
+ */
+
+ final PlanPhaseSpecifier spec = existingEvent.getPlanPhaseSpecifier();
+ final PlanPhase currentPhase = catalog.findPhase(existingEvent.getPlanPhaseName(), effectiveDate, subscription.getAlignStartDate());
+
+
+ final ApiEventBuilder apiBuilder = new ApiEventBuilder()
+ .setSubscriptionId(subscription.getId())
+ .setEventPlan(currentPhase.getPlan().getName())
+ .setEventPlanPhase(currentPhase.getName())
+ .setEventPriceList(spec.getPriceListName())
+ .setActiveVersion(subscription.getActiveVersion())
+ .setProcessedDate(clock.getUTCNow())
+ .setEffectiveDate(effectiveDate)
+ .setRequestedDate(effectiveDate)
+ .setUserToken(context.getUserToken())
+ .setFromDisk(true);
+
+ switch(existingEvent.getSubscriptionTransitionType()) {
+ case MIGRATE_ENTITLEMENT:
+ case RE_CREATE:
+ case CREATE:
+ newEvent = new ApiEventTransfer(apiBuilder);
+ break;
+
+ // Should we even keep future change events; product question really
+ case CHANGE:
+ newEvent = firstEvent ? new ApiEventTransfer(apiBuilder) : new ApiEventChange(apiBuilder);
+ break;
+
+ case PHASE:
+ newEvent = firstEvent ? new ApiEventTransfer(apiBuilder) :
+ PhaseEventData.createNextPhaseEvent(currentPhase.getName(), subscription, clock.getUTCNow(), effectiveDate);
+ break;
+
+ // Ignore
+ case CANCEL:
+ case UNCANCEL:
+ case MIGRATE_BILLING:
+ break;
+ default:
+ throw new EntitlementError(String.format("Unepxected transitionType %s", existingEvent.getSubscriptionTransitionType()));
+ }
+ return newEvent;
+ }
+
+
+ private List<EntitlementEvent> toEvents(final List<ExistingEvent> existingEvents, final SubscriptionData subscription,
+ final DateTime transferDate, final CallContext context) throws EntitlementTransferApiException {
+
+ try {
+ final List<EntitlementEvent> result = new LinkedList<EntitlementEvent>();
+
+ EntitlementEvent event = null;
+ ExistingEvent prevEvent = null;
+ boolean firstEvent = true;
+ for (ExistingEvent cur : existingEvents) {
+ // Skip all events prior to the transferDate
+ if (cur.getEffectiveDate().isBefore(transferDate)) {
+ prevEvent = cur;
+ continue;
+ }
+
+ // Add previous event the first time if needed
+ if (prevEvent != null) {
+ event = createEvent(firstEvent, prevEvent, subscription, transferDate, context);
+ if (event != null) {
+ result.add(event);
+ firstEvent = false;
+ }
+ prevEvent = null;
+ }
+
+ event = createEvent(firstEvent, cur, subscription, transferDate, context);
+ if (event != null) {
+ result.add(event);
+ firstEvent = false;
+ }
+ }
+
+ // Previous loop did not get anything because transferDate is greater than effectiveDate of last event
+ if (prevEvent != null) {
+ event = createEvent(firstEvent, prevEvent, subscription, transferDate, context);
+ if (event != null) {
+ result.add(event);
+ }
+ prevEvent = null;
+ }
+
+
+ return result;
+ } catch (CatalogApiException e) {
+ throw new EntitlementTransferApiException(e);
+ }
+ }
+
+
+ @Override
+ public void transferBundle(final UUID sourceAccountId, final UUID destAccountId,
+ final String bundleKey, final DateTime transferDate, final boolean transferAddOn,
+ final CallContext context) throws EntitlementTransferApiException {
+
+ try {
+
+ final SubscriptionBundle bundle = dao.getSubscriptionBundleFromAccountAndKey(sourceAccountId, bundleKey);
+ if (bundle == null) {
+ throw new EntitlementTransferApiException(ErrorCode.ENT_CREATE_NO_BUNDLE, bundleKey);
+ }
+
+ // Get the bundle timeline for the old account
+ final BundleTimeline bundleTimeline = timelineApi.getBundleTimeline(bundle);
+
+ final SubscriptionBundleData subscriptionBundleData = new SubscriptionBundleData(bundleKey, destAccountId, transferDate);
+ final List<SubscriptionMigrationData> subscriptionMigrationDataList = new LinkedList<SubscriptionMigrationData>();
+
+ final List<TransferCancelData> transferCancelDataList = new LinkedList<TransferCancelData>();
+
+ DateTime bundleStartdate = null;
+
+
+ for (SubscriptionTimeline cur : bundleTimeline.getSubscriptions()) {
+
+ final SubscriptionData oldSubscription = (SubscriptionData) dao.getSubscriptionFromId(subscriptionFactory, cur.getId());
+ final List<ExistingEvent> existingEvents = cur.getExistingEvents();
+ final ProductCategory productCategory = existingEvents.get(0).getPlanPhaseSpecifier().getProductCategory();
+ if (productCategory == ProductCategory.ADD_ON) {
+ if (!transferAddOn) {
+ continue;
+ }
+ } else {
+
+
+
+ // If BP or STANDALONE subscription, create the cancel event on effectiveCancelDate
+ final DateTime effectiveCancelDate = oldSubscription.getChargedThroughDate() != null && transferDate.isBefore(oldSubscription.getChargedThroughDate()) ?
+ oldSubscription.getChargedThroughDate() : transferDate;
+
+ final EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
+ .setSubscriptionId(cur.getId())
+ .setActiveVersion(cur.getActiveVersion())
+ .setProcessedDate(clock.getUTCNow())
+ .setEffectiveDate(effectiveCancelDate)
+ .setRequestedDate(transferDate)
+ .setUserToken(context.getUserToken())
+ .setFromDisk(true));
+
+ TransferCancelData cancelData = new TransferCancelData(oldSubscription, cancelEvent);
+ transferCancelDataList.add(cancelData);
+ }
+
+ // We Align with the original subscription
+ final DateTime subscriptionAlignStartDate = oldSubscription.getAlignStartDate();
+ if (bundleStartdate == null) {
+ bundleStartdate = oldSubscription.getStartDate();
+ }
+
+ // Create the new subscription for the new bundle on the new account
+ final SubscriptionData subscriptionData = subscriptionFactory.createSubscription(new SubscriptionBuilder()
+ .setId(UUID.randomUUID())
+ .setBundleId(subscriptionBundleData.getId())
+ .setCategory(productCategory)
+ .setBundleStartDate(transferDate)
+ .setAlignStartDate(subscriptionAlignStartDate),
+ ImmutableList.<EntitlementEvent>of());
+
+ final List<EntitlementEvent> events = toEvents(existingEvents, subscriptionData, transferDate, context);
+ final SubscriptionMigrationData curData = new SubscriptionMigrationData(subscriptionData, events);
+ subscriptionMigrationDataList.add(curData);
+ }
+ BundleMigrationData bundleMigrationData = new BundleMigrationData(subscriptionBundleData, subscriptionMigrationDataList);
+
+ // Atomically cancel all subscription on old account and create new bundle, subscriptions, events for new account
+ dao.transfer(sourceAccountId, destAccountId, bundleMigrationData, transferCancelDataList, context);
+
+ } catch (EntitlementRepairException e) {
+ throw new EntitlementTransferApiException(e);
+ }
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/TransferCancelData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/TransferCancelData.java
new file mode 100644
index 0000000..86f05ae
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/TransferCancelData.java
@@ -0,0 +1,39 @@
+/*
+ * 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.transfer;
+
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+
+public class TransferCancelData {
+
+ final SubscriptionData subscription;
+ final EntitlementEvent cancelEvent;
+
+ public TransferCancelData(final SubscriptionData subscription,
+ final EntitlementEvent cancelEvent) {
+ this.subscription = subscription;
+ this.cancelEvent = cancelEvent;
+ }
+
+ public SubscriptionData getSubscription() {
+ return subscription;
+ }
+
+ public EntitlementEvent getCancelEvent() {
+ return cancelEvent;
+ }
+}
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 f63cf6e..11f6bef 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
@@ -56,7 +56,6 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
@Inject
public DefaultEntitlementUserApi(final Clock clock, final EntitlementDao dao, final CatalogService catalogService,
final DefaultSubscriptionApiService apiService, final SubscriptionFactory subscriptionFactory, final AddonUtils addonUtils) {
- super();
this.clock = clock;
this.apiService = apiService;
this.dao = dao;
@@ -84,8 +83,8 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
}
@Override
- public SubscriptionBundle getBundleForKey(final String bundleKey) throws EntitlementUserApiException {
- final SubscriptionBundle result = dao.getSubscriptionBundleFromKey(bundleKey);
+ public SubscriptionBundle getBundleForAccountAndKey(final UUID accountId, final String bundleKey) throws EntitlementUserApiException {
+ final SubscriptionBundle result = dao.getSubscriptionBundleFromAccountAndKey(accountId, bundleKey);
if (result == null) {
throw new EntitlementUserApiException(ErrorCode.ENT_GET_INVALID_BUNDLE_KEY, bundleKey);
}
@@ -93,13 +92,19 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
}
@Override
+ public List<SubscriptionBundle> getBundlesForKey(final String bundleKey)
+ throws EntitlementUserApiException {
+ return dao.getSubscriptionBundlesForKey(bundleKey);
+ }
+
+ @Override
public List<SubscriptionBundle> getBundlesForAccount(final UUID accountId) {
return dao.getSubscriptionBundleForAccount(accountId);
}
@Override
- public List<Subscription> getSubscriptionsForKey(final String bundleKey) {
- return dao.getSubscriptionsForKey(subscriptionFactory, bundleKey);
+ public List<Subscription> getSubscriptionsForAccountAndKey(final UUID accountId, final String bundleKey) {
+ return dao.getSubscriptionsForAccountAndKey(subscriptionFactory, accountId, bundleKey);
}
@Override
@@ -116,6 +121,7 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
return result;
}
+ @Override
public SubscriptionBundle createBundleForAccount(final UUID accountId, final String bundleName, final CallContext context)
throws EntitlementUserApiException {
final SubscriptionBundleData bundle = new SubscriptionBundleData(bundleName, accountId, clock.getUTCNow());
@@ -190,7 +196,7 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
.setBundleId(bundleId)
.setCategory(plan.getProduct().getCategory())
.setBundleStartDate(bundleStartDate)
- .setStartDate(effectiveDate),
+ .setAlignStartDate(effectiveDate),
plan, spec.getPhaseType(), realPriceList, requestedDate, effectiveDate, now, context);
} catch (CatalogApiException e) {
throw new EntitlementUserApiException(e);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionApiService.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionApiService.java
index da61996..ac3e704 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionApiService.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionApiService.java
@@ -121,17 +121,17 @@ public class DefaultSubscriptionApiService implements SubscriptionApiService {
final TimedPhase[] curAndNextPhases = planAligner.getCurrentAndNextTimedPhaseOnCreate(subscription, plan, initialPhase, realPriceList, requestedDate, effectiveDate);
final ApiEventBuilder createBuilder = new ApiEventBuilder()
- .setSubscriptionId(subscription.getId())
- .setEventPlan(plan.getName())
- .setEventPlanPhase(curAndNextPhases[0].getPhase().getName())
- .setEventPriceList(realPriceList)
- .setActiveVersion(subscription.getActiveVersion())
- .setProcessedDate(processedDate)
- .setEffectiveDate(effectiveDate)
- .setRequestedDate(requestedDate)
- .setUserToken(context.getUserToken())
- .setFromDisk(true);
- final ApiEvent creationEvent = (reCreate) ? new ApiEventReCreate(createBuilder) : new ApiEventCreate(createBuilder);
+ .setSubscriptionId(subscription.getId())
+ .setEventPlan(plan.getName())
+ .setEventPlanPhase(curAndNextPhases[0].getPhase().getName())
+ .setEventPriceList(realPriceList)
+ .setActiveVersion(subscription.getActiveVersion())
+ .setProcessedDate(processedDate)
+ .setEffectiveDate(effectiveDate)
+ .setRequestedDate(requestedDate)
+ .setUserToken(context.getUserToken())
+ .setFromDisk(true);
+ final ApiEvent creationEvent = (reCreate) ? new ApiEventReCreate(createBuilder) : new ApiEventCreate(createBuilder);
final TimedPhase nextTimedPhase = curAndNextPhases[1];
final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
@@ -176,13 +176,13 @@ public class DefaultSubscriptionApiService implements SubscriptionApiService {
final DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy, requestedDate);
final EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
- .setSubscriptionId(subscription.getId())
- .setActiveVersion(subscription.getActiveVersion())
- .setProcessedDate(now)
- .setEffectiveDate(effectiveDate)
- .setRequestedDate(requestedDate)
- .setUserToken(context.getUserToken())
- .setFromDisk(true));
+ .setSubscriptionId(subscription.getId())
+ .setActiveVersion(subscription.getActiveVersion())
+ .setProcessedDate(now)
+ .setEffectiveDate(effectiveDate)
+ .setRequestedDate(requestedDate)
+ .setUserToken(context.getUserToken())
+ .setFromDisk(true));
dao.cancelSubscription(subscription, cancelEvent, context, 0);
subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
@@ -300,16 +300,16 @@ public class DefaultSubscriptionApiService implements SubscriptionApiService {
final TimedPhase currentTimedPhase = planAligner.getCurrentTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), requestedDate, effectiveDate);
final EntitlementEvent changeEvent = new ApiEventChange(new ApiEventBuilder()
- .setSubscriptionId(subscription.getId())
- .setEventPlan(newPlan.getName())
- .setEventPlanPhase(currentTimedPhase.getPhase().getName())
- .setEventPriceList(newPriceList.getName())
- .setActiveVersion(subscription.getActiveVersion())
- .setProcessedDate(now)
- .setEffectiveDate(effectiveDate)
- .setRequestedDate(requestedDate)
- .setUserToken(context.getUserToken())
- .setFromDisk(true));
+ .setSubscriptionId(subscription.getId())
+ .setEventPlan(newPlan.getName())
+ .setEventPlanPhase(currentTimedPhase.getPhase().getName())
+ .setEventPriceList(newPriceList.getName())
+ .setActiveVersion(subscription.getActiveVersion())
+ .setProcessedDate(now)
+ .setEffectiveDate(effectiveDate)
+ .setRequestedDate(requestedDate)
+ .setUserToken(context.getUserToken())
+ .setFromDisk(true));
final TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), requestedDate, effectiveDate);
final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionFactory.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionFactory.java
index 8bd7471..1e1ebeb 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionFactory.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionFactory.java
@@ -43,6 +43,7 @@ public class DefaultSubscriptionFactory implements SubscriptionFactory {
this.catalogService = catalogService;
}
+ @Override
public SubscriptionData createSubscription(final SubscriptionBuilder builder, final List<EntitlementEvent> events) {
final SubscriptionData subscription = new SubscriptionData(builder, apiService, clock);
if (events.size() > 0) {
@@ -54,7 +55,7 @@ public class DefaultSubscriptionFactory implements SubscriptionFactory {
public static class SubscriptionBuilder {
private UUID id;
private UUID bundleId;
- private DateTime startDate;
+ private DateTime alignStartDate;
private DateTime bundleStartDate;
private Long activeVersion;
private ProductCategory category;
@@ -68,7 +69,7 @@ public class DefaultSubscriptionFactory implements SubscriptionFactory {
public SubscriptionBuilder(final SubscriptionData original) {
this.id = original.getId();
this.bundleId = original.getBundleId();
- this.startDate = original.getStartDate();
+ this.alignStartDate = original.getAlignStartDate();
this.bundleStartDate = original.getBundleStartDate();
this.category = original.getCategory();
this.activeVersion = original.getActiveVersion();
@@ -86,8 +87,8 @@ public class DefaultSubscriptionFactory implements SubscriptionFactory {
return this;
}
- public SubscriptionBuilder setStartDate(final DateTime startDate) {
- this.startDate = startDate;
+ public SubscriptionBuilder setAlignStartDate(final DateTime alignStartDate) {
+ this.alignStartDate = alignStartDate;
return this;
}
@@ -124,8 +125,8 @@ public class DefaultSubscriptionFactory implements SubscriptionFactory {
return bundleId;
}
- public DateTime getStartDate() {
- return startDate;
+ public DateTime getAlignStartDate() {
+ return alignStartDate;
}
public DateTime getBundleStartDate() {
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 d7b8a78..fe7d666 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
@@ -63,7 +63,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
// Final subscription fields
//
private final UUID bundleId;
- private final DateTime startDate;
+ private final DateTime alignStartDate;
private final DateTime bundleStartDate;
private final ProductCategory category;
@@ -88,12 +88,12 @@ public class SubscriptionData extends EntityBase implements Subscription {
}
public SubscriptionData(final SubscriptionBuilder builder,
- @Nullable final SubscriptionApiService apiService, @Nullable final Clock clock) {
+ @Nullable final SubscriptionApiService apiService, @Nullable final Clock clock) {
super(builder.getId());
this.apiService = apiService;
this.clock = clock;
this.bundleId = builder.getBundleId();
- this.startDate = builder.getStartDate();
+ this.alignStartDate = builder.getAlignStartDate();
this.bundleStartDate = builder.getBundleStartDate();
this.category = builder.getCategory();
this.activeVersion = builder.getActiveVersion();
@@ -108,7 +108,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
@Override
public DateTime getStartDate() {
- return startDate;
+ return transitions.get(0).getEffectiveTransitionTime();
}
@Override
@@ -118,6 +118,23 @@ public class SubscriptionData extends EntityBase implements Subscription {
}
@Override
+ public SubscriptionSourceType getSourceType() {
+ if (transitions == null) {
+ return null;
+ }
+ final SubscriptionTransitionData initialTransition = transitions.get(0);
+ switch (initialTransition.getApiEventType()) {
+ case MIGRATE_BILLING:
+ case MIGRATE_ENTITLEMENT:
+ return SubscriptionSourceType.MIGRATED;
+ case TRANSFER:
+ return SubscriptionSourceType.TRANSFERED;
+ default:
+ return SubscriptionSourceType.NATIVE;
+ }
+ }
+
+ @Override
public PlanPhase getCurrentPhase() {
return (getPreviousTransitionData() == null) ? null
: getPreviousTransitionData().getNextPhase();
@@ -132,7 +149,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
@Override
public PriceList getCurrentPriceList() {
return (getPreviousTransitionData() == null) ? null :
- getPreviousTransitionData().getNextPriceList();
+ getPreviousTransitionData().getNextPriceList();
}
@@ -165,31 +182,31 @@ public class SubscriptionData extends EntityBase implements Subscription {
@Override
public boolean cancel(final DateTime requestedDate, final boolean eot,
- final CallContext context) throws EntitlementUserApiException {
+ final CallContext context) throws EntitlementUserApiException {
return apiService.cancel(this, requestedDate, eot, context);
}
@Override
public boolean uncancel(final CallContext context)
- throws EntitlementUserApiException {
+ throws EntitlementUserApiException {
return apiService.uncancel(this, context);
}
@Override
public boolean changePlan(final String productName, final BillingPeriod term, final String priceList,
- final DateTime requestedDate, final CallContext context) throws EntitlementUserApiException {
+ final DateTime requestedDate, final CallContext context) throws EntitlementUserApiException {
return apiService.changePlan(this, productName, term, priceList, requestedDate, context);
}
@Override
public boolean changePlanWithPolicy(final String productName, final BillingPeriod term, final String priceList,
- final DateTime requestedDate, final ActionPolicy policy, final CallContext context) throws EntitlementUserApiException {
+ final DateTime requestedDate, final ActionPolicy policy, final CallContext context) throws EntitlementUserApiException {
return apiService.changePlanWithPolicy(this, productName, term, priceList, requestedDate, policy, context);
}
@Override
public boolean recreate(final PlanPhaseSpecifier spec, final DateTime requestedDate,
- final CallContext context) throws EntitlementUserApiException {
+ final CallContext context) throws EntitlementUserApiException {
return apiService.recreatePlan(this, spec, requestedDate, context);
}
@@ -199,7 +216,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
if (data == null) {
return null;
}
- return new DefaultEffectiveSubscriptionEvent(data, startDate);
+ return new DefaultEffectiveSubscriptionEvent(data, alignStartDate);
}
@Override
@@ -223,7 +240,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
if (data == null) {
return null;
}
- return new DefaultEffectiveSubscriptionEvent(data, startDate);
+ return new DefaultEffectiveSubscriptionEvent(data, alignStartDate);
}
protected SubscriptionTransitionData getPreviousTransitionData() {
@@ -260,7 +277,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
final int prime = 31;
int result = 1;
result = prime * result
- + ((id == null) ? 0 : id.hashCode());
+ + ((id == null) ? 0 : id.hashCode());
return result;
}
@@ -297,7 +314,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
clock, transitions, Order.ASC_FROM_PAST, Kind.BILLING,
Visibility.ALL, TimeLimit.ALL);
while (it.hasNext()) {
- result.add(new DefaultEffectiveSubscriptionEvent(it.next(), startDate));
+ result.add(new DefaultEffectiveSubscriptionEvent(it.next(), alignStartDate));
}
return result;
}
@@ -309,12 +326,17 @@ public class SubscriptionData extends EntityBase implements Subscription {
for (final SubscriptionTransitionData cur : transitions) {
if (cur.getId().equals(event.getId())) {
final SubscriptionTransitionData withSeq = new SubscriptionTransitionData(cur, seqId);
- return new DefaultEffectiveSubscriptionEvent(withSeq, startDate);
+ return new DefaultEffectiveSubscriptionEvent(withSeq, alignStartDate);
}
}
return null;
}
+ public DateTime getAlignStartDate() {
+ return alignStartDate;
+ }
+
+
public long getLastEventOrderedId() {
final SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(
clock, transitions, Order.DESC_FROM_FUTURE, Kind.ENTITLEMENT,
@@ -335,7 +357,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
final List<EffectiveSubscriptionEvent> result = new ArrayList<EffectiveSubscriptionEvent>();
final SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(clock, transitions, Order.ASC_FROM_PAST, Kind.ALL, Visibility.ALL, TimeLimit.ALL);
while (it.hasNext()) {
- result.add(new DefaultEffectiveSubscriptionEvent(it.next(), startDate));
+ result.add(new DefaultEffectiveSubscriptionEvent(it.next(), alignStartDate));
}
return result;
@@ -347,16 +369,17 @@ public class SubscriptionData extends EntityBase implements Subscription {
}
final SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(clock,
- transitions,
- Order.DESC_FROM_FUTURE,
- Kind.ENTITLEMENT,
- Visibility.ALL,
- TimeLimit.PAST_OR_PRESENT_ONLY);
+ transitions,
+ Order.DESC_FROM_FUTURE,
+ Kind.ENTITLEMENT,
+ Visibility.ALL,
+ TimeLimit.PAST_OR_PRESENT_ONLY);
while (it.hasNext()) {
final SubscriptionTransitionData cur = it.next();
if (cur.getTransitionType() == SubscriptionTransitionType.CREATE
|| cur.getTransitionType() == SubscriptionTransitionType.RE_CREATE
+ || cur.getTransitionType() == SubscriptionTransitionType.TRANSFER
|| cur.getTransitionType() == SubscriptionTransitionType.CHANGE
|| cur.getTransitionType() == SubscriptionTransitionType.MIGRATE_ENTITLEMENT) {
return cur;
@@ -372,7 +395,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
}
public DateTime getPlanChangeEffectiveDate(final ActionPolicy policy,
- final DateTime requestedDate) {
+ final DateTime requestedDate) {
if (policy == ActionPolicy.IMMEDIATE) {
return requestedDate;
@@ -403,6 +426,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
final SubscriptionTransitionData cur = it.next();
if (cur.getTransitionType() == SubscriptionTransitionType.PHASE
+ || cur.getTransitionType() == SubscriptionTransitionType.TRANSFER
|| cur.getTransitionType() == SubscriptionTransitionType.CREATE
|| cur.getTransitionType() == SubscriptionTransitionType.RE_CREATE
|| cur.getTransitionType() == SubscriptionTransitionType.CHANGE
@@ -415,7 +439,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
}
public void rebuildTransitions(final List<EntitlementEvent> inputEvents,
- final Catalog catalog) {
+ final Catalog catalog) {
if (inputEvents == null) {
return;
@@ -446,52 +470,53 @@ public class SubscriptionData extends EntityBase implements Subscription {
switch (cur.getType()) {
- case PHASE:
- final PhaseEvent phaseEV = (PhaseEvent) cur;
- nextPhaseName = phaseEV.getPhase();
+ case PHASE:
+ final PhaseEvent phaseEV = (PhaseEvent) cur;
+ nextPhaseName = phaseEV.getPhase();
+ break;
+
+ case API_USER:
+ final ApiEvent userEV = (ApiEvent) cur;
+ apiEventType = userEV.getEventType();
+ isFromDisk = userEV.isFromDisk();
+ nextUserToken = userEV.getUserToken();
+
+ switch (apiEventType) {
+ case TRANSFER:
+ case MIGRATE_BILLING:
+ case MIGRATE_ENTITLEMENT:
+ case CREATE:
+ case RE_CREATE:
+ previousState = null;
+ previousPlan = null;
+ previousPhase = null;
+ previousPriceList = null;
+ nextState = SubscriptionState.ACTIVE;
+ nextPlanName = userEV.getEventPlan();
+ nextPhaseName = userEV.getEventPlanPhase();
+ nextPriceListName = userEV.getPriceList();
break;
-
- case API_USER:
- final ApiEvent userEV = (ApiEvent) cur;
- apiEventType = userEV.getEventType();
- isFromDisk = userEV.isFromDisk();
- nextUserToken = userEV.getUserToken();
-
- switch (apiEventType) {
- case MIGRATE_BILLING:
- case MIGRATE_ENTITLEMENT:
- case CREATE:
- case RE_CREATE:
- previousState = null;
- previousPlan = null;
- previousPhase = null;
- previousPriceList = null;
- nextState = SubscriptionState.ACTIVE;
- nextPlanName = userEV.getEventPlan();
- nextPhaseName = userEV.getEventPlanPhase();
- nextPriceListName = userEV.getPriceList();
- break;
- case CHANGE:
- nextPlanName = userEV.getEventPlan();
- nextPhaseName = userEV.getEventPlanPhase();
- nextPriceListName = userEV.getPriceList();
- break;
- case CANCEL:
- nextState = SubscriptionState.CANCELLED;
- nextPlanName = null;
- nextPhaseName = null;
- break;
- case UNCANCEL:
- break;
- default:
- throw new EntitlementError(String.format(
- "Unexpected UserEvent type = %s", userEV
- .getEventType().toString()));
- }
+ case CHANGE:
+ nextPlanName = userEV.getEventPlan();
+ nextPhaseName = userEV.getEventPlanPhase();
+ nextPriceListName = userEV.getPriceList();
+ break;
+ case CANCEL:
+ nextState = SubscriptionState.CANCELLED;
+ nextPlanName = null;
+ nextPhaseName = null;
+ break;
+ case UNCANCEL:
break;
default:
throw new EntitlementError(String.format(
- "Unexpected Event type = %s", cur.getType()));
+ "Unexpected UserEvent type = %s", userEV
+ .getEventType().toString()));
+ }
+ break;
+ default:
+ throw new EntitlementError(String.format(
+ "Unexpected Event type = %s", cur.getType()));
}
Plan nextPlan = null;
@@ -499,8 +524,8 @@ public class SubscriptionData extends EntityBase implements Subscription {
PriceList nextPriceList = null;
try {
- nextPlan = (nextPlanName != null) ? catalog.findPlan(nextPlanName, cur.getRequestedDate(), getStartDate()) : null;
- nextPhase = (nextPhaseName != null) ? catalog.findPhase(nextPhaseName, cur.getRequestedDate(), getStartDate()) : null;
+ nextPlan = (nextPlanName != null) ? catalog.findPlan(nextPlanName, cur.getRequestedDate(), getAlignStartDate()) : null;
+ nextPhase = (nextPhaseName != null) ? catalog.findPhase(nextPhaseName, cur.getRequestedDate(), getAlignStartDate()) : null;
nextPriceList = (nextPriceListName != null) ? catalog.findPriceList(nextPriceListName, cur.getRequestedDate()) : null;
} catch (CatalogApiException e) {
log.error(String.format("Failed to build transition for subscription %s", id), e);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/AuditedEntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/AuditedEntitlementDao.java
index e8c3d2e..b2816ce 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/AuditedEntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/AuditedEntitlementDao.java
@@ -50,6 +50,7 @@ import com.ning.billing.entitlement.api.migration.AccountMigrationData.Subscript
import com.ning.billing.entitlement.api.timeline.DefaultRepairEntitlementEvent;
import com.ning.billing.entitlement.api.timeline.RepairEntitlementEvent;
import com.ning.billing.entitlement.api.timeline.SubscriptionDataRepair;
+import com.ning.billing.entitlement.api.transfer.TransferCancelData;
import com.ning.billing.entitlement.api.user.DefaultRequestedSubscriptionEvent;
import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.api.user.Subscription;
@@ -89,7 +90,6 @@ public class AuditedEntitlementDao implements EntitlementDao {
private final NotificationQueueService notificationQueueService;
private final AddonUtils addonUtils;
private final Bus eventBus;
- private final CatalogService catalogService;
@Inject
public AuditedEntitlementDao(final IDBI dbi, final Clock clock, final AddonUtils addonUtils,
@@ -101,12 +101,11 @@ public class AuditedEntitlementDao implements EntitlementDao {
this.notificationQueueService = notificationQueueService;
this.addonUtils = addonUtils;
this.eventBus = eventBus;
- this.catalogService = catalogService;
}
@Override
- public SubscriptionBundle getSubscriptionBundleFromKey(final String bundleKey) {
- return bundlesDao.getBundleFromKey(bundleKey);
+ public SubscriptionBundle getSubscriptionBundleFromAccountAndKey(final UUID accountId, final String bundleKey) {
+ return bundlesDao.getBundleFromAccountAndKey(accountId.toString(), bundleKey);
}
@Override
@@ -120,6 +119,12 @@ public class AuditedEntitlementDao implements EntitlementDao {
}
@Override
+ public List<SubscriptionBundle> getSubscriptionBundlesForKey(final String bundleKey) {
+ return bundlesDao.getBundlesForKey(bundleKey);
+ }
+
+
+ @Override
public SubscriptionBundle createSubscriptionBundle(final SubscriptionBundleData bundle, final CallContext context) {
return bundlesDao.inTransaction(new Transaction<SubscriptionBundle, BundleSqlDao>() {
@Override
@@ -174,8 +179,8 @@ public class AuditedEntitlementDao implements EntitlementDao {
}
@Override
- public List<Subscription> getSubscriptionsForKey(final SubscriptionFactory factory, final String bundleKey) {
- final SubscriptionBundle bundle = bundlesDao.getBundleFromKey(bundleKey);
+ public List<Subscription> getSubscriptionsForAccountAndKey(final SubscriptionFactory factory, final UUID accountId, final String bundleKey) {
+ final SubscriptionBundle bundle = bundlesDao.getBundleFromAccountAndKey(accountId.toString(), bundleKey);
if (bundle == null) {
return Collections.emptyList();
}
@@ -341,24 +346,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
@Override
public Void inTransaction(final EntitlementEventSqlDao transactional, final TransactionStatus status) throws Exception {
- final UUID subscriptionId = subscription.getId();
- cancelNextCancelEventFromTransaction(subscriptionId, transactional, context);
- cancelNextChangeEventFromTransaction(subscriptionId, transactional, context);
- cancelNextPhaseEventFromTransaction(subscriptionId, transactional, context);
- transactional.insertEvent(cancelEvent, context);
- final String cancelEventId = cancelEvent.getId().toString();
-
- final Long recordId = transactional.getRecordId(cancelEventId);
- final EntityAudit audit = new EntityAudit(TableName.SUBSCRIPTION_EVENTS, recordId, ChangeType.INSERT);
- transactional.insertAuditFromTransaction(audit, context);
-
- recordFutureNotificationFromTransaction(transactional,
- cancelEvent.getEffectiveDate(),
- new EntitlementNotificationKey(cancelEvent.getId(), seqId));
-
- // Notify the Bus of the requested change
- notifyBusOfRequestedChange(transactional, subscription, cancelEvent);
-
+ cancelSubscriptionFromTransaction(subscription, cancelEvent, transactional, context, seqId);
return null;
}
});
@@ -442,6 +430,28 @@ public class AuditedEntitlementDao implements EntitlementDao {
});
}
+ private void cancelSubscriptionFromTransaction(final SubscriptionData subscription, final EntitlementEvent cancelEvent, final EntitlementEventSqlDao transactional, final CallContext context, final int seqId) {
+ final UUID subscriptionId = subscription.getId();
+ cancelNextCancelEventFromTransaction(subscriptionId, transactional, context);
+ cancelNextChangeEventFromTransaction(subscriptionId, transactional, context);
+ cancelNextPhaseEventFromTransaction(subscriptionId, transactional, context);
+ transactional.insertEvent(cancelEvent, context);
+ final String cancelEventId = cancelEvent.getId().toString();
+
+ final Long recordId = transactional.getRecordId(cancelEventId);
+ final EntityAudit audit = new EntityAudit(TableName.SUBSCRIPTION_EVENTS, recordId, ChangeType.INSERT);
+ transactional.insertAuditFromTransaction(audit, context);
+
+ recordFutureNotificationFromTransaction(transactional,
+ cancelEvent.getEffectiveDate(),
+ new EntitlementNotificationKey(cancelEvent.getId(), seqId));
+
+ // Notify the Bus of the requested change
+ notifyBusOfRequestedChange(transactional, subscription, cancelEvent);
+ }
+
+
+
private void cancelNextPhaseEventFromTransaction(final UUID subscriptionId, final EntitlementEventSqlDao dao, final CallContext context) {
cancelFutureEventFromTransaction(subscriptionId, dao, EventType.PHASE, null, context);
}
@@ -529,7 +539,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
} else if (o2.getCategory() == ProductCategory.BASE) {
return 1;
} else {
- return o1.getStartDate().compareTo(o2.getStartDate());
+ return ((SubscriptionData) o1).getAlignStartDate().compareTo(((SubscriptionData) o2).getAlignStartDate());
}
}
});
@@ -594,44 +604,9 @@ public class AuditedEntitlementDao implements EntitlementDao {
eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
@Override
public Void inTransaction(final EntitlementEventSqlDao transactional, final TransactionStatus status) throws Exception {
- final SubscriptionSqlDao transSubDao = transactional.become(SubscriptionSqlDao.class);
- final BundleSqlDao transBundleDao = transactional.become(BundleSqlDao.class);
-
- final List<EntityAudit> audits = new ArrayList<EntityAudit>();
-
- Long recordId;
-
for (final BundleMigrationData curBundle : accountData.getData()) {
- final SubscriptionBundleData bundleData = curBundle.getData();
-
- for (final SubscriptionMigrationData curSubscription : curBundle.getSubscriptions()) {
- final SubscriptionData subData = curSubscription.getData();
- for (final EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
- transactional.insertEvent(curEvent, context);
- recordId = transactional.getRecordId(curEvent.getId().toString());
- audits.add(new EntityAudit(TableName.SUBSCRIPTION_EVENTS, recordId, ChangeType.INSERT));
-
- recordFutureNotificationFromTransaction(transactional,
- curEvent.getEffectiveDate(),
- new EntitlementNotificationKey(curEvent.getId()));
- }
- transSubDao.insertSubscription(subData, context);
- recordId = transSubDao.getRecordId(subData.getId().toString());
- audits.add(new EntityAudit(TableName.SUBSCRIPTIONS, recordId, ChangeType.INSERT));
-
- // Notify the Bus of the latest requested change
- final EntitlementEvent finalEvent = curSubscription.getInitialEvents().get(curSubscription.getInitialEvents().size() - 1);
- notifyBusOfRequestedChange(transactional, subData, finalEvent);
- }
-
- transBundleDao.insertBundle(bundleData, context);
- recordId = transBundleDao.getRecordId(bundleData.getId().toString());
- audits.add(new EntityAudit(TableName.BUNDLES, recordId, ChangeType.INSERT));
+ migrateBundleDataFromTransaction(curBundle, transactional, context);
}
-
- // add audit records for bundles, subscriptions, and events
- transSubDao.insertAuditFromTransaction(audits, context);
-
return null;
}
});
@@ -644,7 +619,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
public Void inTransaction(final SubscriptionSqlDao transactional, final TransactionStatus status) throws Exception {
final EntitlementEventSqlDao transEventDao = transactional.become(EntitlementEventSqlDao.class);
for (final SubscriptionDataRepair cur : inRepair) {
- transactional.updateForRepair(cur.getId().toString(), cur.getActiveVersion(), cur.getStartDate().toDate(), cur.getBundleStartDate().toDate(), context);
+ transactional.updateForRepair(cur.getId().toString(), cur.getActiveVersion(), cur.getAlignStartDate().toDate(), cur.getBundleStartDate().toDate(), context);
for (final EntitlementEvent event : cur.getInitialEvents()) {
transEventDao.updateVersion(event.getId().toString(), event.getActiveVersion(), context);
}
@@ -671,6 +646,26 @@ public class AuditedEntitlementDao implements EntitlementDao {
});
}
+ @Override
+ public void transfer(final UUID srcAccountId, final UUID destAccountId, final BundleMigrationData bundleTransferData,
+ final List<TransferCancelData> transferCancelData, final CallContext context) {
+
+ eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
+ @Override
+ public Void inTransaction(final EntitlementEventSqlDao transactional, final TransactionStatus status) throws Exception {
+
+ // Cancel the subscriptions for the old bundle
+ for (TransferCancelData cancel : transferCancelData) {
+ cancelSubscriptionFromTransaction(cancel.getSubscription(), cancel.getCancelEvent(), transactional, context, 0);
+ }
+
+ migrateBundleDataFromTransaction(bundleTransferData, transactional, context);
+ return null;
+ }
+ });
+ }
+
+
private Subscription getBaseSubscription(final SubscriptionFactory factory, final UUID bundleId, final boolean rebuildSubscription) {
final List<Subscription> subscriptions = subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString());
for (final Subscription cur : subscriptions) {
@@ -701,4 +696,40 @@ public class AuditedEntitlementDao implements EntitlementDao {
log.warn("Failed to post requested change event for subscription " + subscription.getId(), e);
}
}
+
+ private void migrateBundleDataFromTransaction(final BundleMigrationData bundleTransferData, final EntitlementEventSqlDao transactional, final CallContext context) {
+
+ final SubscriptionSqlDao transSubDao = transactional.become(SubscriptionSqlDao.class);
+ final BundleSqlDao transBundleDao = transactional.become(BundleSqlDao.class);
+
+ final List<EntityAudit> audits = new ArrayList<EntityAudit>();
+ Long recordId;
+ final SubscriptionBundleData bundleData = bundleTransferData.getData();
+ for (final SubscriptionMigrationData curSubscription : bundleTransferData.getSubscriptions()) {
+ final SubscriptionData subData = curSubscription.getData();
+ for (final EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
+ transactional.insertEvent(curEvent, context);
+ recordId = transactional.getRecordId(curEvent.getId().toString());
+ audits.add(new EntityAudit(TableName.SUBSCRIPTION_EVENTS, recordId, ChangeType.INSERT));
+
+ recordFutureNotificationFromTransaction(transactional,
+ curEvent.getEffectiveDate(),
+ new EntitlementNotificationKey(curEvent.getId()));
+ }
+ transSubDao.insertSubscription(subData, context);
+ recordId = transSubDao.getRecordId(subData.getId().toString());
+ audits.add(new EntityAudit(TableName.SUBSCRIPTIONS, recordId, ChangeType.INSERT));
+
+ // Notify the Bus of the latest requested change
+ final EntitlementEvent finalEvent = curSubscription.getInitialEvents().get(curSubscription.getInitialEvents().size() - 1);
+ notifyBusOfRequestedChange(transactional, subData, finalEvent);
+ }
+
+ transBundleDao.insertBundle(bundleData, context);
+ recordId = transBundleDao.getRecordId(bundleData.getId().toString());
+ audits.add(new EntityAudit(TableName.BUNDLES, recordId, ChangeType.INSERT));
+
+ // add audit records for bundles, subscriptions, and events
+ transSubDao.insertAuditFromTransaction(audits, context);
+ }
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
index 5c5e7fc..c48a5f2 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
@@ -60,11 +60,14 @@ public interface BundleSqlDao extends Transactional<BundleSqlDao>, EntitySqlDao<
public SubscriptionBundle getBundleFromId(@Bind("id") String id);
@SqlQuery
- public SubscriptionBundle getBundleFromKey(@Bind("externalKey") String externalKey);
+ public SubscriptionBundle getBundleFromAccountAndKey(@Bind("accountId") String accountId, @Bind("externalKey") String externalKey);
@SqlQuery
public List<SubscriptionBundle> getBundleFromAccount(@Bind("accountId") String accountId);
+ @SqlQuery
+ public List<SubscriptionBundle> getBundlesForKey(@Bind("externalKey") String externalKey);
+
public static class SubscriptionBundleBinder extends BinderBase implements Binder<Bind, SubscriptionBundleData> {
@Override
public void bind(@SuppressWarnings("rawtypes") final SQLStatement stmt, final Bind bind, final SubscriptionBundleData bundle) {
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 b85a47a..0544d1f 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
@@ -22,7 +22,9 @@ import java.util.UUID;
import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.migration.AccountMigrationData;
+import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
import com.ning.billing.entitlement.api.timeline.SubscriptionDataRepair;
+import com.ning.billing.entitlement.api.transfer.TransferCancelData;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
@@ -34,7 +36,9 @@ public interface EntitlementDao {
// Bundle apis
public List<SubscriptionBundle> getSubscriptionBundleForAccount(final UUID accountId);
- public SubscriptionBundle getSubscriptionBundleFromKey(final String bundleKey);
+ public List<SubscriptionBundle> getSubscriptionBundlesForKey(final String bundleKey);
+
+ public SubscriptionBundle getSubscriptionBundleFromAccountAndKey(final UUID accountId, final String bundleKey);
public SubscriptionBundle getSubscriptionBundleFromId(final UUID bundleId);
@@ -50,7 +54,7 @@ public interface EntitlementDao {
public List<Subscription> getSubscriptions(final SubscriptionFactory factory, final UUID bundleId);
- public List<Subscription> getSubscriptionsForKey(final SubscriptionFactory factory, final String bundleKey);
+ public List<Subscription> getSubscriptionsForAccountAndKey(final SubscriptionFactory factory, final UUID accountId, final String bundleKey);
// Update
public void updateChargedThroughDate(final SubscriptionData subscription, final CallContext context);
@@ -79,6 +83,8 @@ public interface EntitlementDao {
public void migrate(final UUID accountId, final AccountMigrationData data, final CallContext context);
+ public void transfer(final UUID srcAccountId, final UUID destAccountId, final BundleMigrationData data, final List<TransferCancelData> transferCancelData, final CallContext context);
+
// Repair
public void repair(final UUID accountId, final UUID bundleId, final List<SubscriptionDataRepair> inRepair, final CallContext context);
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.java
index 74973b7..4c89cf6 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.java
@@ -50,6 +50,7 @@ import com.ning.billing.entitlement.events.user.ApiEventCreate;
import com.ning.billing.entitlement.events.user.ApiEventMigrateBilling;
import com.ning.billing.entitlement.events.user.ApiEventMigrateEntitlement;
import com.ning.billing.entitlement.events.user.ApiEventReCreate;
+import com.ning.billing.entitlement.events.user.ApiEventTransfer;
import com.ning.billing.entitlement.events.user.ApiEventType;
import com.ning.billing.entitlement.events.user.ApiEventUncancel;
import com.ning.billing.entitlement.exceptions.EntitlementError;
@@ -176,6 +177,8 @@ public interface EntitlementEventSqlDao extends Transactional<EntitlementEventSq
result = new ApiEventMigrateEntitlement(builder);
} else if (userType == ApiEventType.MIGRATE_BILLING) {
result = new ApiEventMigrateBilling(builder);
+ } else if (userType == ApiEventType.TRANSFER) {
+ result = new ApiEventTransfer(builder);
} else if (userType == ApiEventType.CHANGE) {
result = new ApiEventChange(builder);
} else if (userType == ApiEventType.CANCEL) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java
index c9f2681..ad82bd5 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java
@@ -31,8 +31,10 @@ import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.migration.AccountMigrationData;
+import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
import com.ning.billing.entitlement.api.timeline.RepairEntitlementLifecycleDao;
import com.ning.billing.entitlement.api.timeline.SubscriptionDataRepair;
+import com.ning.billing.entitlement.api.transfer.TransferCancelData;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
@@ -213,7 +215,7 @@ public class RepairEntitlementDao implements EntitlementDao, RepairEntitlementLi
}
@Override
- public SubscriptionBundle getSubscriptionBundleFromKey(final String bundleKey) {
+ public SubscriptionBundle getSubscriptionBundleFromAccountAndKey(final UUID accountId, final String bundleKey) {
throw new EntitlementError(NOT_IMPLEMENTED);
}
@@ -252,8 +254,7 @@ public class RepairEntitlementDao implements EntitlementDao, RepairEntitlementLi
}
@Override
- public List<Subscription> getSubscriptionsForKey(
- final SubscriptionFactory factory, final String bundleKey) {
+ public List<Subscription> getSubscriptionsForAccountAndKey(final SubscriptionFactory factory, final UUID accountId, final String bundleKey) {
throw new EntitlementError(NOT_IMPLEMENTED);
}
@@ -296,4 +297,17 @@ public class RepairEntitlementDao implements EntitlementDao, RepairEntitlementLi
final CallContext context) {
throw new EntitlementError(NOT_IMPLEMENTED);
}
+
+ @Override
+ public void transfer(UUID srcAccountId, UUID destAccountId,
+ BundleMigrationData data,
+ List<TransferCancelData> transferCancelData, CallContext context) {
+ throw new EntitlementError(NOT_IMPLEMENTED);
+ }
+
+ @Override
+ public List<SubscriptionBundle> getSubscriptionBundlesForKey(
+ String bundleKey) {
+ throw new EntitlementError(NOT_IMPLEMENTED);
+ }
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
index 861973c..5debc59 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
@@ -80,7 +80,7 @@ public interface SubscriptionSqlDao extends Transactional<SubscriptionSqlDao>, A
stmt.bind("id", sub.getId().toString());
stmt.bind("bundleId", sub.getBundleId().toString());
stmt.bind("category", sub.getCategory().toString());
- stmt.bind("startDate", getDate(sub.getStartDate()));
+ stmt.bind("startDate", getDate(sub.getAlignStartDate()));
stmt.bind("bundleStartDate", getDate(sub.getBundleStartDate()));
stmt.bind("activeVersion", sub.getActiveVersion());
stmt.bind("chargedThroughDate", getDate(sub.getChargedThroughDate()));
@@ -107,7 +107,7 @@ public interface SubscriptionSqlDao extends Transactional<SubscriptionSqlDao>, A
.setBundleId(bundleId)
.setCategory(category)
.setBundleStartDate(bundleStartDate)
- .setStartDate(startDate)
+ .setAlignStartDate(startDate)
.setActiveVersion(activeVersion)
.setChargedThroughDate(ctd)
.setPaidThroughDate(ptd));
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventTransfer.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventTransfer.java
new file mode 100644
index 0000000..0773f34
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventTransfer.java
@@ -0,0 +1,23 @@
+/*
+ * 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.events.user;
+
+public class ApiEventTransfer extends ApiEventBase {
+ public ApiEventTransfer(final ApiEventBuilder builder) {
+ super(builder.setEventType(ApiEventType.TRANSFER));
+ }
+
+}
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 fb1954c..1e8731f 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
@@ -38,6 +38,12 @@ public enum ApiEventType {
return SubscriptionTransitionType.MIGRATE_BILLING;
}
},
+ TRANSFER {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() {
+ return SubscriptionTransitionType.TRANSFER;
+ }
+ },
CHANGE {
@Override
public SubscriptionTransitionType getSubscriptionTransitionType() {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java b/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
index 5dc11bf..0d3fd20 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
@@ -35,6 +35,8 @@ import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
import com.ning.billing.entitlement.api.timeline.RepairEntitlementLifecycleDao;
import com.ning.billing.entitlement.api.timeline.RepairSubscriptionApiService;
import com.ning.billing.entitlement.api.timeline.RepairSubscriptionFactory;
+import com.ning.billing.entitlement.api.transfer.DefaultEntitlementTransferApi;
+import com.ning.billing.entitlement.api.transfer.EntitlementTransferApi;
import com.ning.billing.entitlement.api.user.DefaultEntitlementUserApi;
import com.ning.billing.entitlement.api.user.DefaultSubscriptionApiService;
import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory;
@@ -81,6 +83,7 @@ public class DefaultEntitlementModule extends AbstractModule implements Entitlem
installEntitlementMigrationApi();
installChargeThruApi();
installEntitlementUserApi();
+ installEntitlementTransferApi();
}
@Override
@@ -105,6 +108,8 @@ public class DefaultEntitlementModule extends AbstractModule implements Entitlem
bind(EntitlementMigrationApi.class).to(DefaultEntitlementMigrationApi.class).asEagerSingleton();
}
+
+
@Override
public void installChargeThruApi() {
bind(ChargeThruApi.class).to(DefaultChargeThruApi.class).asEagerSingleton();
@@ -114,4 +119,9 @@ public class DefaultEntitlementModule extends AbstractModule implements Entitlem
public void installEntitlementUserApi() {
bind(EntitlementUserApi.class).annotatedWith(RealImplementation.class).to(DefaultEntitlementUserApi.class).asEagerSingleton();
}
+
+ @Override
+ public void installEntitlementTransferApi() {
+ bind(EntitlementTransferApi.class).to(DefaultEntitlementTransferApi.class).asEagerSingleton();
+ }
}
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
index 1ecf151..e4d5a68 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
@@ -28,7 +28,7 @@ getBundleFromId() ::= <<
;
>>
-getBundleFromKey() ::= <<
+getBundlesForKey() ::= <<
select <fields()>
from bundles
where
@@ -36,6 +36,14 @@ getBundleFromKey() ::= <<
;
>>
+getBundleFromAccountAndKey() ::= <<
+ select <fields()>
+ from bundles
+ where
+ external_key = :externalKey AND account_id = :accountId
+ ;
+>>
+
getBundleFromAccount() ::= <<
select <fields()>
from bundles
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/alignment/TestPlanAligner.java b/entitlement/src/test/java/com/ning/billing/entitlement/alignment/TestPlanAligner.java
index 65df594..aafeb08 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/alignment/TestPlanAligner.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/alignment/TestPlanAligner.java
@@ -166,11 +166,11 @@ public class TestPlanAligner extends KillbillTestSuite {
final DefaultSubscriptionFactory.SubscriptionBuilder builder = new DefaultSubscriptionFactory.SubscriptionBuilder();
builder.setBundleStartDate(clock.getUTCNow().minusHours(10));
// Make sure to set the dates apart
- builder.setStartDate(new DateTime(builder.getBundleStartDate().plusHours(5)));
+ builder.setAlignStartDate(new DateTime(builder.getBundleStartDate().plusHours(5)));
// Create the transitions
final SubscriptionData subscriptionData = new SubscriptionData(builder, null, clock);
- final EntitlementEvent event = createEntitlementEvent(builder.getStartDate(),
+ final EntitlementEvent event = createEntitlementEvent(builder.getAlignStartDate(),
productName,
phaseType,
ApiEventType.CREATE,
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 69ece03..88b24b1 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
@@ -57,6 +57,8 @@ import com.ning.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
import com.ning.billing.entitlement.api.billing.ChargeThruApi;
import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
+import com.ning.billing.entitlement.api.transfer.EntitlementTransferApi;
+import com.ning.billing.entitlement.api.transfer.EntitlementTransferApiException;
import com.ning.billing.entitlement.api.user.EffectiveSubscriptionEvent;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
@@ -89,6 +91,7 @@ public abstract class TestApiBase extends EntitlementTestSuiteWithEmbeddedDB imp
protected EntitlementService entitlementService;
protected EntitlementUserApi entitlementApi;
protected ChargeThruApi billingApi;
+ protected EntitlementTransferApi transferApi;
protected EntitlementMigrationApi migrationApi;
protected EntitlementTimelineApi repairApi;
@@ -160,6 +163,7 @@ public abstract class TestApiBase extends EntitlementTestSuiteWithEmbeddedDB imp
billingApi = g.getInstance(ChargeThruApi.class);
migrationApi = g.getInstance(EntitlementMigrationApi.class);
repairApi = g.getInstance(EntitlementTimelineApi.class);
+ transferApi = g.getInstance(EntitlementTransferApi.class);
catalogService = g.getInstance(CatalogService.class);
busService = g.getInstance(BusService.class);
config = g.getInstance(EntitlementConfig.class);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestApiBaseRepair.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestApiBaseRepair.java
index 449d1e7..6fb3525 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestApiBaseRepair.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestApiBaseRepair.java
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2010-2011 Ning, Inc.
*
* Ning licenses this file to you under the Apache License, version 2.0
@@ -79,6 +79,11 @@ public abstract class TestApiBaseRepair extends TestApiBase {
public List<DeletedEvent> getDeletedEvents() {
return deletedEvents;
}
+
+ @Override
+ public long getActiveVersion() {
+ return 1;
+ }
};
}
@@ -135,6 +140,11 @@ public abstract class TestApiBaseRepair extends TestApiBase {
public DateTime getEffectiveDate() {
return effectiveDateTime;
}
+
+ @Override
+ public String getPlanPhaseName() {
+ return null;
+ }
};
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairBP.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairBP.java
index e57df9b..0d73825 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairBP.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairBP.java
@@ -74,7 +74,7 @@ public class TestRepairBP extends TestApiBaseRepair {
final SubscriptionData aoSubscription = createSubscription(aoProduct, aoTerm, aoPriceList);
- final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
final List<SubscriptionTimeline> subscriptionRepair = bundleRepair.getSubscriptions();
assertEquals(subscriptionRepair.size(), 2);
@@ -134,7 +134,7 @@ public class TestRepairBP extends TestApiBaseRepair {
final Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(10));
clock.addDeltaFromReality(it.toDurationMillis());
- final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
final List<DeletedEvent> des = new LinkedList<SubscriptionTimeline.DeletedEvent>();
@@ -306,7 +306,7 @@ public class TestRepairBP extends TestApiBaseRepair {
}
}
- final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
final DateTime newCreateTime = baseSubscription.getStartDate().plusDays(clockShift - 1);
@@ -476,7 +476,7 @@ public class TestRepairBP extends TestApiBaseRepair {
assertTrue(testListener.isCompleted(5000));
}
- final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
final DateTime changeTime = baseSubscription.getStartDate().plusDays(clockShift - 1);
@@ -597,7 +597,7 @@ public class TestRepairBP extends TestApiBaseRepair {
assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.MONTHLY);
final DateTime repairTime = clock.getUTCNow().minusDays(1);
- final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
@@ -647,7 +647,7 @@ public class TestRepairBP extends TestApiBaseRepair {
@Override
public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
- final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
final NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
@@ -680,7 +680,7 @@ public class TestRepairBP extends TestApiBaseRepair {
@Override
public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
- final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
final NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithAO.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithAO.java
index 7757cec..661f960 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithAO.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithAO.java
@@ -75,7 +75,7 @@ public class TestRepairWithAO extends TestApiBaseRepair {
it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
clock.addDeltaFromReality(it.toDurationMillis());
- BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
// Quick check
@@ -226,7 +226,7 @@ public class TestRepairWithAO extends TestApiBaseRepair {
clock.addDeltaFromReality(it.toDurationMillis());
assertTrue(testListener.isCompleted(7000));
- BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
// Quick check
@@ -350,7 +350,7 @@ public class TestRepairWithAO extends TestApiBaseRepair {
billingApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate, context);
baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
- BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
// Quick check
@@ -480,7 +480,7 @@ public class TestRepairWithAO extends TestApiBaseRepair {
it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
clock.addDeltaFromReality(it.toDurationMillis());
- final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
// Quick check
@@ -570,7 +570,7 @@ public class TestRepairWithAO extends TestApiBaseRepair {
it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
clock.addDeltaFromReality(it.toDurationMillis());
- final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
// Quick check
@@ -661,7 +661,7 @@ public class TestRepairWithAO extends TestApiBaseRepair {
it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
clock.addDeltaFromReality(it.toDurationMillis());
- final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
// Quick check
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithError.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithError.java
index b15446d..83d2f1e 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithError.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithError.java
@@ -78,7 +78,7 @@ public class TestRepairWithError extends TestApiBaseRepair {
assertTrue(testListener.isCompleted(5000));
- final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
final NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
@@ -112,7 +112,7 @@ public class TestRepairWithError extends TestApiBaseRepair {
clock.addDeltaFromReality(it.toDurationMillis());
assertTrue(testListener.isCompleted(5000));
- final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
final NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
@@ -132,7 +132,7 @@ public class TestRepairWithError extends TestApiBaseRepair {
@Override
public void doTest() throws EntitlementRepairException {
- final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
final NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
@@ -158,7 +158,7 @@ public class TestRepairWithError extends TestApiBaseRepair {
clock.addDeltaFromReality(it.toDurationMillis());
assertTrue(testListener.isCompleted(5000));
- final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
final NewEvent ne = createNewEvent(SubscriptionTransitionType.CREATE, baseSubscription.getStartDate().plusDays(10), spec);
@@ -187,7 +187,7 @@ public class TestRepairWithError extends TestApiBaseRepair {
clock.addDeltaFromReality(it.toDurationMillis());
assertTrue(testListener.isCompleted(5000));
- final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
final NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
@@ -217,7 +217,7 @@ public class TestRepairWithError extends TestApiBaseRepair {
it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(4));
clock.addDeltaFromReality(it.toDurationMillis());
- final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
// Quick check
@@ -260,7 +260,7 @@ public class TestRepairWithError extends TestApiBaseRepair {
it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(4));
clock.addDeltaFromReality(it.toDurationMillis());
- BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
// Quick check
@@ -300,7 +300,7 @@ public class TestRepairWithError extends TestApiBaseRepair {
final SubscriptionData aoSubscription = createSubscription("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
- final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
sortEventsOnBundle(bundleRepair);
final DateTime newCreateTime = baseSubscription.getStartDate().plusDays(3);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestTransfer.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestTransfer.java
new file mode 100644
index 0000000..be36ccb
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestTransfer.java
@@ -0,0 +1,366 @@
+/*
+ * 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.transfer;
+
+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 java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+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.api.TestApiListener.NextEvent;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.Product;
+import com.ning.billing.entitlement.api.TestApiBase;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.glue.MockEngineModuleSql;
+
+public class TestTransfer extends TestApiBase {
+
+ protected static final Logger log = LoggerFactory.getLogger(TestTransfer.class);
+
+
+ @Override
+ protected Injector getInjector() {
+ return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
+ }
+
+ @Test(groups = "slow")
+ public void testTransferBPInTrialWithNoCTD() throws Exception {
+
+ final UUID newAccountId = UUID.randomUUID();
+
+ final String baseProduct = "Shotgun";
+ final BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+ final String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ // CREATE BP
+ final Subscription baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+ final DateTime evergreenPhaseDate = baseSubscription.getPendingTransition().getEffectiveTransitionTime();
+
+ // MOVE A LITTLE, STILL IN TRIAL
+ clock.addDays(20);
+
+ final DateTime beforeTransferDate = clock.getUTCNow();
+ final DateTime transferRequestedDate = clock.getUTCNow();
+
+ testListener.pushExpectedEvent(NextEvent.TRANSFER);
+ testListener.pushExpectedEvent(NextEvent.CANCEL);
+ transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, context);
+ assertTrue(testListener.isCompleted(3000));
+ final DateTime afterTransferDate = clock.getUTCNow();
+
+ // CHECK OLD BASE IS CANCEL AT THE TRANSFER DATE
+ final Subscription oldBaseSubscription = entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+ assertNotNull(oldBaseSubscription.getEndDate());
+ assertDateWithin(oldBaseSubscription.getEndDate(), beforeTransferDate, afterTransferDate);
+ assertTrue(oldBaseSubscription.getEndDate().compareTo(transferRequestedDate) == 0);
+
+ // CHECK NEW BUNDLE EXIST, WITH ONE SUBSCRIPTION SARTING ON TRANSFER_DATE
+ SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getKey());
+
+ List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(newBundle.getId());
+ assertEquals(subscriptions.size(), 1);
+
+ final Subscription newBaseSubscription = subscriptions.get(0);
+ assertTrue(((SubscriptionData) newBaseSubscription).getAlignStartDate().compareTo(((SubscriptionData) oldBaseSubscription).getAlignStartDate()) == 0);
+
+ // CHECK NEXT PENDING PHASE IS ALIGNED WITH OLD SUBSCRIPTION START DATE
+ assertEquals(newBaseSubscription.getAllTransitions().size(), 2);
+ assertTrue(newBaseSubscription.getAllTransitions().get(1).getEffectiveTransitionTime().compareTo(evergreenPhaseDate) == 0);
+
+ Plan newPlan = newBaseSubscription.getCurrentPlan();
+ assertEquals(newPlan.getProduct().getName(), baseProduct);
+ assertEquals(newBaseSubscription.getCurrentPhase().getPhaseType(), PhaseType.TRIAL);
+ }
+
+
+ @Test(groups = "slow")
+ public void testTransferBPInTrialWithCTD() throws Exception {
+
+ final UUID newAccountId = UUID.randomUUID();
+
+ final String baseProduct = "Shotgun";
+ final BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+ final String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ // CREATE BP
+ final Subscription baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+ final DateTime ctd = baseSubscription.getStartDate().plusDays(30);
+
+ billingApi.setChargedThroughDate(baseSubscription.getId(), ctd, context);
+
+ final DateTime evergreenPhaseDate = baseSubscription.getPendingTransition().getEffectiveTransitionTime();
+
+ // MOVE A LITTLE, STILL IN TRIAL
+ clock.addDays(20);
+
+ testListener.pushExpectedEvent(NextEvent.TRANSFER);
+ final DateTime transferRequestedDate = clock.getUTCNow();
+ transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, context);
+ assertTrue(testListener.isCompleted(3000));
+
+ // CHECK OLD BASE IS CANCEL AT THE TRANSFER DATE
+ final Subscription oldBaseSubscription = entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+ assertNotNull(oldBaseSubscription.getFutureEndDate());
+ assertTrue(oldBaseSubscription.getFutureEndDate().compareTo(ctd) == 0);
+
+ // CHECK NEW BUNDLE EXIST, WITH ONE SUBSCRIPTION SARTING ON TRANSFER_DATE
+ SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getKey());
+
+ List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(newBundle.getId());
+ assertEquals(subscriptions.size(), 1);
+
+ final Subscription newBaseSubscription = subscriptions.get(0);
+ assertTrue(((SubscriptionData) newBaseSubscription).getAlignStartDate().compareTo(((SubscriptionData) oldBaseSubscription).getAlignStartDate()) == 0);
+
+ // CHECK NEXT PENDING PHASE IS ALIGNED WITH OLD SUBSCRIPTION START DATE
+ assertEquals(newBaseSubscription.getAllTransitions().size(), 2);
+ assertTrue(newBaseSubscription.getAllTransitions().get(1).getEffectiveTransitionTime().compareTo(evergreenPhaseDate) == 0);
+
+ Plan newPlan = newBaseSubscription.getCurrentPlan();
+ assertEquals(newPlan.getProduct().getName(), baseProduct);
+ assertEquals(newBaseSubscription.getCurrentPhase().getPhaseType(), PhaseType.TRIAL);
+ }
+
+
+ @Test(groups = "slow")
+ public void testTransferBPNoTrialWithNoCTD() throws Exception {
+
+ final UUID newAccountId = UUID.randomUUID();
+
+ final String baseProduct = "Shotgun";
+ final BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+ final String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ // CREATE BP
+ final Subscription baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+ // MOVE AFTER TRIAL
+ testListener.pushExpectedEvent(NextEvent.PHASE);
+ clock.addDays(40);
+ assertTrue(testListener.isCompleted(3000));
+
+ final DateTime beforeTransferDate = clock.getUTCNow();
+ final DateTime transferRequestedDate = clock.getUTCNow();
+ testListener.pushExpectedEvent(NextEvent.TRANSFER);
+ testListener.pushExpectedEvent(NextEvent.CANCEL);
+ transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, context);
+ assertTrue(testListener.isCompleted(3000));
+ final DateTime afterTransferDate = clock.getUTCNow();
+
+ // CHECK OLD BASE IS CANCEL AT THE TRANSFER DATE
+ final Subscription oldBaseSubscription = entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+ assertNotNull(oldBaseSubscription.getEndDate());
+ assertDateWithin(oldBaseSubscription.getEndDate(), beforeTransferDate, afterTransferDate);
+ assertTrue(oldBaseSubscription.getEndDate().compareTo(transferRequestedDate) == 0);
+
+ // CHECK NEW BUNDLE EXIST, WITH ONE SUBSCRIPTION SARTING ON TRANSFER_DATE
+ SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getKey());
+
+ List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(newBundle.getId());
+ assertEquals(subscriptions.size(), 1);
+
+ final Subscription newBaseSubscription = subscriptions.get(0);
+ assertTrue(((SubscriptionData) newBaseSubscription).getAlignStartDate().compareTo(((SubscriptionData) baseSubscription).getAlignStartDate()) == 0);
+
+ // CHECK ONLY ONE PHASE EXISTS
+ assertEquals(newBaseSubscription.getAllTransitions().size(), 1);
+
+ Plan newPlan = newBaseSubscription.getCurrentPlan();
+ assertEquals(newPlan.getProduct().getName(), baseProduct);
+ assertEquals(newBaseSubscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+ }
+
+ @Test(groups = "slow")
+ public void testTransferBPNoTrialWithCTD() throws Exception {
+
+ final UUID newAccountId = UUID.randomUUID();
+
+ final String baseProduct = "Shotgun";
+ final BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+ final String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ // CREATE BP
+ final Subscription baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+ // MOVE AFTER TRIAL
+ testListener.pushExpectedEvent(NextEvent.PHASE);
+ clock.addDays(40);
+ assertTrue(testListener.isCompleted(3000));
+
+ // SET CTD
+ final DateTime ctd = baseSubscription.getStartDate().plusDays(30).plusMonths(1);
+ billingApi.setChargedThroughDate(baseSubscription.getId(), ctd, context);
+
+
+ final DateTime transferRequestedDate = clock.getUTCNow();
+ testListener.pushExpectedEvent(NextEvent.TRANSFER);
+ transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, context);
+ assertTrue(testListener.isCompleted(3000));
+
+ // CHECK OLD BASE IS CANCEL AT THE TRANSFER DATE
+ final Subscription oldBaseSubscription = entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+ assertNotNull(oldBaseSubscription.getFutureEndDate());
+ assertTrue(oldBaseSubscription.getFutureEndDate().compareTo(ctd) == 0);
+
+ // CHECK NEW BUNDLE EXIST, WITH ONE SUBSCRIPTION SARTING ON TRANSFER_DATE
+ SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getKey());
+
+ List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(newBundle.getId());
+ assertEquals(subscriptions.size(), 1);
+
+ final Subscription newBaseSubscription = subscriptions.get(0);
+ assertTrue(((SubscriptionData) newBaseSubscription).getAlignStartDate().compareTo(((SubscriptionData) baseSubscription).getAlignStartDate()) == 0);
+
+ // CHECK ONLY ONE PHASE EXISTS
+ assertEquals(newBaseSubscription.getAllTransitions().size(), 1);
+
+ Plan newPlan = newBaseSubscription.getCurrentPlan();
+ assertEquals(newPlan.getProduct().getName(), baseProduct);
+ assertEquals(newBaseSubscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+
+ // MAKE A PLAN CHANGE IMM
+ clock.addDays(5);
+
+ final String newBaseProduct1 = "Assault-Rifle";
+ final BillingPeriod newBaseTerm1 = BillingPeriod.ANNUAL;
+ final DateTime changeDate1 = clock.getUTCNow();
+ newBaseSubscription.changePlan(newBaseProduct1, newBaseTerm1, basePriceList, changeDate1, context);
+
+ newPlan = newBaseSubscription.getCurrentPlan();
+ assertEquals(newPlan.getProduct().getName(), newBaseProduct1);
+ assertEquals(newBaseSubscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+
+ // SET CTD AND MAKE CHANGE EOT
+ clock.addDays(2);
+
+ DateTime newCtd = newBaseSubscription.getStartDate().plusYears(1);
+ billingApi.setChargedThroughDate(newBaseSubscription.getId(), newCtd, context);
+ final Subscription newBaseSubscriptionWithCtd = entitlementApi.getSubscriptionFromId(newBaseSubscription.getId());
+
+ final String newBaseProduct2 = "Pistol";
+ final BillingPeriod newBaseTerm2 = BillingPeriod.ANNUAL;
+ final DateTime changeDate2 = clock.getUTCNow();
+ newBaseSubscriptionWithCtd.changePlan(newBaseProduct2, newBaseTerm2, basePriceList, changeDate2, context);
+
+ newPlan = newBaseSubscriptionWithCtd.getCurrentPlan();
+ assertEquals(newPlan.getProduct().getName(), newBaseProduct1);
+ assertEquals(newBaseSubscriptionWithCtd.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+
+ assertNotNull(newBaseSubscriptionWithCtd.getPendingTransition());
+ assertEquals(newBaseSubscriptionWithCtd.getPendingTransition().getEffectiveTransitionTime(), newCtd);
+ }
+
+ @Test(groups = "slow")
+ public void testTransferWithAO() throws Exception {
+
+ final UUID newAccountId = UUID.randomUUID();
+
+ final String baseProduct = "Shotgun";
+ final BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+ final String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ // CREATE BP
+ final Subscription baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+ // MOVE 3 DAYS AND CREATE AO1
+ clock.addDays(3);
+ final String aoProduct1 = "Telescopic-Scope";
+ final BillingPeriod aoTerm1 = BillingPeriod.MONTHLY;
+ final SubscriptionData aoSubscription1 = createSubscription(aoProduct1, aoTerm1, basePriceList);
+ assertEquals(aoSubscription1.getState(), SubscriptionState.ACTIVE);
+
+ // MOVE ANOTHER 25 DAYS AND CREATE AO2 [ BP STILL IN TRIAL]
+ // LASER-SCOPE IS SUBSCRIPTION ALIGN SO EVERGREN WILL ONLY START IN A MONTH
+ clock.addDays(25);
+ final String aoProduct2 = "Laser-Scope";
+ final BillingPeriod aoTerm2 = BillingPeriod.MONTHLY;
+ final SubscriptionData aoSubscription2 = createSubscription(aoProduct2, aoTerm2, basePriceList);
+ assertEquals(aoSubscription2.getState(), SubscriptionState.ACTIVE);
+
+ // MOVE AFTER TRIAL AND AO DISCOUNT PHASE [LASER SCOPE STILL IN DISCOUNT]
+ testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushExpectedEvent(NextEvent.PHASE);
+ clock.addDays(5);
+ assertTrue(testListener.isCompleted(3000));
+
+ // SET CTD TO TRIGGER CANCELLATION EOT
+ final DateTime ctd = baseSubscription.getStartDate().plusDays(30).plusMonths(1);
+ billingApi.setChargedThroughDate(baseSubscription.getId(), ctd, context);
+
+ final DateTime transferRequestedDate = clock.getUTCNow();
+ testListener.pushExpectedEvent(NextEvent.TRANSFER);
+ transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, true, context);
+ assertTrue(testListener.isCompleted(3000));
+
+ // RETRIEVE NEW BUNDLE AND CHECK SUBSCRIPTIONS
+ SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getKey());
+ List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(newBundle.getId());
+ assertEquals(subscriptions.size(), 3);
+ boolean foundBP = false;
+ boolean foundAO1 = false;
+ boolean foundAO2 = false;
+ for (Subscription cur : subscriptions) {
+ Plan curPlan = cur.getCurrentPlan();
+ Product curProduct = curPlan.getProduct();
+ if (curProduct.getName().equals(baseProduct)) {
+ foundBP = true;
+ assertTrue(((SubscriptionData) cur).getAlignStartDate().compareTo(((SubscriptionData) baseSubscription).getAlignStartDate()) == 0);
+ assertNull(cur.getPendingTransition());
+ } else if (curProduct.getName().equals(aoProduct1)) {
+ foundAO1 = true;
+ assertTrue(((SubscriptionData) cur).getAlignStartDate().compareTo((aoSubscription1).getAlignStartDate()) == 0);
+ assertNull(cur.getPendingTransition());
+ } else if (curProduct.getName().equals(aoProduct2)) {
+ foundAO2 = true;
+ assertTrue(((SubscriptionData) cur).getAlignStartDate().compareTo((aoSubscription2).getAlignStartDate()) == 0);
+ assertNotNull(cur.getPendingTransition());
+ } else {
+ Assert.fail("Unexpected product " + curProduct.getName());
+ }
+ }
+ assertTrue(foundBP);
+ assertTrue(foundAO1);
+ assertTrue(foundAO2);
+
+ // MOVE AFTER CANCEL DATE TO TRIGGER OLD SUBSCRIPTIONS CANCELLATION + LASER_SCOPE PHASE EVENT
+ testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushExpectedEvent(NextEvent.CANCEL);
+ testListener.pushExpectedEvent(NextEvent.CANCEL);
+ testListener.pushExpectedEvent(NextEvent.CANCEL);
+ clock.addMonths(1);
+ assertTrue(testListener.isCompleted(3000));
+ }
+}
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 3479d0e..8463775 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
@@ -40,6 +40,7 @@ import com.ning.billing.entitlement.api.migration.AccountMigrationData;
import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
import com.ning.billing.entitlement.api.migration.AccountMigrationData.SubscriptionMigrationData;
import com.ning.billing.entitlement.api.timeline.SubscriptionDataRepair;
+import com.ning.billing.entitlement.api.transfer.TransferCancelData;
import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
@@ -58,6 +59,7 @@ import com.ning.billing.util.notificationq.NotificationQueue;
import com.ning.billing.util.notificationq.NotificationQueueService;
import com.ning.billing.util.notificationq.NotificationQueueService.NoSuchNotificationQueue;
+
public class MockEntitlementDaoMemory implements EntitlementDao {
protected static final Logger log = LoggerFactory.getLogger(EntitlementDao.class);
@@ -99,6 +101,18 @@ public class MockEntitlementDaoMemory implements EntitlementDao {
}
@Override
+ public List<SubscriptionBundle> getSubscriptionBundlesForKey(final String bundleKey) {
+ final List<SubscriptionBundle> results = new ArrayList<SubscriptionBundle>();
+ for (final SubscriptionBundle cur : bundles) {
+ if (cur.getKey().equals(bundleKey)) {
+ results.add(cur);
+ }
+ }
+ return results;
+ }
+
+
+ @Override
public SubscriptionBundle getSubscriptionBundleFromId(final UUID bundleId) {
for (final SubscriptionBundle cur : bundles) {
if (cur.getId().equals(bundleId)) {
@@ -109,9 +123,9 @@ public class MockEntitlementDaoMemory implements EntitlementDao {
}
@Override
- public SubscriptionBundle getSubscriptionBundleFromKey(final String bundleKey) {
+ public SubscriptionBundle getSubscriptionBundleFromAccountAndKey(final UUID accountId, final String bundleKey) {
for (final SubscriptionBundle cur : bundles) {
- if (cur.getKey().equals(bundleKey)) {
+ if (cur.getKey().equals(bundleKey) && cur.getAccountId().equals(accountId)) {
return cur;
}
}
@@ -140,10 +154,10 @@ public class MockEntitlementDaoMemory implements EntitlementDao {
}
@Override
- public List<Subscription> getSubscriptionsForKey(final SubscriptionFactory factory, final String bundleKey) {
+ public List<Subscription> getSubscriptionsForAccountAndKey(final SubscriptionFactory factory, final UUID accountId, final String bundleKey) {
for (final SubscriptionBundle cur : bundles) {
- if (cur.getKey().equals(bundleKey)) {
+ if (cur.getKey().equals(bundleKey) && cur.getAccountId().equals(bundleKey)) {
return getSubscriptions(factory, cur.getId());
}
}
@@ -418,4 +432,10 @@ public class MockEntitlementDaoMemory implements EntitlementDao {
public void repair(final UUID accountId, final UUID bundleId, final List<SubscriptionDataRepair> inRepair,
final CallContext context) {
}
+
+ @Override
+ public void transfer(UUID srcAccountId, UUID destAccountId,
+ BundleMigrationData data,
+ List<TransferCancelData> transferCancelData, CallContext context) {
+ }
}
diff --git a/entitlement/src/test/resources/entitlement.properties b/entitlement/src/test/resources/entitlement.properties
index 58f4f95..7039cc2 100644
--- a/entitlement/src/test/resources/entitlement.properties
+++ b/entitlement/src/test/resources/entitlement.properties
@@ -5,4 +5,3 @@ killbill.entitlement.engine.notifications.sleep=100
killbill.billing.util.persistent.bus.sleep=100
killbill.billing.util.persistent.bus.nbThreads=1
user.timezone=UTC
-
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
index 25eaf57..20d0320 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
@@ -47,6 +47,7 @@ import com.ning.billing.entitlement.api.timeline.BundleTimeline;
import com.ning.billing.entitlement.api.timeline.EntitlementRepairException;
import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoicePayment;
@@ -131,18 +132,26 @@ public class AccountResource extends JaxRsResourceBase {
@GET
@Path("/{accountId:" + UUID_PATTERN + "}/" + BUNDLES)
@Produces(APPLICATION_JSON)
- public Response getAccountBundles(@PathParam("accountId") final String accountId) throws AccountApiException {
+ public Response getAccountBundles(@PathParam("accountId") final String accountId, @QueryParam(QUERY_EXTERNAL_KEY) final String externalKey)
+ throws AccountApiException, EntitlementUserApiException {
+
final UUID uuid = UUID.fromString(accountId);
accountApi.getAccountById(uuid);
- final List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(uuid);
- final Collection<BundleJsonNoSubscriptions> result = Collections2.transform(bundles, new Function<SubscriptionBundle, BundleJsonNoSubscriptions>() {
- @Override
- public BundleJsonNoSubscriptions apply(final SubscriptionBundle input) {
- return new BundleJsonNoSubscriptions(input);
- }
- });
- return Response.status(Status.OK).entity(result).build();
+ if (externalKey != null) {
+ final SubscriptionBundle bundle = entitlementApi.getBundleForAccountAndKey(uuid, externalKey);
+ final BundleJsonNoSubscriptions json = new BundleJsonNoSubscriptions(bundle);
+ return Response.status(Status.OK).entity(json).build();
+ } else {
+ final List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(uuid);
+ final Collection<BundleJsonNoSubscriptions> result = Collections2.transform(bundles, new Function<SubscriptionBundle, BundleJsonNoSubscriptions>() {
+ @Override
+ public BundleJsonNoSubscriptions apply(final SubscriptionBundle input) {
+ return new BundleJsonNoSubscriptions(input);
+ }
+ });
+ return Response.status(Status.OK).entity(result).build();
+ }
}
@GET
@@ -229,7 +238,7 @@ public class AccountResource extends JaxRsResourceBase {
final List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(account.getId());
final List<BundleTimeline> bundlesTimeline = new LinkedList<BundleTimeline>();
for (final SubscriptionBundle cur : bundles) {
- bundlesTimeline.add(timelineApi.getBundleRepair(cur.getId()));
+ bundlesTimeline.add(timelineApi.getBundleTimeline(cur.getId()));
}
final AccountTimelineJson json = new AccountTimelineJson(account, invoices, payments, bundlesTimeline,
@@ -238,6 +247,10 @@ public class AccountResource extends JaxRsResourceBase {
return Response.status(Status.OK).entity(json).build();
}
+
+
+
+
/*
* ************************** EMAIL NOTIFICATIONS FOR INVOICES ********************************
*/
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
index 44e5e77..b1c3586 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
@@ -86,13 +86,6 @@ public class BundleResource extends JaxRsResourceBase {
return Response.status(Status.OK).entity(json).build();
}
- @GET
- @Produces(APPLICATION_JSON)
- public Response getBundleByKey(@QueryParam(QUERY_EXTERNAL_KEY) final String externalKey) throws EntitlementUserApiException {
- final SubscriptionBundle bundle = entitlementApi.getBundleForKey(externalKey);
- final BundleJsonNoSubscriptions json = new BundleJsonNoSubscriptions(bundle);
- return Response.status(Status.OK).entity(json).build();
- }
@POST
@Consumes(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
index 7e22ed1..932d1e2 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
@@ -41,7 +41,7 @@ public interface JaxrsResource {
/*
* Query parameters
*/
- public static final String QUERY_EXTERNAL_KEY = "external_key";
+ public static final String QUERY_EXTERNAL_KEY = "externalKey";
public static final String QUERY_REQUESTED_DT = "requestedDate";
public static final String QUERY_CALL_COMPLETION = "callCompletion";
public static final String QUERY_CALL_TIMEOUT = "callTimeoutSec";
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingEntitlementUserApi.java b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingEntitlementUserApi.java
index a2bd036..d89c64d 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingEntitlementUserApi.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingEntitlementUserApi.java
@@ -64,8 +64,8 @@ public class BlockingEntitlementUserApi implements EntitlementUserApi {
}
@Override
- public SubscriptionBundle getBundleForKey(final String bundleKey) throws EntitlementUserApiException {
- final SubscriptionBundle bundle = entitlementUserApi.getBundleForKey(bundleKey);
+ public SubscriptionBundle getBundleForAccountAndKey(final UUID accountId, final String bundleKey) throws EntitlementUserApiException {
+ final SubscriptionBundle bundle = entitlementUserApi.getBundleForAccountAndKey(accountId, bundleKey);
return new BlockingSubscriptionBundle(bundle, blockingApi);
}
@@ -80,6 +80,18 @@ public class BlockingEntitlementUserApi implements EntitlementUserApi {
}
@Override
+ public List<SubscriptionBundle> getBundlesForKey(String bundleKey)
+ throws EntitlementUserApiException {
+ final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>();
+ final List<SubscriptionBundle> bundles = entitlementUserApi.getBundlesForKey(bundleKey);
+ for (final SubscriptionBundle bundle : bundles) {
+ result.add(new BlockingSubscriptionBundle(bundle, blockingApi));
+ }
+ return result;
+ }
+
+
+ @Override
public List<Subscription> getSubscriptionsForBundle(final UUID bundleId) {
final List<Subscription> result = new ArrayList<Subscription>();
final List<Subscription> subscriptions = entitlementUserApi.getSubscriptionsForBundle(bundleId);
@@ -90,9 +102,9 @@ public class BlockingEntitlementUserApi implements EntitlementUserApi {
}
@Override
- public List<Subscription> getSubscriptionsForKey(final String bundleKey) {
+ public List<Subscription> getSubscriptionsForAccountAndKey(final UUID accountId, final String bundleKey) {
final List<Subscription> result = new ArrayList<Subscription>();
- final List<Subscription> subscriptions = entitlementUserApi.getSubscriptionsForKey(bundleKey);
+ final List<Subscription> subscriptions = entitlementUserApi.getSubscriptionsForAccountAndKey(accountId, bundleKey);
for (final Subscription subscription : subscriptions) {
result.add(new BlockingSubscription(subscription, blockingApi, checker));
}
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java
index 3a7f932..41a9015 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java
@@ -67,7 +67,7 @@ public class BlockingSubscription implements Subscription {
@Override
public boolean changePlan(final String productName, final BillingPeriod term, final String priceList, final DateTime requestedDate,
- final CallContext context) throws EntitlementUserApiException {
+ final CallContext context) throws EntitlementUserApiException {
try {
checker.checkBlockedChange(this);
} catch (BlockingApiException e) {
@@ -78,7 +78,7 @@ public class BlockingSubscription implements Subscription {
@Override
public boolean changePlanWithPolicy(final String productName, final BillingPeriod term, final String priceList,
- final DateTime requestedDate, final ActionPolicy policy, final CallContext context) throws EntitlementUserApiException {
+ final DateTime requestedDate, final ActionPolicy policy, final CallContext context) throws EntitlementUserApiException {
try {
checker.checkBlockedChange(this);
} catch (BlockingApiException e) {
@@ -89,7 +89,7 @@ public class BlockingSubscription implements Subscription {
@Override
public boolean recreate(final PlanPhaseSpecifier spec, final DateTime requestedDate, final CallContext context)
- throws EntitlementUserApiException {
+ throws EntitlementUserApiException {
return subscription.recreate(spec, requestedDate, context);
}
@@ -104,6 +104,11 @@ public class BlockingSubscription implements Subscription {
}
@Override
+ public SubscriptionSourceType getSourceType() {
+ return subscription.getSourceType();
+ }
+
+ @Override
public DateTime getStartDate() {
return subscription.getStartDate();
}
@@ -179,5 +184,4 @@ public class BlockingSubscription implements Subscription {
public Subscription getDelegateSubscription() {
return subscription;
}
-
}
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java b/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
index 962c581..749e108 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2010-2011 Ning, Inc.
*
* Ning licenses this file to you under the Apache License, version 2.0
@@ -45,7 +45,8 @@ public class TestBundle extends TestJaxrsBase {
// Retrieves by external key
final Map<String, String> queryParams = new HashMap<String, String>();
queryParams.put(JaxrsResource.QUERY_EXTERNAL_KEY, "12345");
- final Response response = doGet(JaxrsResource.BUNDLES_PATH, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+ final String uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.BUNDLES;
+ final Response response = doGet(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
final String baseJson = response.getResponseBody();
final BundleJsonNoSubscriptions objFromJson = mapper.readValue(baseJson, BundleJsonNoSubscriptions.class);
@@ -86,7 +87,8 @@ public class TestBundle extends TestJaxrsBase {
// Retrieves by external key
final Map<String, String> queryParams = new HashMap<String, String>();
queryParams.put(JaxrsResource.QUERY_EXTERNAL_KEY, "56566");
- response = doGet(JaxrsResource.BUNDLES_PATH, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+ uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.BUNDLES;
+ response = doGet(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
Assert.assertEquals(response.getStatusCode(), Status.NOT_FOUND.getStatusCode());
uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.BUNDLES;
diff --git a/util/src/test/java/com/ning/billing/api/TestApiListener.java b/util/src/test/java/com/ning/billing/api/TestApiListener.java
index 2425525..841cffb 100644
--- a/util/src/test/java/com/ning/billing/api/TestApiListener.java
+++ b/util/src/test/java/com/ning/billing/api/TestApiListener.java
@@ -56,6 +56,7 @@ public class TestApiListener {
MIGRATE_ENTITLEMENT,
MIGRATE_BILLING,
CREATE,
+ TRANSFER,
RE_CREATE,
CHANGE,
CANCEL,
@@ -86,10 +87,14 @@ public class TestApiListener {
public void handleEntitlementEvents(final EffectiveSubscriptionEvent eventEffective) {
log.info(String.format("Got subscription event %s", eventEffective.toString()));
switch (eventEffective.getTransitionType()) {
- case MIGRATE_ENTITLEMENT:
- assertEqualsNicely(NextEvent.MIGRATE_ENTITLEMENT);
- notifyIfStackEmpty();
- break;
+ case TRANSFER:
+ assertEqualsNicely(NextEvent.TRANSFER);
+ notifyIfStackEmpty();
+ break;
+ case MIGRATE_ENTITLEMENT:
+ assertEqualsNicely(NextEvent.MIGRATE_ENTITLEMENT);
+ notifyIfStackEmpty();
+ break;
case MIGRATE_BILLING:
assertEqualsNicely(NextEvent.MIGRATE_BILLING);
notifyIfStackEmpty();
diff --git a/util/src/test/java/com/ning/billing/mock/api/MockEntitlementUserApi.java b/util/src/test/java/com/ning/billing/mock/api/MockEntitlementUserApi.java
index 0fdeeb1..09a1820 100644
--- a/util/src/test/java/com/ning/billing/mock/api/MockEntitlementUserApi.java
+++ b/util/src/test/java/com/ning/billing/mock/api/MockEntitlementUserApi.java
@@ -89,6 +89,12 @@ public class MockEntitlementUserApi implements EntitlementUserApi {
}
@Override
+ public List<SubscriptionBundle> getBundlesForKey(String bundleKey)
+ throws EntitlementUserApiException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public List<Subscription> getSubscriptionsForBundle(final UUID bundleId) {
throw new UnsupportedOperationException();
}
@@ -99,7 +105,7 @@ public class MockEntitlementUserApi implements EntitlementUserApi {
}
@Override
- public List<Subscription> getSubscriptionsForKey(final String bundleKey) {
+ public List<Subscription> getSubscriptionsForAccountAndKey(final UUID accountId, final String bundleKey) {
throw new UnsupportedOperationException();
}
@@ -110,7 +116,7 @@ public class MockEntitlementUserApi implements EntitlementUserApi {
}
@Override
- public SubscriptionBundle getBundleForKey(final String bundleKey) {
+ public SubscriptionBundle getBundleForAccountAndKey(final UUID accountId, final String bundleKey) {
throw new UnsupportedOperationException();
}
diff --git a/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java b/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java
index d712082..e408e14 100644
--- a/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java
+++ b/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java
@@ -23,6 +23,7 @@ import com.ning.billing.entitlement.api.EntitlementService;
import com.ning.billing.entitlement.api.billing.ChargeThruApi;
import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
+import com.ning.billing.entitlement.api.transfer.EntitlementTransferApi;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
import com.ning.billing.glue.EntitlementModule;
import com.ning.billing.util.glue.RealImplementation;
@@ -61,4 +62,10 @@ public class MockEntitlementModule extends AbstractModule implements Entitlement
public void installEntitlementTimelineApi() {
bind(EntitlementTimelineApi.class).toInstance(Mockito.mock(EntitlementTimelineApi.class));
}
+
+ @Override
+ public void installEntitlementTransferApi() {
+ bind(EntitlementTransferApi.class).toInstance(Mockito.mock(EntitlementTransferApi.class));
+
+ }
}
diff --git a/util/src/test/java/com/ning/billing/mock/MockSubscription.java b/util/src/test/java/com/ning/billing/mock/MockSubscription.java
index 013c9e0..57775aa 100644
--- a/util/src/test/java/com/ning/billing/mock/MockSubscription.java
+++ b/util/src/test/java/com/ning/billing/mock/MockSubscription.java
@@ -179,7 +179,11 @@ public class MockSubscription implements Subscription {
@Override
public DateTime getFutureEndDate() {
- // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public SubscriptionSourceType getSourceType() {
return null;
}
}