diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/BillCycleDayCalculator.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/BillCycleDayCalculator.java
new file mode 100644
index 0000000..eed92c6
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/BillCycleDayCalculator.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.entitlement.api.billing;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.account.api.DefaultAccount;
+import com.ning.billing.catalog.api.BillingAlignment;
+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.Product;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition.SubscriptionTransitionType;
+
+public class BillCycleDayCalculator {
+ private static final Logger log = LoggerFactory.getLogger(BillCycleDayCalculator.class);
+
+ private final AccountUserApi accountApi;
+ private final CatalogService catalogService;
+
+ @Inject
+ public BillCycleDayCalculator(final AccountUserApi accountApi, final CatalogService catalogService) {
+ super();
+ this.accountApi = accountApi;
+ this.catalogService = catalogService;
+ }
+
+ protected int calculateBcd(SubscriptionBundle bundle, Subscription subscription, final SubscriptionTransition transition, final UUID accountId) throws CatalogApiException, AccountApiException {
+ Catalog catalog = catalogService.getFullCatalog();
+ Plan plan = (transition.getTransitionType() != SubscriptionTransitionType.CANCEL) ?
+ transition.getNextPlan() : transition.getPreviousPlan();
+ Product product = plan.getProduct();
+ PlanPhase phase = (transition.getTransitionType() != SubscriptionTransitionType.CANCEL) ?
+ transition.getNextPhase() : transition.getPreviousPhase();
+
+ BillingAlignment alignment = catalog.billingAlignment(
+ new PlanPhaseSpecifier(product.getName(),
+ product.getCategory(),
+ phase.getBillingPeriod(),
+ transition.getNextPriceList(),
+ phase.getPhaseType()),
+ transition.getRequestedTransitionTime());
+ int result = -1;
+
+ Account account = accountApi.getAccountById(accountId);
+ switch (alignment) {
+ case ACCOUNT :
+ result = account.getBillCycleDay();
+
+ if(result == 0) {
+ result = calculateBcdFromSubscription(subscription, plan, account);
+ }
+ break;
+ case BUNDLE :
+ result = bundle.getStartDate().toDateTime(account.getTimeZone()).getDayOfMonth();
+ break;
+ case SUBSCRIPTION :
+ result = subscription.getStartDate().toDateTime(account.getTimeZone()).getDayOfMonth();
+ break;
+ }
+ if(result == -1) {
+ throw new CatalogApiException(ErrorCode.CAT_INVALID_BILLING_ALIGNMENT, alignment.toString());
+ }
+ return result;
+
+ }
+
+ private int calculateBcdFromSubscription(Subscription subscription, Plan plan, Account account) throws AccountApiException {
+ int result = account.getBillCycleDay();
+ if(result != 0) {
+ return result;
+ }
+ result = new DateTime(account.getTimeZone()).getDayOfMonth();
+
+ try {
+ result = billCycleDay(subscription.getStartDate(),account.getTimeZone(), plan);
+ } catch (CatalogApiException e) {
+ log.error("Unexpected catalog error encountered when updating BCD",e);
+ }
+
+
+ Account modifiedAccount = new DefaultAccount(
+ account.getId(),
+ account.getExternalKey(),
+ account.getEmail(),
+ account.getName(),
+ account.getFirstNameLength(),
+ account.getCurrency(),
+ result,
+ account.getPaymentProviderName(),
+ account.getTimeZone(),
+ account.getLocale(),
+ account.getAddress1(),
+ account.getAddress2(),
+ account.getCompanyName(),
+ account.getCity(),
+ account.getStateOrProvince(),
+ account.getCountry(),
+ account.getPostalCode(),
+ account.getPhone(),
+ account.getCreatedDate(),
+ null // Updated date will be set internally
+ );
+ accountApi.updateAccount(modifiedAccount);
+ return result;
+ }
+
+ private int billCycleDay(DateTime requestedDate, DateTimeZone timeZone,
+ Plan plan) throws CatalogApiException {
+
+ DateTime date = plan.dateOfFirstRecurringNonZeroCharge(requestedDate);
+ return date.toDateTime(timeZone).getDayOfMonth();
+
+ }
+
+}