killbill-uncached
Changes
account/killbill-account.iml 1(+1 -0)
api/killbill-internal-api.iml 1(+1 -0)
beatrix/killbill-beatrix.iml 1(+1 -0)
catalog/killbill-catalog.iml 1(+1 -0)
entitlement/killbill-entitlement.iml 1(+1 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/BlockingSubscriptionBundle.java 122(+0 -122)
entitlement/src/main/java/com/ning/billing/entitlement/api/BlockingSubscriptionUserApi.java 159(+0 -159)
entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java 24(+5 -19)
invoice/killbill-invoice.iml 1(+1 -0)
jaxrs/killbill-jaxrs.iml 1(+1 -0)
junction/killbill-junction.iml 1(+1 -0)
osgi/killbill-osgi.iml 1(+1 -0)
overdue/killbill-overdue.iml 1(+1 -0)
payment/killbill-payment.iml 1(+1 -0)
server/killbill-server.iml 1(+1 -0)
subscription/src/main/java/com/ning/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java 178(+173 -5)
tenant/killbill-tenant.iml 1(+1 -0)
usage/killbill-usage.iml 1(+1 -0)
util/killbill-util.iml 1(+1 -0)
Details
account/killbill-account.iml 1(+1 -0)
diff --git a/account/killbill-account.iml b/account/killbill-account.iml
index ae75673..529e756 100644
--- a/account/killbill-account.iml
+++ b/account/killbill-account.iml
@@ -11,6 +11,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.1.0" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.1.0" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.1.0" level="project" />
api/killbill-internal-api.iml 1(+1 -0)
diff --git a/api/killbill-internal-api.iml b/api/killbill-internal-api.iml
index 5cba95a..1d87293 100644
--- a/api/killbill-internal-api.iml
+++ b/api/killbill-internal-api.iml
@@ -9,6 +9,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.code.findbugs:jsr305:1.3.9" level="project" />
<orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.1.0" level="project" />
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 29ac72f..5e686af 100644
--- a/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
+++ b/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
@@ -17,11 +17,6 @@
package com.ning.billing.glue;
public interface EntitlementModule {
- public void installAccountUserApi();
-
- public void installSubscriptionUserApi();
-
-
public void installBlockingStateDao();
public void installBlockingApi();
beatrix/killbill-beatrix.iml 1(+1 -0)
diff --git a/beatrix/killbill-beatrix.iml b/beatrix/killbill-beatrix.iml
index c296529..053b292 100644
--- a/beatrix/killbill-beatrix.iml
+++ b/beatrix/killbill-beatrix.iml
@@ -12,6 +12,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.guava:guava:14.0.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.inject:guice:3.0" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: javax.inject:javax.inject:1" level="project" />
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
index 2a516f6..66b5fbd 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
@@ -59,7 +59,6 @@ import com.ning.billing.catalog.api.Currency;
import com.ning.billing.catalog.api.PlanPhaseSpecifier;
import com.ning.billing.catalog.api.PriceListSet;
import com.ning.billing.catalog.api.ProductCategory;
-import com.ning.billing.entitlement.api.BlockingSubscription;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.api.InvoicePayment;
@@ -320,7 +319,8 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
}
protected SubscriptionData subscriptionDataFromSubscription(final Subscription sub) {
- return (SubscriptionData) ((BlockingSubscription) sub).getDelegateSubscription();
+ // STEPH_ENT
+ return (SubscriptionData) sub;
}
protected Account createAccountWithOsgiPaymentMethod(final AccountData accountData) throws Exception {
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/util/SubscriptionChecker.java b/beatrix/src/test/java/com/ning/billing/beatrix/util/SubscriptionChecker.java
index 7db4b52..959d58e 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/util/SubscriptionChecker.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/util/SubscriptionChecker.java
@@ -16,10 +16,8 @@
package com.ning.billing.beatrix.util;
-import com.ning.billing.entitlement.api.BlockingSubscription;
import com.ning.billing.subscription.api.user.Subscription;
import com.ning.billing.subscription.api.user.SubscriptionBundle;
-import com.ning.billing.subscription.api.user.SubscriptionData;
import com.ning.billing.subscription.api.user.SubscriptionTransition;
import com.ning.billing.subscription.api.user.SubscriptionTransitionData;
import com.ning.billing.subscription.api.user.SubscriptionUserApi;
@@ -75,7 +73,8 @@ public class SubscriptionChecker {
}
private List<SubscriptionTransition> getSubscriptionEvents(final Subscription subscription) {
- return ((SubscriptionData) ((BlockingSubscription) subscription).getDelegateSubscription()).getAllTransitions();
+ // STEPH_ENT
+ return subscription.getAllTransitions();
}
catalog/killbill-catalog.iml 1(+1 -0)
diff --git a/catalog/killbill-catalog.iml b/catalog/killbill-catalog.iml
index 791ac91..2b23402 100644
--- a/catalog/killbill-catalog.iml
+++ b/catalog/killbill-catalog.iml
@@ -12,6 +12,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.code.findbugs:jsr305:1.3.9" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.guava:guava:14.0.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.inject:guice:3.0" level="project" />
entitlement/killbill-entitlement.iml 1(+1 -0)
diff --git a/entitlement/killbill-entitlement.iml b/entitlement/killbill-entitlement.iml
index 6a94abc..6c55f74 100644
--- a/entitlement/killbill-entitlement.iml
+++ b/entitlement/killbill-entitlement.iml
@@ -12,6 +12,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.1.0" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.code.findbugs:jsr305:1.3.9" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.guava:guava:14.0.1" level="project" />
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
index aa2a545..c02dd14 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
@@ -24,6 +24,7 @@ import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.catalog.api.ActionPolicy;
import com.ning.billing.catalog.api.BillingPeriod;
import com.ning.billing.clock.Clock;
+import com.ning.billing.entitlement.block.BlockingChecker;
import com.ning.billing.subscription.api.user.Subscription;
import com.ning.billing.subscription.api.user.SubscriptionUserApiException;
import com.ning.billing.util.callcontext.CallContext;
@@ -38,16 +39,17 @@ public class DefaultEntitlement implements Entitlement {
private final Subscription subscription;
private final InternalCallContextFactory internalCallContextFactory;
private final Clock clock;
+ private final BlockingChecker checker;
- public DefaultEntitlement(final AccountInternalApi accountApi, final Subscription subscription, final InternalCallContextFactory internalCallContextFactory, final Clock clock) {
+ public DefaultEntitlement(final AccountInternalApi accountApi, final Subscription subscription, final InternalCallContextFactory internalCallContextFactory, final Clock clock, final BlockingChecker checker) {
this.accountApi = accountApi;
this.subscription = subscription;
this.internalCallContextFactory = internalCallContextFactory;
this.clock = clock;
+ this.checker = checker;
}
-
@Override
public boolean cancelEntitlementWithDate(final LocalDate localDate, final CallContext callContext) throws EntitlementApiException {
@@ -81,8 +83,18 @@ public class DefaultEntitlement implements Entitlement {
}
@Override
- public boolean changePlan(final String s, final BillingPeriod billingPeriod, final String s2, final LocalDate localDate, final CallContext callContext) throws EntitlementApiException {
- return false;
+ public boolean changePlan(final String productName, final BillingPeriod billingPeriod, final String priceList, final LocalDate localDate, final CallContext callContext) throws EntitlementApiException {
+
+ final InternalCallContext context = internalCallContextFactory.createInternalCallContext(callContext);
+ final DateTime requestedDate = fromLocalDateAndReferenceTime(localDate, subscription.getStartDate(), clock, context);
+ try {
+ checker.checkBlockedChange(subscription, context);
+ return subscription.changePlan(productName, billingPeriod, priceList, requestedDate, callContext);
+ } catch (BlockingApiException e) {
+ throw new EntitlementApiException(e, e.getCode(), e.getMessage());
+ } catch (SubscriptionUserApiException e) {
+ throw new EntitlementApiException(e);
+ }
}
@Override
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java
index e05385b..e3ccde1 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java
@@ -16,27 +16,144 @@
package com.ning.billing.entitlement.api;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.clock.Clock;
+import com.ning.billing.entitlement.block.BlockingChecker;
import com.ning.billing.entitlement.dao.BlockingStateDao;
+import com.ning.billing.subscription.api.user.Subscription;
+import com.ning.billing.subscription.api.user.SubscriptionBundle;
+import com.ning.billing.subscription.api.user.SubscriptionState;
+import com.ning.billing.subscription.api.user.SubscriptionUserApiException;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.InternalCallContext;
import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.callcontext.InternalTenantContext;
import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.svcapi.account.AccountInternalApi;
+import com.ning.billing.util.svcapi.subscription.SubscriptionInternalApi;
-import javax.inject.Inject;
-import java.util.List;
-import java.util.UUID;
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
public class DefaultEntitlementApi implements EntitlementApi {
private final BlockingStateDao dao;
+ private final SubscriptionInternalApi subscriptionInternalApi;
+ private final AccountInternalApi accountApi;
+ private final Clock clock;
private final InternalCallContextFactory internalCallContextFactory;
+ private final BlockingChecker checker;
@Inject
- public DefaultEntitlementApi(final BlockingStateDao dao, final InternalCallContextFactory internalCallContextFactory) {
+ public DefaultEntitlementApi(final BlockingStateDao dao, final InternalCallContextFactory internalCallContextFactory, final SubscriptionInternalApi subscriptionInternalApi, final AccountInternalApi accountApi, final Clock clock, final BlockingChecker checker) {
this.dao = dao;
this.internalCallContextFactory = internalCallContextFactory;
+ this.subscriptionInternalApi = subscriptionInternalApi;
+ this.accountApi = accountApi;
+ this.clock = clock;
+ this.checker = checker;
+ }
+
+
+ @Override
+ public Entitlement createBaseEntitlement(final UUID accountId, final PlanPhaseSpecifier planPhaseSpecifier, final String externalKey, final CallContext callContext) throws EntitlementApiException {
+ final InternalCallContext context = internalCallContextFactory.createInternalCallContext(callContext);
+ try {
+ final SubscriptionBundle bundle = subscriptionInternalApi.createBundleForAccount(accountId, externalKey, context);
+ final Subscription subscription = subscriptionInternalApi.createSubscription(bundle.getId(), planPhaseSpecifier, clock.getUTCNow(), context);
+ return new DefaultEntitlement(accountApi, subscription, internalCallContextFactory, clock, checker);
+ } catch (SubscriptionUserApiException e) {
+ throw new EntitlementApiException(e);
+ }
+ }
+
+ @Override
+ public Entitlement addEntitlement(final UUID baseSubscriptionId, final PlanPhaseSpecifier planPhaseSpecifier, final CallContext callContext) throws EntitlementApiException {
+ final InternalCallContext context = internalCallContextFactory.createInternalCallContext(callContext);
+ try {
+ final Subscription baseSubscription = subscriptionInternalApi.getSubscriptionFromId(baseSubscriptionId, context);
+ if (baseSubscription.getCategory() != ProductCategory.BASE ||
+ baseSubscription.getState() != SubscriptionState.ACTIVE) {
+ throw new EntitlementApiException(new SubscriptionUserApiException(ErrorCode.SUB_GET_NO_SUCH_BASE_SUBSCRIPTION, baseSubscription.getBundleId()));
+ }
+ final Subscription subscription = subscriptionInternalApi.createSubscription(baseSubscription.getBundleId(), planPhaseSpecifier, clock.getUTCNow(), context);
+ return new DefaultEntitlement(accountApi, subscription, internalCallContextFactory, clock, checker);
+ } catch (SubscriptionUserApiException e) {
+ throw new EntitlementApiException(e);
+ }
+ }
+
+ @Override
+ public Entitlement getEntitlementFromId(final UUID uuid, final TenantContext tenantContext) throws EntitlementApiException {
+ final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(tenantContext);
+ try {
+ final Subscription subscription = subscriptionInternalApi.getSubscriptionFromId(uuid, context);
+ return new DefaultEntitlement(accountApi, subscription, internalCallContextFactory, clock, checker);
+ } catch (SubscriptionUserApiException e) {
+ throw new EntitlementApiException(e);
+ }
+ }
+
+ @Override
+ public List<Entitlement> getAllEntitlementFromBaseId(final UUID baseSubscriptionId, final TenantContext tenantContext) throws EntitlementApiException {
+ final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(tenantContext);
+ try {
+ final Subscription baseSubscription = subscriptionInternalApi.getSubscriptionFromId(baseSubscriptionId, context);
+ return getAllEntitlementFromBundleId(baseSubscription.getBundleId(), context);
+ } catch (SubscriptionUserApiException e) {
+ throw new EntitlementApiException(e);
+ }
+ }
+
+ @Override
+ public List<Entitlement> getAllEntitlementForAccountIdAndExternalKey(final UUID accountId, final String externalKey, final TenantContext tenantContext) throws EntitlementApiException {
+ final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(tenantContext);
+
+ try {
+ final SubscriptionBundle bundle = subscriptionInternalApi.getBundleForAccountAndKey(accountId, externalKey, context);
+ return getAllEntitlementFromBundleId(bundle.getId(), context);
+ } catch (SubscriptionUserApiException e) {
+ throw new EntitlementApiException(e);
+ }
+ }
+
+
+ private List<Entitlement> getAllEntitlementFromBundleId(final UUID bundleId, final InternalTenantContext context) throws EntitlementApiException {
+ final List<Subscription> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(bundleId, context);
+ return ImmutableList.<Entitlement>copyOf(Collections2.transform(subscriptions, new Function<Subscription, Entitlement>() {
+ @Nullable
+ @Override
+ public Entitlement apply(@Nullable final Subscription input) {
+ return new DefaultEntitlement(accountApi, input, internalCallContextFactory, clock, checker);
+ }
+ }));
}
@Override
+ public List<Entitlement> getAllEntitlementFromAccountId(final UUID accountId, final TenantContext tenantContext) throws EntitlementApiException {
+
+ final List<Entitlement> result = new LinkedList<Entitlement>();
+ final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(tenantContext);
+ final List<SubscriptionBundle> bundles = subscriptionInternalApi.getBundlesForAccount(accountId, context);
+ for (final SubscriptionBundle bundle : bundles) {
+ final List<Entitlement> entitlements = getAllEntitlementFromBundleId(bundle.getId(), context);
+ result.addAll(entitlements);
+ }
+ return result;
+ }
+
+ @Override
public List<BlockingState> getBlockingHistory(final UUID overdueableId, final TenantContext context) {
return dao.getBlockingHistoryFor(overdueableId, internalCallContextFactory.createInternalTenantContext(context));
}
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 991c545..6de4ac7 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
@@ -16,21 +16,19 @@
package com.ning.billing.entitlement.glue;
-import com.google.inject.AbstractModule;
-import com.ning.billing.account.api.AccountUserApi;
-import com.ning.billing.entitlement.api.BlockingAccountUserApi;
-import com.ning.billing.entitlement.api.BlockingSubscriptionUserApi;
+import org.skife.config.ConfigSource;
+
import com.ning.billing.entitlement.api.DefaultEntitlementApi;
+import com.ning.billing.entitlement.api.EntitlementApi;
import com.ning.billing.entitlement.api.svcs.DefaultInternalBlockingApi;
import com.ning.billing.entitlement.block.BlockingChecker;
import com.ning.billing.entitlement.block.DefaultBlockingChecker;
import com.ning.billing.entitlement.dao.BlockingStateDao;
import com.ning.billing.entitlement.dao.DefaultBlockingStateDao;
import com.ning.billing.glue.EntitlementModule;
-import com.ning.billing.entitlement.api.EntitlementApi;
-import com.ning.billing.subscription.api.user.SubscriptionUserApi;
import com.ning.billing.util.svcapi.junction.BlockingInternalApi;
-import org.skife.config.ConfigSource;
+
+import com.google.inject.AbstractModule;
public class DefaultEntitlementModule extends AbstractModule implements EntitlementModule {
@@ -40,8 +38,6 @@ public class DefaultEntitlementModule extends AbstractModule implements Entitlem
@Override
protected void configure() {
- installAccountUserApi();
- installSubscriptionUserApi();
installBlockingStateDao();
installBlockingApi();
installEntitlementApi();
@@ -58,16 +54,6 @@ public class DefaultEntitlementModule extends AbstractModule implements Entitlem
bind(BlockingInternalApi.class).to(DefaultInternalBlockingApi.class).asEagerSingleton();
}
- @Override
- public void installAccountUserApi() {
- bind(AccountUserApi.class).to(BlockingAccountUserApi.class).asEagerSingleton();
- }
-
-
- @Override
- public void installSubscriptionUserApi() {
- bind(SubscriptionUserApi.class).to(BlockingSubscriptionUserApi.class).asEagerSingleton();
- }
@Override
public void installEntitlementApi() {
invoice/killbill-invoice.iml 1(+1 -0)
diff --git a/invoice/killbill-invoice.iml b/invoice/killbill-invoice.iml
index 01944d4..64752f9 100644
--- a/invoice/killbill-invoice.iml
+++ b/invoice/killbill-invoice.iml
@@ -12,6 +12,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.1.0" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.code.findbugs:jsr305:1.3.9" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.guava:guava:14.0.1" level="project" />
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
index ccb4c4e..2d85364 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
@@ -29,9 +29,7 @@ import java.util.UUID;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
-import org.joda.time.LocalTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -73,6 +71,7 @@ import com.ning.billing.util.svcapi.account.AccountInternalApi;
import com.ning.billing.util.svcapi.subscription.SubscriptionInternalApi;
import com.ning.billing.util.svcapi.junction.BillingEventSet;
import com.ning.billing.util.svcapi.junction.BillingInternalApi;
+import com.ning.billing.util.timezone.DateAndTimeZoneContext;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
@@ -328,40 +327,4 @@ public class InvoiceDispatcher {
}
}
- final static class DateAndTimeZoneContext {
-
- private final LocalTime referenceTime;
- private final DateTimeZone accountTimeZone;
- private final Clock clock;
-
- public DateAndTimeZoneContext(final DateTime effectiveDateTime, final DateTimeZone accountTimeZone, final Clock clock) {
- this.clock = clock;
- this.referenceTime = effectiveDateTime != null ? effectiveDateTime.toLocalTime() : null;
- this.accountTimeZone = accountTimeZone;
- }
-
- public LocalDate computeTargetDate(final DateTime targetDateTime) {
- return new LocalDate(targetDateTime, accountTimeZone);
- }
-
- public DateTime computeUTCDateTimeFromLocalDate(final LocalDate invoiceItemEndDate) {
- //
- // Since we create the targetDate for next invoice using the date from the notificationQ, we need to make sure
- // that this datetime once transformed into a LocalDate points to the correct day.
- //
- // e.g If accountTimeZone is -8 and we want to invoice on the 16, with a toDateTimeAtCurrentTime = 00:00:23,
- // we will generate a datetime that is 16T08:00:23 => LocalDate in that timeZone stays on the 16.
- //
- //
- // We use clock.getUTCNow() to get the offset with account timezone but that may not be correct
- // when we transition from standard time and daylight saving time. We could end up with a result
- // that is slightly in advance and therefore results in a null invoice.
- // We will fix that by re-inserting ourselves in the notificationQ if we detect that there is no invoice
- // and yet the subscription is recurring and not cancelled.
- //
- final int utcOffest = accountTimeZone.getOffset(clock.getUTCNow());
- final int localToUTCOffest = -1 * utcOffest;
- return invoiceItemEndDate.toDateTime(referenceTime, DateTimeZone.UTC).plusMillis(localToUTCOffest);
- }
- }
}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
index 467cf0a..d2dbca6 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
@@ -41,7 +41,7 @@ import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
import com.ning.billing.subscription.api.SubscriptionTransitionType;
import com.ning.billing.subscription.api.user.Subscription;
-import com.ning.billing.invoice.InvoiceDispatcher.DateAndTimeZoneContext;
+import com.ning.billing.util.timezone.DateAndTimeZoneContext;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.api.InvoiceItem;
@@ -196,7 +196,7 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
((ClockMock) clock).setTime(new DateTime(2012, 10, 26, 1, 12, 23, DateTimeZone.UTC));
- final InvoiceDispatcher.DateAndTimeZoneContext dateAndTimeZoneContext = new DateAndTimeZoneContext(clock.getUTCNow(), DateTimeZone.forID("Pacific/Pitcairn"), clock);
+ final DateAndTimeZoneContext dateAndTimeZoneContext = new DateAndTimeZoneContext(clock.getUTCNow(), DateTimeZone.forID("Pacific/Pitcairn"), clock);
final InvoiceItemModelDao item = new InvoiceItemModelDao(UUID.randomUUID(), clock.getUTCNow(), InvoiceItemType.RECURRING, UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(),
"planName", "phaseName", startDate, endDate, new BigDecimal("23.9"), new BigDecimal("23.9"), Currency.EUR, null);
jaxrs/killbill-jaxrs.iml 1(+1 -0)
diff --git a/jaxrs/killbill-jaxrs.iml b/jaxrs/killbill-jaxrs.iml
index 3891ad4..3642870 100644
--- a/jaxrs/killbill-jaxrs.iml
+++ b/jaxrs/killbill-jaxrs.iml
@@ -11,6 +11,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.code.findbugs:jsr305:1.3.9" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.guava:guava:14.0.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.inject:guice:3.0" level="project" />
junction/killbill-junction.iml 1(+1 -0)
diff --git a/junction/killbill-junction.iml b/junction/killbill-junction.iml
index 53d4625..9272fcc 100644
--- a/junction/killbill-junction.iml
+++ b/junction/killbill-junction.iml
@@ -12,6 +12,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.code.findbugs:jsr305:1.3.9" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.guava:guava:14.0.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.inject:guice:3.0" level="project" />
osgi/killbill-osgi.iml 1(+1 -0)
diff --git a/osgi/killbill-osgi.iml b/osgi/killbill-osgi.iml
index e0c8736..9a77311 100644
--- a/osgi/killbill-osgi.iml
+++ b/osgi/killbill-osgi.iml
@@ -11,6 +11,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.guava:guava:14.0.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.inject:guice:3.0" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: javax.inject:javax.inject:1" level="project" />
diff --git a/osgi-bundles/bundles/jruby/killbill-osgi-bundles-jruby.iml b/osgi-bundles/bundles/jruby/killbill-osgi-bundles-jruby.iml
index 82f2b79..da99a24 100644
--- a/osgi-bundles/bundles/jruby/killbill-osgi-bundles-jruby.iml
+++ b/osgi-bundles/bundles/jruby/killbill-osgi-bundles-jruby.iml
@@ -9,6 +9,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: com.google.guava:guava:14.0.1" level="project" />
<orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.1.0" level="project" />
diff --git a/osgi-bundles/defaultbundles/killbill-osgi-bundles-defaultbundles.iml b/osgi-bundles/defaultbundles/killbill-osgi-bundles-defaultbundles.iml
index e2399bf..cfc4020 100644
--- a/osgi-bundles/defaultbundles/killbill-osgi-bundles-defaultbundles.iml
+++ b/osgi-bundles/defaultbundles/killbill-osgi-bundles-defaultbundles.iml
@@ -8,6 +8,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: com.ning.billing:killbill-osgi-bundles-analytics:0.3.3" level="project" />
<orderEntry type="module" module-name="killbill-osgi-bundles-jruby" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.guava:guava:14.0.1" level="project" />
diff --git a/osgi-bundles/libs/killbill/killbill-osgi-bundles-lib-killbill.iml b/osgi-bundles/libs/killbill/killbill-osgi-bundles-lib-killbill.iml
index 66797cd..363bf73 100644
--- a/osgi-bundles/libs/killbill/killbill-osgi-bundles-lib-killbill.iml
+++ b/osgi-bundles/libs/killbill/killbill-osgi-bundles-lib-killbill.iml
@@ -12,6 +12,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: com.google.code.findbugs:jsr305:1.3.9" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.1.0" level="project" />
diff --git a/osgi-bundles/tests/beatrix/killbill-osgi-bundles-test-beatrix.iml b/osgi-bundles/tests/beatrix/killbill-osgi-bundles-test-beatrix.iml
index 2efd2ba..a2aa6ce 100644
--- a/osgi-bundles/tests/beatrix/killbill-osgi-bundles-test-beatrix.iml
+++ b/osgi-bundles/tests/beatrix/killbill-osgi-bundles-test-beatrix.iml
@@ -13,6 +13,7 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.1.0" level="project" />
<orderEntry type="library" name="Maven: joda-time:joda-time:2.0" level="project" />
<orderEntry type="module" module-name="killbill-osgi-bundles-lib-killbill" />
diff --git a/osgi-bundles/tests/payment/killbill-osgi-bundles-test-payment.iml b/osgi-bundles/tests/payment/killbill-osgi-bundles-test-payment.iml
index ed68635..a965893 100644
--- a/osgi-bundles/tests/payment/killbill-osgi-bundles-test-payment.iml
+++ b/osgi-bundles/tests/payment/killbill-osgi-bundles-test-payment.iml
@@ -12,6 +12,7 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.1.0" level="project" />
<orderEntry type="library" name="Maven: joda-time:joda-time:2.0" level="project" />
<orderEntry type="module" module-name="killbill-osgi-bundles-lib-killbill" />
overdue/killbill-overdue.iml 1(+1 -0)
diff --git a/overdue/killbill-overdue.iml b/overdue/killbill-overdue.iml
index a05d8c5..cfb0e8a 100644
--- a/overdue/killbill-overdue.iml
+++ b/overdue/killbill-overdue.iml
@@ -11,6 +11,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.code.findbugs:jsr305:1.3.9" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.guava:guava:14.0.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.inject:guice:3.0" level="project" />
payment/killbill-payment.iml 1(+1 -0)
diff --git a/payment/killbill-payment.iml b/payment/killbill-payment.iml
index d0831dc..a07337d 100644
--- a/payment/killbill-payment.iml
+++ b/payment/killbill-payment.iml
@@ -12,6 +12,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.code.findbugs:jsr305:1.3.9" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.guava:guava:14.0.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.inject:guice:3.0" level="project" />
server/killbill-server.iml 1(+1 -0)
diff --git a/server/killbill-server.iml b/server/killbill-server.iml
index 81dfb17..be0f59e 100644
--- a/server/killbill-server.iml
+++ b/server/killbill-server.iml
@@ -12,6 +12,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: ch.qos.logback:logback-classic:1.0.1" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: ch.qos.logback:logback-core:1.0.1" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.5" level="project" />
diff --git a/subscription/killbill-subscription.iml b/subscription/killbill-subscription.iml
index 1dee000..b78690f 100644
--- a/subscription/killbill-subscription.iml
+++ b/subscription/killbill-subscription.iml
@@ -12,6 +12,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.1.0" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.code.findbugs:jsr305:1.3.9" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.guava:guava:14.0.1" level="project" />
diff --git a/subscription/src/main/java/com/ning/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java b/subscription/src/main/java/com/ning/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
index 0d3a128..5729a38 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
@@ -16,6 +16,7 @@
package com.ning.billing.subscription.api.svcs;
+import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
@@ -26,19 +27,34 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.PriceListSet;
+import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.clock.Clock;
+import com.ning.billing.clock.DefaultClock;
import com.ning.billing.subscription.api.SubscriptionApiBase;
import com.ning.billing.subscription.api.user.DefaultEffectiveSubscriptionEvent;
import com.ning.billing.subscription.api.user.DefaultSubscriptionApiService;
-import com.ning.billing.subscription.api.user.SubscriptionBuilder;
-import com.ning.billing.subscription.api.user.SubscriptionData;
-import com.ning.billing.subscription.api.user.SubscriptionTransitionData;
-import com.ning.billing.subscription.engine.dao.SubscriptionDao;
+import com.ning.billing.subscription.api.user.DefaultSubscriptionStatusDryRun;
import com.ning.billing.subscription.api.user.Subscription;
+import com.ning.billing.subscription.api.user.SubscriptionBuilder;
import com.ning.billing.subscription.api.user.SubscriptionBundle;
+import com.ning.billing.subscription.api.user.SubscriptionBundleData;
+import com.ning.billing.subscription.api.user.SubscriptionData;
+import com.ning.billing.subscription.api.user.SubscriptionState;
+import com.ning.billing.subscription.api.user.SubscriptionStatusDryRun;
+import com.ning.billing.subscription.api.user.SubscriptionStatusDryRun.DryRunChangeReason;
import com.ning.billing.subscription.api.user.SubscriptionTransition;
+import com.ning.billing.subscription.api.user.SubscriptionTransitionData;
import com.ning.billing.subscription.api.user.SubscriptionUserApiException;
+import com.ning.billing.subscription.engine.addon.AddonUtils;
+import com.ning.billing.subscription.engine.dao.SubscriptionDao;
+import com.ning.billing.subscription.exceptions.SubscriptionError;
import com.ning.billing.util.callcontext.InternalCallContext;
import com.ning.billing.util.callcontext.InternalTenantContext;
import com.ning.billing.util.events.EffectiveSubscriptionInternalEvent;
@@ -53,13 +69,106 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
private final Logger log = LoggerFactory.getLogger(DefaultSubscriptionInternalApi.class);
+ private final AddonUtils addonUtils;
@Inject
public DefaultSubscriptionInternalApi(final SubscriptionDao dao,
final DefaultSubscriptionApiService apiService,
final Clock clock,
- final CatalogService catalogService) {
+ final CatalogService catalogService,
+ final AddonUtils addonUtils) {
super(dao, apiService, clock, catalogService);
+ this.addonUtils = addonUtils;
+ }
+
+ @Override
+ public Subscription createSubscription(final UUID bundleId, final PlanPhaseSpecifier spec, final DateTime requestedDateWithMs, final InternalCallContext context) throws SubscriptionUserApiException {
+ try {
+ final String realPriceList = (spec.getPriceListName() == null) ? PriceListSet.DEFAULT_PRICELIST_NAME : spec.getPriceListName();
+ final DateTime now = clock.getUTCNow();
+ final DateTime requestedDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
+ if (requestedDate.isAfter(now)) {
+ throw new SubscriptionUserApiException(ErrorCode.SUB_INVALID_REQUESTED_DATE, now.toString(), requestedDate.toString());
+ }
+ final DateTime effectiveDate = requestedDate;
+
+ final Catalog catalog = catalogService.getFullCatalog();
+ final Plan plan = catalog.findPlan(spec.getProductName(), spec.getBillingPeriod(), realPriceList, requestedDate);
+
+ final PlanPhase phase = plan.getAllPhases()[0];
+ if (phase == null) {
+ throw new SubscriptionError(String.format("No initial PlanPhase for Product %s, term %s and set %s does not exist in the catalog",
+ spec.getProductName(), spec.getBillingPeriod().toString(), realPriceList));
+ }
+
+ final SubscriptionBundle bundle = dao.getSubscriptionBundleFromId(bundleId, context);
+ if (bundle == null) {
+ throw new SubscriptionUserApiException(ErrorCode.SUB_CREATE_NO_BUNDLE, bundleId);
+ }
+
+ DateTime bundleStartDate = null;
+ final SubscriptionData baseSubscription = (SubscriptionData) dao.getBaseSubscription(bundleId, context);
+ switch (plan.getProduct().getCategory()) {
+ case BASE:
+ if (baseSubscription != null) {
+ if (baseSubscription.getState() == SubscriptionState.ACTIVE) {
+ throw new SubscriptionUserApiException(ErrorCode.SUB_CREATE_BP_EXISTS, bundleId);
+ } else {
+ // If we do create on an existing CANCELLED BP, this is equivalent to call recreate on that Subscription.
+ final Subscription recreatedSubscriptionForApiUse = createSubscriptionForApiUse(baseSubscription);
+ recreatedSubscriptionForApiUse.recreate(spec, requestedDate, context.toCallContext());
+ return recreatedSubscriptionForApiUse;
+ }
+ }
+ bundleStartDate = requestedDate;
+ break;
+ case ADD_ON:
+ if (baseSubscription == null) {
+ throw new SubscriptionUserApiException(ErrorCode.SUB_CREATE_NO_BP, bundleId);
+ }
+ if (effectiveDate.isBefore(baseSubscription.getStartDate())) {
+ throw new SubscriptionUserApiException(ErrorCode.SUB_INVALID_REQUESTED_DATE, effectiveDate.toString(), baseSubscription.getStartDate().toString());
+ }
+ addonUtils.checkAddonCreationRights(baseSubscription, plan);
+ bundleStartDate = baseSubscription.getStartDate();
+ break;
+ case STANDALONE:
+ if (baseSubscription != null) {
+ throw new SubscriptionUserApiException(ErrorCode.SUB_CREATE_BP_EXISTS, bundleId);
+ }
+ // Not really but we don't care, there is no alignment for STANDALONE subscriptions
+ bundleStartDate = requestedDate;
+ break;
+ default:
+ throw new SubscriptionError(String.format("Can't create subscription of type %s",
+ plan.getProduct().getCategory().toString()));
+ }
+
+ return apiService.createPlan(new SubscriptionBuilder()
+ .setId(UUID.randomUUID())
+ .setBundleId(bundleId)
+ .setCategory(plan.getProduct().getCategory())
+ .setBundleStartDate(bundleStartDate)
+ .setAlignStartDate(effectiveDate),
+ plan, spec.getPhaseType(), realPriceList, requestedDate, effectiveDate, now, context.toCallContext());
+ } catch (CatalogApiException e) {
+ throw new SubscriptionUserApiException(e);
+ }
+ }
+
+ @Override
+ public SubscriptionBundle createBundleForAccount(final UUID accountId, final String bundleName, final InternalCallContext context) throws SubscriptionUserApiException {
+ final SubscriptionBundleData bundle = new SubscriptionBundleData(bundleName, accountId, clock.getUTCNow());
+ return dao.createSubscriptionBundle(bundle, context);
+ }
+
+ @Override
+ public SubscriptionBundle getBundleForAccountAndKey(final UUID accountId, final String bundleKey, final InternalTenantContext context) throws SubscriptionUserApiException {
+ final SubscriptionBundle result = dao.getSubscriptionBundleFromAccountAndKey(accountId, bundleKey, context);
+ if (result == null) {
+ throw new SubscriptionUserApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_KEY, bundleKey);
+ }
+ return result;
}
@Override
@@ -132,6 +241,65 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
return convertEffectiveSubscriptionInternalEventFromSubscriptionTransitions(subscription, context, transitions);
}
+ @Override
+ public DateTime getNextBillingDate(final UUID accountId, final InternalTenantContext context) {
+ final List<SubscriptionBundle> bundles = getBundlesForAccount(accountId, context);
+ DateTime result = null;
+ for (final SubscriptionBundle bundle : bundles) {
+ final List<Subscription> subscriptions = getSubscriptionsForBundle(bundle.getId(), context);
+ for (final Subscription subscription : subscriptions) {
+ final DateTime chargedThruDate = subscription.getChargedThroughDate();
+ if (result == null ||
+ (chargedThruDate != null && chargedThruDate.isBefore(result))) {
+ result = subscription.getChargedThroughDate();
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public List<SubscriptionStatusDryRun> getDryRunChangePlanStatus(final UUID subscriptionId, @Nullable final String baseProductName, final DateTime requestedDate, final InternalTenantContext context) throws SubscriptionUserApiException {
+ final Subscription subscription = dao.getSubscriptionFromId(subscriptionId, context);
+ if (subscription == null) {
+ throw new SubscriptionUserApiException(ErrorCode.SUB_INVALID_SUBSCRIPTION_ID, subscriptionId);
+ }
+ if (subscription.getCategory() != ProductCategory.BASE) {
+ throw new SubscriptionUserApiException(ErrorCode.SUB_CHANGE_DRY_RUN_NOT_BP);
+ }
+
+ final List<SubscriptionStatusDryRun> result = new LinkedList<SubscriptionStatusDryRun>();
+
+ final List<Subscription> bundleSubscriptions = dao.getSubscriptions(subscription.getBundleId(), context);
+ for (final Subscription cur : bundleSubscriptions) {
+ if (cur.getId().equals(subscriptionId)) {
+ continue;
+ }
+
+ // If ADDON is cancelled, skip
+ if (cur.getState() == SubscriptionState.CANCELLED) {
+ continue;
+ }
+
+ final DryRunChangeReason reason;
+ // If baseProductName is null, it's a cancellation dry-run. In this case, return all addons, so they are cancelled
+ if (baseProductName != null && addonUtils.isAddonIncludedFromProdName(baseProductName, requestedDate, cur.getCurrentPlan())) {
+ reason = DryRunChangeReason.AO_INCLUDED_IN_NEW_PLAN;
+ } else if (baseProductName != null && addonUtils.isAddonAvailableFromProdName(baseProductName, requestedDate, cur.getCurrentPlan())) {
+ reason = DryRunChangeReason.AO_AVAILABLE_IN_NEW_PLAN;
+ } else {
+ reason = DryRunChangeReason.AO_NOT_AVAILABLE_IN_NEW_PLAN;
+ }
+ final SubscriptionStatusDryRun status = new DefaultSubscriptionStatusDryRun(cur.getId(),
+ cur.getCurrentPlan().getProduct().getName(),
+ cur.getCurrentPhase().getPhaseType(),
+ cur.getCurrentPlan().getBillingPeriod(),
+ cur.getCurrentPriceList().getName(), reason);
+ result.add(status);
+ }
+ return result;
+ }
+
private List<EffectiveSubscriptionInternalEvent> convertEffectiveSubscriptionInternalEventFromSubscriptionTransitions(final Subscription subscription,
final InternalTenantContext context, final List<SubscriptionTransition> transitions) {
return ImmutableList.<EffectiveSubscriptionInternalEvent>copyOf(Collections2.transform(transitions, new Function<SubscriptionTransition, EffectiveSubscriptionInternalEvent>() {
tenant/killbill-tenant.iml 1(+1 -0)
diff --git a/tenant/killbill-tenant.iml b/tenant/killbill-tenant.iml
index 53cd6d8..3ae480f 100644
--- a/tenant/killbill-tenant.iml
+++ b/tenant/killbill-tenant.iml
@@ -11,6 +11,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.code.findbugs:jsr305:1.3.9" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.guava:guava:14.0.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.inject:guice:3.0" level="project" />
usage/killbill-usage.iml 1(+1 -0)
diff --git a/usage/killbill-usage.iml b/usage/killbill-usage.iml
index 3d33b47..22324bc 100644
--- a/usage/killbill-usage.iml
+++ b/usage/killbill-usage.iml
@@ -11,6 +11,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.guava:guava:14.0.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.inject:guice:3.0" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: javax.inject:javax.inject:1" level="project" />
util/killbill-util.iml 1(+1 -0)
diff --git a/util/killbill-util.iml b/util/killbill-util.iml
index bd5ff00..5706f01 100644
--- a/util/killbill-util.iml
+++ b/util/killbill-util.iml
@@ -12,6 +12,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.3.3-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.1.0" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.1.0" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.1.0" level="project" />
diff --git a/util/src/main/java/com/ning/billing/util/svcapi/subscription/SubscriptionInternalApi.java b/util/src/main/java/com/ning/billing/util/svcapi/subscription/SubscriptionInternalApi.java
index 5a51de9..ef4d4c1 100644
--- a/util/src/main/java/com/ning/billing/util/svcapi/subscription/SubscriptionInternalApi.java
+++ b/util/src/main/java/com/ning/billing/util/svcapi/subscription/SubscriptionInternalApi.java
@@ -19,18 +19,34 @@ package com.ning.billing.util.svcapi.subscription;
import java.util.List;
import java.util.UUID;
+import javax.annotation.Nullable;
+
import org.joda.time.DateTime;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
import com.ning.billing.subscription.api.user.Subscription;
import com.ning.billing.subscription.api.user.SubscriptionBundle;
+import com.ning.billing.subscription.api.user.SubscriptionStatusDryRun;
import com.ning.billing.subscription.api.user.SubscriptionUserApiException;
+import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.InternalCallContext;
import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.callcontext.TenantContext;
import com.ning.billing.util.events.EffectiveSubscriptionInternalEvent;
public interface SubscriptionInternalApi {
+ public Subscription createSubscription(final UUID bundleId, final PlanPhaseSpecifier spec, final DateTime requestedDateWithMs,
+ final InternalCallContext context) throws SubscriptionUserApiException;
+
+
+ public SubscriptionBundle createBundleForAccount(final UUID accountId, final String bundleName, final InternalCallContext context)
+ throws SubscriptionUserApiException;
+
+ public SubscriptionBundle getBundleForAccountAndKey(final UUID accountId, final String bundleKey, final InternalTenantContext context)
+ throws SubscriptionUserApiException;
+
public List<SubscriptionBundle> getBundlesForAccount(final UUID accountId, final InternalTenantContext context);
public List<Subscription> getSubscriptionsForBundle(final UUID bundleId, final InternalTenantContext context);
@@ -48,4 +64,9 @@ public interface SubscriptionInternalApi {
public List<EffectiveSubscriptionInternalEvent> getAllTransitions(final Subscription subscription, final InternalTenantContext context);
public List<EffectiveSubscriptionInternalEvent> getBillingTransitions(final Subscription subscription, final InternalTenantContext context);
+
+ public DateTime getNextBillingDate(final UUID accountId, final InternalTenantContext context);
+
+ public List<SubscriptionStatusDryRun> getDryRunChangePlanStatus(final UUID subscriptionId, @Nullable final String baseProductName,
+ final DateTime requestedDate, final InternalTenantContext context) throws SubscriptionUserApiException;
}
diff --git a/util/src/main/java/com/ning/billing/util/timezone/DateAndTimeZoneContext.java b/util/src/main/java/com/ning/billing/util/timezone/DateAndTimeZoneContext.java
new file mode 100644
index 0000000..1aba246
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/timezone/DateAndTimeZoneContext.java
@@ -0,0 +1,50 @@
+package com.ning.billing.util.timezone;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.joda.time.LocalTime;
+
+import com.ning.billing.clock.Clock;
+
+/**
+ * Used by entitlement and invoice to calculate:
+ * - a LocalDate from DateTime and the timeZone set on the account
+ * - A DateTime from a LocalDate and the referenceTime attached to the account.
+ */
+public final class DateAndTimeZoneContext {
+
+ private final LocalTime referenceTime;
+ private final DateTimeZone accountTimeZone;
+ private final Clock clock;
+
+ public DateAndTimeZoneContext(final DateTime effectiveDateTime, final DateTimeZone accountTimeZone, final Clock clock) {
+ this.clock = clock;
+ this.referenceTime = effectiveDateTime != null ? effectiveDateTime.toLocalTime() : null;
+ this.accountTimeZone = accountTimeZone;
+ }
+
+ public LocalDate computeTargetDate(final DateTime targetDateTime) {
+ return new LocalDate(targetDateTime, accountTimeZone);
+ }
+
+ public DateTime computeUTCDateTimeFromLocalDate(final LocalDate invoiceItemEndDate) {
+ //
+ // Since we create the targetDate for next invoice using the date from the notificationQ, we need to make sure
+ // that this datetime once transformed into a LocalDate points to the correct day.
+ //
+ // e.g If accountTimeZone is -8 and we want to invoice on the 16, with a toDateTimeAtCurrentTime = 00:00:23,
+ // we will generate a datetime that is 16T08:00:23 => LocalDate in that timeZone stays on the 16.
+ //
+ //
+ // We use clock.getUTCNow() to get the offset with account timezone but that may not be correct
+ // when we transition from standard time and daylight saving time. We could end up with a result
+ // that is slightly in advance and therefore results in a null invoice.
+ // We will fix that by re-inserting ourselves in the notificationQ if we detect that there is no invoice
+ // and yet the subscription is recurring and not cancelled.
+ //
+ final int utcOffest = accountTimeZone.getOffset(clock.getUTCNow());
+ final int localToUTCOffest = -1 * utcOffest;
+ return invoiceItemEndDate.toDateTime(referenceTime, DateTimeZone.UTC).plusMillis(localToUTCOffest);
+ }
+}
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 111a0ea..fdd1c1e 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
@@ -26,31 +26,17 @@ import org.mockito.Mockito;
public class MockEntitlementModule extends AbstractModule implements EntitlementModule {
- private final AccountUserApi userApi = Mockito.mock(AccountUserApi.class);
- private final SubscriptionUserApi entUserApi = Mockito.mock(SubscriptionUserApi.class);
private final BlockingInternalApi blockingApi = Mockito.mock(BlockingInternalApi.class);
private final EntitlementApi entitlementApi = Mockito.mock(EntitlementApi.class);
@Override
protected void configure() {
- installAccountUserApi();
- installSubscriptionUserApi();
installBlockingStateDao();
installBlockingApi();
installEntitlementApi();
}
@Override
- public void installAccountUserApi() {
- bind(AccountUserApi.class).toInstance(userApi);
- }
-
- @Override
- public void installSubscriptionUserApi() {
- bind(SubscriptionUserApi.class).toInstance(entUserApi);
- }
-
- @Override
public void installBlockingStateDao() {
}