killbill-memoizeit

entitlement: refine Entitlement#getBillCycleDayLocal

12/3/2018 4:39:25 PM

Changes

Details

diff --git a/account/src/main/java/org/killbill/billing/account/api/svcs/DefaultAccountInternalApi.java b/account/src/main/java/org/killbill/billing/account/api/svcs/DefaultAccountInternalApi.java
index ba32fd5..44e8b4d 100644
--- a/account/src/main/java/org/killbill/billing/account/api/svcs/DefaultAccountInternalApi.java
+++ b/account/src/main/java/org/killbill/billing/account/api/svcs/DefaultAccountInternalApi.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2017 Groupon, Inc
- * Copyright 2014-2017 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -49,6 +49,7 @@ import org.killbill.billing.util.cache.CacheLoaderArgument;
 import org.killbill.billing.util.dao.NonEntityDao;
 
 import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
 
@@ -104,9 +105,11 @@ public class DefaultAccountInternalApi extends DefaultAccountApiBase implements 
     }
 
     @Override
-    public int getBCD(final UUID accountId, final InternalTenantContext context) throws AccountApiException {
+    public int getBCD(final InternalTenantContext context) throws AccountApiException {
         final CacheLoaderArgument arg = createBCDCacheLoaderArgument(context);
-        final Integer result = bcdCacheController.get(accountId, arg);
+        Preconditions.checkNotNull(context.getAccountRecordId(), "Context missing accountRecordId");
+        final ImmutableAccountData account = immutableAccountInternalApi.getImmutableAccountDataByRecordId(context.getAccountRecordId(), context);
+        final Integer result = bcdCacheController.get(account.getId(), arg);
         return result != null ? result : DefaultMutableAccountData.DEFAULT_BILLING_CYCLE_DAY_LOCAL;
     }
 
diff --git a/api/src/main/java/org/killbill/billing/account/api/AccountInternalApi.java b/api/src/main/java/org/killbill/billing/account/api/AccountInternalApi.java
index f935ae7..6907d07 100644
--- a/api/src/main/java/org/killbill/billing/account/api/AccountInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/account/api/AccountInternalApi.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -34,7 +34,7 @@ public interface AccountInternalApi extends ImmutableAccountInternalApi {
 
     void updateBCD(String key, int bcd, InternalCallContext context) throws AccountApiException;
 
-    int getBCD(UUID accountId, InternalTenantContext context) throws AccountApiException;
+    int getBCD(InternalTenantContext context) throws AccountApiException;
 
     List<AccountEmail> getEmails(UUID accountId, InternalTenantContext context);
 
diff --git a/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java b/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
index a62fc85..5305ac8 100644
--- a/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
+++ b/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
@@ -66,7 +66,7 @@ public interface EventsStream {
 
     boolean isBlockEntitlement(final DateTime effectiveDate);
 
-    int getDefaultBillCycleDayLocal();
+    Integer getDefaultBillCycleDayLocal();
 
     Collection<BlockingState> getPendingEntitlementCancellationEvents();
 
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBase.java b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBase.java
index a9f6125..a94da73 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBase.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBase.java
@@ -46,7 +46,7 @@ public interface SubscriptionBase extends Entity, Blockable {
     public boolean cancelWithDate(final DateTime requestedDate, final CallContext context)
             throws SubscriptionBaseApiException;
 
-    public boolean cancelWithPolicy(final BillingActionPolicy policy, int accountBillCycleDayLocal, final CallContext context)
+    public boolean cancelWithPolicy(final BillingActionPolicy policy, final CallContext context)
             throws SubscriptionBaseApiException;
 
     public boolean uncancel(final CallContext context)
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
index 4d8b6b9..562aa85 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
@@ -47,7 +47,7 @@ public interface SubscriptionBaseInternalApi {
                                                                               boolean renameCancelledBundleIfExist,
                                                                               InternalCallContext contextWithValidAccountRecordId) throws SubscriptionBaseApiException;
 
-    public void cancelBaseSubscriptions(Iterable<SubscriptionBase> subscriptions, BillingActionPolicy policy, int accountBillCycleDayLocal, InternalCallContext context) throws SubscriptionBaseApiException;
+    public void cancelBaseSubscriptions(Iterable<SubscriptionBase> subscriptions, BillingActionPolicy policy, InternalCallContext context) throws SubscriptionBaseApiException;
 
     //@VisibleForTesting
     SubscriptionBaseBundle createBundleForAccount(UUID accountId, String bundleName, boolean renameCancelledBundleIfExist, InternalCallContext context)
@@ -94,8 +94,6 @@ public interface SubscriptionBaseInternalApi {
 
     public void updateBCD(final UUID subscriptionId, final int bcd, @Nullable final LocalDate effectiveFromDate, final InternalCallContext internalCallContext) throws SubscriptionBaseApiException;
 
-    public int getDefaultBillCycleDayLocal(final Map<UUID, Integer> bcdCache, final SubscriptionBase subscription, final SubscriptionBase baseSubscription, final PlanPhaseSpecifier planPhaseSpecifier, final int accountBillCycleDayLocal, final Catalog catalog, final InternalTenantContext context) throws SubscriptionBaseApiException;
-
     public UUID getAccountIdFromBundleId(UUID bundleId, InternalTenantContext context) throws SubscriptionBaseApiException;
 
     public UUID getBundleIdFromSubscriptionId(UUID entitlementId, InternalTenantContext context) throws SubscriptionBaseApiException;
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseApiException.java b/api/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseApiException.java
index 99e98b9..3f2126e 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseApiException.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseApiException.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -18,13 +20,12 @@ package org.killbill.billing.subscription.api.user;
 
 import org.killbill.billing.BillingExceptionBase;
 import org.killbill.billing.ErrorCode;
-import org.killbill.billing.catalog.api.CatalogApiException;
 
 public class SubscriptionBaseApiException extends BillingExceptionBase {
 
     private static final long serialVersionUID = 19083233L;
 
-    public SubscriptionBaseApiException(final CatalogApiException e) {
+    public SubscriptionBaseApiException(final BillingExceptionBase e) {
         super(e, e.getCode(), e.getMessage());
     }
 
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
index 172c7cf..4e3200b 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2017 Groupon, Inc
- * Copyright 2014-2017 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -36,14 +36,10 @@ import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
-import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
-import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
-import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.DefaultEntitlementService;
-import org.killbill.billing.entitlement.EntitlementService;
 import org.killbill.billing.entitlement.EventsStream;
 import org.killbill.billing.entitlement.api.EntitlementPluginExecution.WithEntitlementPlugin;
 import org.killbill.billing.entitlement.block.BlockingChecker;
@@ -52,7 +48,6 @@ import org.killbill.billing.entitlement.engine.core.EntitlementNotificationKey;
 import org.killbill.billing.entitlement.engine.core.EntitlementNotificationKeyAction;
 import org.killbill.billing.entitlement.engine.core.EntitlementUtils;
 import org.killbill.billing.entitlement.engine.core.EventsStreamBuilder;
-import org.killbill.billing.entitlement.logging.EntitlementLoggingHelper;
 import org.killbill.billing.entitlement.plugin.api.EntitlementContext;
 import org.killbill.billing.entitlement.plugin.api.OperationType;
 import org.killbill.billing.entity.EntityBase;
@@ -498,7 +493,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
 
                 try {
                     // Cancel subscription base first, to correctly compute the add-ons entitlements we need to cancel (see below)
-                    getSubscriptionBase().cancelWithPolicy(billingPolicy, eventsStream.getDefaultBillCycleDayLocal(), callContext);
+                    getSubscriptionBase().cancelWithPolicy(billingPolicy, callContext);
                 } catch (final SubscriptionBaseApiException e) {
                     throw new EntitlementApiException(e);
                 }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
index cf0128a..d5c9e39 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2017 Groupon, Inc
- * Copyright 2014-2017 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -33,13 +33,11 @@ import javax.inject.Inject;
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
-import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.entitlement.DefaultEntitlementService;
 import org.killbill.billing.entitlement.EntitlementInternalApi;
-import org.killbill.billing.entitlement.EntitlementService;
 import org.killbill.billing.entitlement.api.BaseEntitlementWithAddOnsSpecifier;
 import org.killbill.billing.entitlement.api.BlockingState;
 import org.killbill.billing.entitlement.api.BlockingStateType;
@@ -76,7 +74,6 @@ import org.killbill.notificationq.api.NotificationQueueService;
 import org.killbill.notificationq.api.NotificationQueueService.NoSuchNotificationQueue;
 
 import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
 
 public class DefaultEntitlementInternalApi extends DefaultEntitlementApiBase implements EntitlementInternalApi {
@@ -102,14 +99,6 @@ public class DefaultEntitlementInternalApi extends DefaultEntitlementApiBase imp
             return;
         }
 
-        int bcd;
-        try {
-            bcd = accountApi.getBCD(entitlements.iterator().next().getAccountId(), internalCallContext);
-        } catch (final AccountApiException e) {
-            throw new EntitlementApiException(e);
-        }
-        Preconditions.checkState(bcd > 0, "Unexpected condition where account info could not be retrieved");
-
         final CallContext callContext = internalCallContextFactory.createCallContext(internalCallContext);
 
         final ImmutableMap.Builder<BlockingState, Optional<UUID>> blockingStates = new ImmutableMap.Builder<BlockingState, Optional<UUID>>();
@@ -155,7 +144,6 @@ public class DefaultEntitlementInternalApi extends DefaultEntitlementApiBase imp
 
         final Callable<Void> preCallbacksCallback = new BulkSubscriptionBaseCancellation(subscriptions,
                                                                                          billingPolicy,
-                                                                                         bcd,
                                                                                          internalCallContext);
 
         pluginExecution.executeWithPlugin(preCallbacksCallback, callbacks, pluginContexts);
@@ -187,23 +175,20 @@ public class DefaultEntitlementInternalApi extends DefaultEntitlementApiBase imp
 
         private final Iterable<SubscriptionBase> subscriptions;
         private final BillingActionPolicy billingPolicy;
-        private final int accountBillCycleDayLocal;
         private final InternalCallContext callContext;
 
         public BulkSubscriptionBaseCancellation(final Iterable<SubscriptionBase> subscriptions,
                                                 final BillingActionPolicy billingPolicy,
-                                                final int accountBillCycleDayLocal,
                                                 final InternalCallContext callContext) {
             this.subscriptions = subscriptions;
             this.billingPolicy = billingPolicy;
-            this.accountBillCycleDayLocal = accountBillCycleDayLocal;
             this.callContext = callContext;
         }
 
         @Override
         public Void call() throws Exception {
             try {
-                subscriptionInternalApi.cancelBaseSubscriptions(subscriptions, billingPolicy, accountBillCycleDayLocal, callContext);
+                subscriptionInternalApi.cancelBaseSubscriptions(subscriptions, billingPolicy, callContext);
             } catch (final SubscriptionBaseApiException e) {
                 throw new EntitlementApiException(e);
             }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
index daf7ff6..1caed00 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
@@ -33,7 +33,6 @@ import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
-import org.killbill.billing.entitlement.EntitlementService;
 import org.killbill.billing.entitlement.EventsStream;
 import org.killbill.billing.entitlement.api.BlockingState;
 import org.killbill.billing.entitlement.api.BlockingStateType;
@@ -72,7 +71,7 @@ public class DefaultEventsStream implements EventsStream {
     private final InternalTenantContext internalTenantContext;
     private final DateTime utcNow;
     private final LocalDate utcToday;
-    private final int defaultBillCycleDayLocal;
+    private final Integer defaultBillCycleDayLocal;
 
     private BlockingAggregator currentStateBlockingAggregator;
     private List<BlockingState> subscriptionEntitlementStates;
@@ -93,7 +92,7 @@ public class DefaultEventsStream implements EventsStream {
                                @Nullable final SubscriptionBase baseSubscription,
                                final SubscriptionBase subscription,
                                final Collection<SubscriptionBase> allSubscriptionsForBundle,
-                               final int defaultBillCycleDayLocal,
+                               @Nullable final Integer defaultBillCycleDayLocal,
                                final InternalTenantContext contextWithValidAccountRecordId, final DateTime utcNow) {
         sanityChecks(account, bundle, baseSubscription, subscription);
         this.account = account;
@@ -234,7 +233,7 @@ public class DefaultEventsStream implements EventsStream {
     }
 
     @Override
-    public int getDefaultBillCycleDayLocal() {
+    public Integer getDefaultBillCycleDayLocal() {
         return defaultBillCycleDayLocal;
     }
 
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
index 6b1991b..9f72b61 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
@@ -36,6 +36,7 @@ import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.catalog.api.BillingAlignment;
 import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.CatalogInternalApi;
@@ -58,6 +59,7 @@ import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseTransition;
+import org.killbill.billing.util.bcd.BillCycleDayCalculator;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
@@ -149,7 +151,7 @@ public class EventsStreamBuilder {
         final int accountBCD;
         try {
             account = accountInternalApi.getImmutableAccountDataByRecordId(internalTenantContext.getAccountRecordId(), internalTenantContext);
-            accountBCD = accountInternalApi.getBCD(account.getId(), internalTenantContext);
+            accountBCD = accountInternalApi.getBCD(internalTenantContext);
         } catch (final AccountApiException e) {
             throw new EntitlementApiException(e);
         }
@@ -276,7 +278,7 @@ public class EventsStreamBuilder {
                                             final InternalTenantContext internalTenantContext) throws EntitlementApiException {
         final int accountBCD;
         try {
-            accountBCD = accountInternalApi.getBCD(bundle.getAccountId(), internalTenantContext);
+            accountBCD = accountInternalApi.getBCD(internalTenantContext);
         } catch (final AccountApiException e) {
             throw new EntitlementApiException(e);
         }
@@ -397,13 +399,15 @@ public class EventsStreamBuilder {
                                              final InternalTenantContext internalTenantContext) throws EntitlementApiException {
 
         try {
-            final int defaultAlignmentDay = subscriptionInternalApi.getDefaultBillCycleDayLocal(bcdCache,
-                                                                                                subscription,
-                                                                                                baseSubscription,
-                                                                                                createPlanPhaseSpecifier(subscription),
-                                                                                                accountBCD,
-                                                                                                catalog,
-                                                                                                internalTenantContext);
+            Integer defaultAlignmentDay = null;
+            try {
+                final BillingAlignment alignment = catalog.billingAlignment(createPlanPhaseSpecifier(subscription), clock.getUTCNow(), subscription.getStartDate());
+                if (alignment != BillingAlignment.ACCOUNT || accountBCD != 0) {
+                    defaultAlignmentDay = BillCycleDayCalculator.calculateBcdForAlignment(bcdCache, subscription, baseSubscription, alignment, internalTenantContext, accountBCD);
+                }
+            } catch (final CatalogApiException e) {
+                throw new SubscriptionBaseApiException(e);
+            }
             return new DefaultEventsStream(account,
                                            bundle,
                                            blockingStates,
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
index 5e5d811..42a2890 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -29,7 +29,6 @@ import java.util.UUID;
 
 import javax.annotation.Nullable;
 
-import org.joda.time.DateTime;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountInternalApi;
@@ -143,7 +142,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
                                             final DefaultBillingEventSet result, final Set<UUID> skipSubscriptionsSet, final Catalog catalog, final List<Tag> tagsForAccount) throws AccountApiException, CatalogApiException, SubscriptionBaseApiException {
         final boolean dryRunMode = dryRunArguments != null;
 
-        final int currentAccountBCD = accountApi.getBCD(account.getId(), context);
+        final int currentAccountBCD = accountApi.getBCD(context);
 
         // In dryRun mode, when we care about invoice generated for new BASE subscription, no such bundle exists yet; we still
         // want to tap into subscriptionBase logic, so we make up a bundleId
@@ -189,7 +188,18 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
             BillingEvent oldestBillingEvent = null;
 
             for (final BillingEvent event : result) {
-                if (oldestBillingEvent == null || event.getEffectiveDate().compareTo(oldestBillingEvent.getEffectiveDate()) < 0) {
+                final BigDecimal recurringPrice = event.getRecurringPrice(event.getEffectiveDate());
+                final boolean hasRecurringPrice = recurringPrice != null; // Note: could be zero (BCD would still be set, by convention)
+                final boolean hasUsage = event.getUsages() != null && !event.getUsages().isEmpty();
+                if (!hasRecurringPrice &&
+                    !hasUsage) {
+                    // Nothing to bill, ignored for the purpose of BCD calculation
+                    continue;
+                }
+
+                if (oldestBillingEvent == null ||
+                    event.getEffectiveDate().compareTo(oldestBillingEvent.getEffectiveDate()) < 0 ||
+                    (event.getEffectiveDate().compareTo(oldestBillingEvent.getEffectiveDate()) == 0 && event.getTotalOrdering().compareTo(oldestBillingEvent.getTotalOrdering()) < 0)) {
                     oldestBillingEvent = event;
                 }
             }
@@ -203,6 +213,11 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
             if (accountBCDCandidate != 0) {
                 log.info("Setting account BCD='{}', accountId='{}'", accountBCDCandidate, account.getId());
                 accountApi.updateBCD(account.getExternalKey(), accountBCDCandidate, context);
+
+                // Because we now have computed the real BCD, we need to re-compute the BillingEvents BCD for ACCOUNT alignments (see BillCycleDayCalculator#calculateBcdForAlignment).
+                // The code could maybe be optimized (no need to re-run the full function?), but since it's run once per account, it's probably not worth it.
+                result.clear();
+                addBillingEventsForBundles(bundles, account, dryRunArguments, context, result, skipSubscriptionsSet, catalog, tagsForAccount);
             }
         }
     }
@@ -248,7 +263,10 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
 
     private int calculateBcdForTransition(final Catalog catalog, final Map<UUID, Integer> bcdCache, final SubscriptionBase baseSubscription, final SubscriptionBase subscription, final int accountBillCycleDayLocal, final EffectiveSubscriptionInternalEvent transition, final InternalTenantContext internalTenantContext)
             throws CatalogApiException {
-        final BillingAlignment alignment = catalog.billingAlignment(getPlanPhaseSpecifierFromTransition(catalog, transition), transition.getEffectiveTransitionTime(), subscription.getStartDate());
+        BillingAlignment alignment = catalog.billingAlignment(getPlanPhaseSpecifierFromTransition(catalog, transition), transition.getEffectiveTransitionTime(), subscription.getStartDate());
+        if (alignment == BillingAlignment.ACCOUNT && accountBillCycleDayLocal == 0) {
+            alignment = BillingAlignment.SUBSCRIPTION;
+        }
         return BillCycleDayCalculator.calculateBcdForAlignment(bcdCache, subscription, baseSubscription, alignment, internalTenantContext, accountBillCycleDayLocal);
     }
 
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java
index f65d823..e6e6daf 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java
@@ -40,7 +40,6 @@ import org.killbill.billing.catalog.api.InternationalPrice;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.PriceList;
-import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.entitlement.api.BlockingState;
 import org.killbill.billing.entitlement.api.BlockingStateType;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
@@ -285,7 +284,7 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
         Mockito.when(account.getTimeZone()).thenReturn(DateTimeZone.UTC);
         Mockito.when(accountInternalApi.getAccountById(Mockito.<UUID>any(), Mockito.<InternalTenantContext>any())).thenReturn(account);
         Mockito.when(accountInternalApi.getImmutableAccountDataById(Mockito.<UUID>any(), Mockito.<InternalTenantContext>any())).thenReturn(account);
-        Mockito.when(accountInternalApi.getBCD(Mockito.<UUID>any(), Mockito.<InternalTenantContext>any())).thenReturn(billCycleDay);
+        Mockito.when(accountInternalApi.getBCD(Mockito.<InternalTenantContext>any())).thenReturn(billCycleDay);
         return account;
     }
 
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
index b4a01d1..106b185 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
@@ -76,7 +76,7 @@ public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbedded
         final List<Entitlement> entitlements = entitlementApi.getAllEntitlementsForAccountId(account.getId(), callContext);
         Assert.assertEquals(entitlements.size(), 1);
         Assert.assertEquals(entitlements.get(0).getLastActiveProduct().getName(), "Cannon");
-        Assert.assertEquals(entitlements.get(0).getBillCycleDayLocal(), (Integer) 7);
+        Assert.assertNull(entitlements.get(0).getBillCycleDayLocal());
 
         // Account still has no BCD
         final Account accountNoBCD = accountApi.getAccountById(account.getId(), callContext);
@@ -125,7 +125,7 @@ public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbedded
         final List<Entitlement> entitlements = entitlementApi.getAllEntitlementsForAccountId(account.getId(), callContext);
         Assert.assertEquals(entitlements.size(), 1);
         Assert.assertEquals(entitlements.get(0).getLastActiveProduct().getName(), "Trebuchet");
-        Assert.assertEquals(entitlements.get(0).getBillCycleDayLocal(), (Integer) 7);
+        Assert.assertNull(entitlements.get(0).getBillCycleDayLocal());
 
         // Account still has no BCD
         final Account accountNoBCD = accountApi.getAccountById(account.getId(), callContext);
@@ -175,9 +175,10 @@ public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbedded
         final List<Entitlement> entitlements = entitlementApi.getAllEntitlementsForAccountId(account.getId(), callContext);
         Assert.assertEquals(entitlements.size(), 2);
         Assert.assertEquals(entitlements.get(0).getLastActiveProduct().getName(), "Shotgun");
-        Assert.assertEquals(entitlements.get(0).getBillCycleDayLocal(), (Integer) 6);
+        // See bug description at https://github.com/killbill/killbill/issues/865 and PR discussion at https://github.com/killbill/killbill/pull/1067/files/926ca68c32c8f8c93bd5eda94fba17f6e19e593d#r237981095
+        Assert.assertNull(entitlements.get(0).getBillCycleDayLocal());
         Assert.assertEquals(entitlements.get(1).getLastActiveProduct().getName(), "Cabinet");
-        Assert.assertEquals(entitlements.get(1).getBillCycleDayLocal(), (Integer) 7);
+        Assert.assertNull(entitlements.get(1).getBillCycleDayLocal());
 
         // Account still has no BCD
         final Account accountNoBCD = accountApi.getAccountById(account.getId(), callContext);
@@ -186,11 +187,7 @@ public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbedded
         List<BillingEvent> events = ImmutableList.<BillingEvent>copyOf(billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), null, internalCallContext));
         Assert.assertEquals(events.size(), 3);
         for (final BillingEvent billingEvent : events) {
-            if (billingEvent.getSubscription().getId().equals(entitlements.get(1).getId())) {
-                Assert.assertEquals(billingEvent.getBillCycleDayLocal(), 7);
-            } else {
-                Assert.assertEquals(billingEvent.getBillCycleDayLocal(), 6);
-            }
+            Assert.assertEquals(billingEvent.getBillCycleDayLocal(), 7);
         }
 
         // Verify BCD
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java b/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java
index 2b97be8..1ee196d 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java
@@ -30,8 +30,6 @@ import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanChangeResult;
-import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
-import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.entitlement.api.EntitlementSpecifier;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
@@ -52,10 +50,10 @@ public interface SubscriptionBaseApiService {
     public boolean cancelWithRequestedDate(DefaultSubscriptionBase subscription, DateTime requestedDate, CallContext context)
             throws SubscriptionBaseApiException;
 
-    public boolean cancelWithPolicy(DefaultSubscriptionBase subscription, BillingActionPolicy policy, int accountBillCycleDayLocal, CallContext context)
+    public boolean cancelWithPolicy(DefaultSubscriptionBase subscription, BillingActionPolicy policy, CallContext context)
             throws SubscriptionBaseApiException;
 
-    public boolean cancelWithPolicyNoValidationAndCatalog(Iterable<DefaultSubscriptionBase> subscriptions, BillingActionPolicy policy, int accountBillCycleDayLocal, Catalog catalog, InternalCallContext context)
+    public boolean cancelWithPolicyNoValidationAndCatalog(Iterable<DefaultSubscriptionBase> subscriptions, BillingActionPolicy policy, Catalog catalog, InternalCallContext context)
             throws SubscriptionBaseApiException;
 
     public boolean uncancel(DefaultSubscriptionBase subscription, CallContext context)
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
index 317f272..2680ba5 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
@@ -35,14 +35,11 @@ import org.killbill.billing.ErrorCode;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
-import org.killbill.billing.catalog.api.BillingAlignment;
 import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.CatalogInternalApi;
 import org.killbill.billing.catalog.api.Plan;
-import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverridesWithCallContext;
-import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
@@ -71,7 +68,6 @@ import org.killbill.billing.subscription.engine.dao.model.SubscriptionBundleMode
 import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
 import org.killbill.billing.subscription.events.bcd.BCDEvent;
 import org.killbill.billing.subscription.events.bcd.BCDEventData;
-import org.killbill.billing.util.bcd.BillCycleDayCalculator;
 import org.killbill.billing.util.cache.AccountIdFromBundleIdCacheLoader;
 import org.killbill.billing.util.cache.BundleIdFromSubscriptionIdCacheLoader;
 import org.killbill.billing.util.cache.Cachable.CacheType;
@@ -158,7 +154,7 @@ public class DefaultSubscriptionInternalApi extends DefaultSubscriptionBaseCreat
     }
 
     @Override
-    public void cancelBaseSubscriptions(final Iterable<SubscriptionBase> subscriptions, final BillingActionPolicy policy, int accountBillCycleDayLocal, final InternalCallContext context) throws SubscriptionBaseApiException {
+    public void cancelBaseSubscriptions(final Iterable<SubscriptionBase> subscriptions, final BillingActionPolicy policy, final InternalCallContext context) throws SubscriptionBaseApiException {
 
         try {
             final Catalog catalog = catalogInternalApi.getFullCatalog(true, true, context);
@@ -174,7 +170,6 @@ public class DefaultSubscriptionInternalApi extends DefaultSubscriptionBaseCreat
                                                                                                                        }
                                                                                                                    }),
                                                               policy,
-                                                              accountBillCycleDayLocal,
                                                               catalog,
                                                               context);
         } catch (CatalogApiException e) {
@@ -458,16 +453,6 @@ public class DefaultSubscriptionInternalApi extends DefaultSubscriptionBaseCreat
     }
 
     @Override
-    public int getDefaultBillCycleDayLocal(final Map<UUID, Integer> bcdCache, final SubscriptionBase subscription, final SubscriptionBase baseSubscription, final PlanPhaseSpecifier planPhaseSpecifier, final int accountBillCycleDayLocal, final Catalog catalog, final InternalTenantContext context) throws SubscriptionBaseApiException {
-        try {
-            final BillingAlignment alignment = catalog.billingAlignment(planPhaseSpecifier, clock.getUTCNow(), subscription.getStartDate());
-            return BillCycleDayCalculator.calculateBcdForAlignment(bcdCache, subscription, baseSubscription, alignment, context, accountBillCycleDayLocal);
-        } catch (final CatalogApiException e) {
-            throw new SubscriptionBaseApiException(e);
-        }
-    }
-
-    @Override
     public UUID getAccountIdFromBundleId(final UUID bundleId, final InternalTenantContext context) throws SubscriptionBaseApiException {
         final CacheLoaderArgument arg = createAccountIdFromBundleIdCacheLoaderArgument(context);
         return accountIdCacheController.get(bundleId, arg);
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
index da5ae17..fc09a99 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
@@ -39,7 +39,6 @@ import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
-import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
@@ -262,8 +261,8 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
     }
 
     @Override
-    public boolean cancelWithPolicy(final BillingActionPolicy policy, final int accountBillCycleDayLocal, final CallContext context) throws SubscriptionBaseApiException {
-        return apiService.cancelWithPolicy(this, policy, accountBillCycleDayLocal, context);
+    public boolean cancelWithPolicy(final BillingActionPolicy policy, final CallContext context) throws SubscriptionBaseApiException {
+        return apiService.cancelWithPolicy(this, policy, context);
     }
 
     @Override
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
index ac01a59..ff95732 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
@@ -32,6 +32,8 @@ import org.joda.time.DateTime;
 import org.joda.time.ReadableInstant;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
@@ -87,16 +89,22 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
 
     private final Clock clock;
     private final SubscriptionDao dao;
+    private final AccountInternalApi accountInternalApi;
     private final CatalogInternalApi catalogInternalApi;
     private final PlanAligner planAligner;
     private final AddonUtils addonUtils;
     private final InternalCallContextFactory internalCallContextFactory;
 
     @Inject
-    public DefaultSubscriptionBaseApiService(final Clock clock, final SubscriptionDao dao, final CatalogInternalApi catalogInternalApi,
-                                             final PlanAligner planAligner, final AddonUtils addonUtils,
+    public DefaultSubscriptionBaseApiService(final Clock clock,
+                                             final SubscriptionDao dao,
+                                             final AccountInternalApi accountInternalApi,
+                                             final CatalogInternalApi catalogInternalApi,
+                                             final PlanAligner planAligner,
+                                             final AddonUtils addonUtils,
                                              final InternalCallContextFactory internalCallContextFactory) {
         this.clock = clock;
+        this.accountInternalApi = accountInternalApi;
         this.catalogInternalApi = catalogInternalApi;
         this.planAligner = planAligner;
         this.dao = dao;
@@ -207,7 +215,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
     }
 
     @Override
-    public boolean cancelWithPolicy(final DefaultSubscriptionBase subscription, final BillingActionPolicy policy, int accountBillCycleDayLocal, final CallContext context) throws SubscriptionBaseApiException {
+    public boolean cancelWithPolicy(final DefaultSubscriptionBase subscription, final BillingActionPolicy policy, final CallContext context) throws SubscriptionBaseApiException {
         final EntitlementState currentState = subscription.getState();
         if (currentState == EntitlementState.CANCELLED) {
             throw new SubscriptionBaseApiException(ErrorCode.SUB_CANCEL_BAD_STATE, subscription.getId(), currentState);
@@ -217,14 +225,14 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         final Catalog fullCatalog;
         try {
             fullCatalog = catalogInternalApi.getFullCatalog(true, true, internalCallContext);
-            return cancelWithPolicyNoValidationAndCatalog(ImmutableList.<DefaultSubscriptionBase>of(subscription), policy, accountBillCycleDayLocal, fullCatalog, internalCallContext);
-        } catch (CatalogApiException e) {
+            return cancelWithPolicyNoValidationAndCatalog(ImmutableList.<DefaultSubscriptionBase>of(subscription), policy, fullCatalog, internalCallContext);
+        } catch (final CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
         }
     }
 
     @Override
-    public boolean cancelWithPolicyNoValidationAndCatalog(final Iterable<DefaultSubscriptionBase> subscriptions, final BillingActionPolicy policy, final int accountBillCycleDayLocal, final Catalog catalog, final InternalCallContext context) throws SubscriptionBaseApiException {
+    public boolean cancelWithPolicyNoValidationAndCatalog(final Iterable<DefaultSubscriptionBase> subscriptions, final BillingActionPolicy policy, final Catalog catalog, final InternalCallContext context) throws SubscriptionBaseApiException {
         final Map<DefaultSubscriptionBase, DateTime> subscriptionsWithEffectiveDate = new HashMap<DefaultSubscriptionBase, DateTime>();
 
         try {
@@ -232,12 +240,15 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
                 final BillingAlignment billingAlignment = (subscription.getState() == EntitlementState.PENDING ? null : catalog.billingAlignment(new PlanPhaseSpecifier(subscription.getLastActivePlan().getName(), subscription.getLastActivePhase().getPhaseType()),
                                                                                                                                                  clock.getUTCNow(),
                                                                                                                                                  subscription.getStartDate()));
+                final Integer accountBillCycleDayLocal = accountInternalApi.getBCD(context);
                 final DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy, billingAlignment, accountBillCycleDayLocal, context);
                 subscriptionsWithEffectiveDate.put(subscription, effectiveDate);
             }
             return doCancelPlan(subscriptionsWithEffectiveDate, catalog, context);
         } catch (final CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
+        } catch (final AccountApiException e) {
+            throw new SubscriptionBaseApiException(e);
         }
     }
 
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java
index b32981b..f1e5f39 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java
@@ -383,7 +383,7 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
         // Move ahead a bit abd cancel START_OF_TERM
         clock.addDays(5);
         testListener.pushExpectedEvent(NextEvent.CANCEL);
-        subscription.cancelWithPolicy(BillingActionPolicy.START_OF_TERM, accountData.getBillCycleDayLocal(), callContext);
+        subscription.cancelWithPolicy(BillingActionPolicy.START_OF_TERM, callContext);
         assertListenerStatus();
 
         subscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
@@ -408,7 +408,7 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
 
         // Cancel / Uncancel a few times to make sure this works and we end up on a stable state
         for (int i = 0; i < 3; i++) {
-            subscription.cancelWithPolicy(BillingActionPolicy.IMMEDIATE, -1, callContext);
+            subscription.cancelWithPolicy(BillingActionPolicy.IMMEDIATE, callContext);
 
             subscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
             assertEquals(subscription.getState(), EntitlementState.PENDING);
@@ -512,7 +512,7 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
         assertEquals(subscription.getState(), Entitlement.EntitlementState.PENDING);
         assertEquals(subscription.getStartDate().compareTo(startDate.toDateTime(accountData.getReferenceTime())), 0);
 
-        subscription.cancelWithPolicy(BillingActionPolicy.IMMEDIATE, 1, callContext);
+        subscription.cancelWithPolicy(BillingActionPolicy.IMMEDIATE, callContext);
 
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.CANCEL);
         clock.addDays(5);
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java
index 8b97f5d..96e7726 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java
@@ -146,7 +146,7 @@ public class TestUserApiError extends SubscriptionTestSuiteNoDB {
 
         subscription = subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
 
-        subscription.cancelWithPolicy(BillingActionPolicy.END_OF_TERM, -1, callContext);
+        subscription.cancelWithPolicy(BillingActionPolicy.END_OF_TERM, callContext);
         try {
             final PlanPhaseSpecifier planPhaseSpecifier = new PlanPhaseSpecifier("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
             subscription.changePlanWithDate(new DefaultEntitlementSpecifier(planPhaseSpecifier), clock.getUTCNow(), callContext);
diff --git a/util/src/main/java/org/killbill/billing/util/bcd/BillCycleDayCalculator.java b/util/src/main/java/org/killbill/billing/util/bcd/BillCycleDayCalculator.java
index d53a0a9..59e00d7 100644
--- a/util/src/main/java/org/killbill/billing/util/bcd/BillCycleDayCalculator.java
+++ b/util/src/main/java/org/killbill/billing/util/bcd/BillCycleDayCalculator.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2017 Groupon, Inc
- * Copyright 2014-2017 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -31,6 +31,8 @@ import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Preconditions;
+
 public abstract class BillCycleDayCalculator {
 
     private static final Logger log = LoggerFactory.getLogger(BillCycleDayCalculator.class);
@@ -39,7 +41,8 @@ public abstract class BillCycleDayCalculator {
         int result = 0;
         switch (alignment) {
             case ACCOUNT:
-                result = accountBillCycleDayLocal != 0 ? accountBillCycleDayLocal : calculateOrRetrieveBcdFromSubscription(bcdCache, subscription, internalTenantContext);
+                Preconditions.checkState(accountBillCycleDayLocal != 0, "Account BCD should be set at this point");
+                result = accountBillCycleDayLocal;
                 break;
             case BUNDLE:
                 result = calculateOrRetrieveBcdFromSubscription(bcdCache, baseSubscription, internalTenantContext);
diff --git a/util/src/test/java/org/killbill/billing/mock/MockSubscription.java b/util/src/test/java/org/killbill/billing/mock/MockSubscription.java
index f686884..3017228 100644
--- a/util/src/test/java/org/killbill/billing/mock/MockSubscription.java
+++ b/util/src/test/java/org/killbill/billing/mock/MockSubscription.java
@@ -26,8 +26,6 @@ import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
-import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
-import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
@@ -73,9 +71,9 @@ public class MockSubscription implements SubscriptionBase {
     }
 
     @Override
-    public boolean cancelWithPolicy(BillingActionPolicy policy, int accountBillCycleDayLocal, CallContext context)
+    public boolean cancelWithPolicy(final BillingActionPolicy policy, final CallContext context)
             throws SubscriptionBaseApiException {
-        return sub.cancelWithPolicy(policy, accountBillCycleDayLocal, context);
+        return sub.cancelWithPolicy(policy, context);
     }
 
     @Override