killbill-aplcache

Details

diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceRecorder.java
index 30baa4a..554ee50 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceRecorder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceRecorder.java
@@ -16,11 +16,12 @@
 
 package com.ning.billing.analytics;
 
-import javax.inject.Inject;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
+import javax.inject.Inject;
+
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
 import org.slf4j.Logger;
@@ -89,14 +90,14 @@ public class BusinessInvoiceRecorder {
             return;
         }
 
-        log.info("Started rebuilding transitions for account id {}", accountId);
+        log.info("Started rebuilding invoices for account id {}", accountId);
         deleteInvoicesAndInvoiceItemsForAccountInTransaction(transactional, accountId);
 
         for (final Invoice invoice : invoiceApi.getInvoicesByAccount(accountId)) {
             createInvoiceInTransaction(transactional, accountKey, invoice);
         }
 
-        log.info("Finished rebuilding transitions for account id {}", accountId);
+        log.info("Finished rebuilding invoices for account id {}", accountId);
     }
 
     private void deleteInvoicesAndInvoiceItemsForAccountInTransaction(final BusinessInvoiceSqlDao transactional, final UUID accountId) {
@@ -107,10 +108,12 @@ public class BusinessInvoiceRecorder {
         for (final BusinessInvoice businessInvoice : invoicesToDelete) {
             final List<BusinessInvoiceItem> invoiceItemsForInvoice = invoiceItemSqlDao.getInvoiceItemsForInvoice(businessInvoice.getInvoiceId().toString());
             for (final BusinessInvoiceItem invoiceItemToDelete : invoiceItemsForInvoice) {
+                log.info("Deleting invoice item {}", invoiceItemToDelete.getItemId());
                 invoiceItemSqlDao.deleteInvoiceItem(invoiceItemToDelete.getItemId().toString());
             }
         }
 
+        log.info("Deleting invoices for account {}", accountId);
         transactional.deleteInvoicesForAccount(accountId.toString());
     }
 
@@ -153,6 +156,7 @@ public class BusinessInvoiceRecorder {
         account.setLastInvoiceDate(invoice.getInvoiceDate());
         account.setTotalInvoiceBalance(account.getTotalInvoiceBalance().add(invoice.getBalance()));
         account.setUpdatedDt(clock.getUTCNow());
+        log.info("Updating account {}", account);
         accountSqlDao.saveAccount(account);
     }
 
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java b/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
index 3757319..0ea8cea 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
@@ -19,6 +19,8 @@ package com.ning.billing.entitlement.api.user;
 import java.util.List;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
+
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
@@ -47,7 +49,7 @@ public interface EntitlementUserApi {
     public Subscription createSubscription(UUID bundleId, PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
             throws EntitlementUserApiException;
 
-    public List<SubscriptionStatusDryRun> getDryRunChangePlanStatus(UUID subscriptionId, String productName, DateTime requestedDate)
+    public List<SubscriptionStatusDryRun> getDryRunChangePlanStatus(UUID subscriptionId, @Nullable String productName, DateTime requestedDate)
             throws EntitlementUserApiException;
 
     public DateTime getNextBillingDate(UUID account);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
index ce96066..b8aba53 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
@@ -20,6 +20,8 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
+
 import org.joda.time.DateTime;
 
 import com.google.inject.Inject;
@@ -213,9 +215,8 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
     }
 
     @Override
-    public List<SubscriptionStatusDryRun> getDryRunChangePlanStatus(final UUID subscriptionId, final String baseProductName, final DateTime requestedDate)
+    public List<SubscriptionStatusDryRun> getDryRunChangePlanStatus(final UUID subscriptionId, @Nullable final String baseProductName, final DateTime requestedDate)
             throws EntitlementUserApiException {
-
         final Subscription subscription = dao.getSubscriptionFromId(subscriptionFactory, subscriptionId);
         if (subscription == null) {
             throw new EntitlementUserApiException(ErrorCode.ENT_INVALID_SUBSCRIPTION_ID, subscriptionId);
@@ -232,10 +233,11 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
                 continue;
             }
 
-            DryRunChangeReason reason = null;
-            if (addonUtils.isAddonIncludedFromProdName(baseProductName, requestedDate, cur.getCurrentPlan())) {
+            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 (addonUtils.isAddonAvailableFromProdName(baseProductName, requestedDate, cur.getCurrentPlan())) {
+            } 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;

junction/pom.xml 14(+8 -6)

diff --git a/junction/pom.xml b/junction/pom.xml
index 5ced827..e7a9373 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -29,6 +29,14 @@
             <artifactId>killbill-util</artifactId>
         </dependency>
         <dependency>
+            <!-- We don't really need Guava - we need jsr305. Guava uses the com.google.code.findbugs
+            implementation, so let's use the same one. But since we don't explicitly depend on it elsewhere
+            (this should be fixed), let's depend on it transitively for now -->
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>com.google.inject</groupId>
             <artifactId>guice</artifactId>
             <scope>provided</scope>
@@ -93,12 +101,6 @@
             <artifactId>mysql-connector-mxj-db-files</artifactId>
             <scope>test</scope>
         </dependency>
-        <!-- Strangely this is needed in order to run the tests in local db mode -->
-        <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
-           <scope>test</scope>
-         </dependency>
     </dependencies>
     <build>
         <plugins>
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingEntitlementUserApi.java b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingEntitlementUserApi.java
index 0b15824..a2bd036 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingEntitlementUserApi.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingEntitlementUserApi.java
@@ -20,9 +20,12 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
+
 import org.joda.time.DateTime;
 
 import com.google.inject.Inject;
+
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
@@ -97,8 +100,7 @@ public class BlockingEntitlementUserApi implements EntitlementUserApi {
     }
 
     @Override
-    public List<SubscriptionStatusDryRun> getDryRunChangePlanStatus(
-            final UUID subscriptionId, final String productName, final DateTime requestedDate)
+    public List<SubscriptionStatusDryRun> getDryRunChangePlanStatus(final UUID subscriptionId, @Nullable final String productName, final DateTime requestedDate)
             throws EntitlementUserApiException {
         return entitlementUserApi.getDryRunChangePlanStatus(subscriptionId, productName, requestedDate);
     }
diff --git a/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculatorBundle.java b/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculatorBundle.java
index 64a3968..0bde914 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculatorBundle.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculatorBundle.java
@@ -70,28 +70,38 @@ public class BillingStateCalculatorBundle extends BillingStateCalculator<Subscri
             }
             final PaymentResponse responseForLastFailedPayment = PaymentResponse.INSUFFICIENT_FUNDS; //TODO MDW
             final Tag[] tags = new Tag[]{}; //TODO MDW
-            final Product basePlanProduct = basePlan.getCurrentPlan().getProduct();
-            final BillingPeriod basePlanBillingPeriod = basePlan.getCurrentPlan().getBillingPeriod();
-            final PriceList basePlanPriceList = basePlan.getCurrentPriceList();
-            final PhaseType basePlanPhaseType = basePlan.getCurrentPhase().getPhaseType();
-
-
-            return new BillingStateBundle(
-                    id,
-                    numberOfUnpaidInvoices,
-                    unpaidInvoiceBalance,
-                    dateOfEarliestUnpaidInvoice,
-                    idOfEarliestUnpaidInvoice,
-                    responseForLastFailedPayment,
-                    tags,
-                    basePlanProduct,
-                    basePlanBillingPeriod,
-                    basePlanPriceList,
-                    basePlanPhaseType);
+
+            final Product basePlanProduct;
+            final BillingPeriod basePlanBillingPeriod;
+            final PriceList basePlanPriceList;
+            final PhaseType basePlanPhaseType;
+            if (basePlan.getCurrentPlan() == null) {
+                // The subscription has been cancelled since
+                basePlanProduct = null;
+                basePlanBillingPeriod = null;
+                basePlanPriceList = null;
+                basePlanPhaseType = null;
+            } else {
+                basePlanProduct = basePlan.getCurrentPlan().getProduct();
+                basePlanBillingPeriod = basePlan.getCurrentPlan().getBillingPeriod();
+                basePlanPriceList = basePlan.getCurrentPriceList();
+                basePlanPhaseType = basePlan.getCurrentPhase().getPhaseType();
+            }
+
+            return new BillingStateBundle(id,
+                                          numberOfUnpaidInvoices,
+                                          unpaidInvoiceBalance,
+                                          dateOfEarliestUnpaidInvoice,
+                                          idOfEarliestUnpaidInvoice,
+                                          responseForLastFailedPayment,
+                                          tags,
+                                          basePlanProduct,
+                                          basePlanBillingPeriod,
+                                          basePlanPriceList,
+                                          basePlanPhaseType);
         } catch (EntitlementUserApiException e) {
             throw new OverdueError(e);
         }
-
     }
 
     public SortedSet<Invoice> unpaidInvoicesForBundle(final UUID bundleId, final UUID accountId) {
diff --git a/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapper.java b/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapper.java
index 419e089..ed4bd61 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapper.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapper.java
@@ -53,10 +53,9 @@ public class OverdueWrapper<T extends Blockable> {
             return overdueStateSet.getClearState();
         }
 
-        final OverdueState<T> nextOverdueState;
         final BillingState<T> billingState = billingState();
         final String previousOverdueStateName = api.getBlockingStateFor(overdueable).getStateName();
-        nextOverdueState = overdueStateSet.calculateOverdueState(billingState, clock.getUTCNow());
+        final OverdueState<T> nextOverdueState = overdueStateSet.calculateOverdueState(billingState, clock.getUTCNow());
 
         if (nextOverdueState != null && !previousOverdueStateName.equals(nextOverdueState.getName())) {
             overdueStateApplicator.apply(overdueable, previousOverdueStateName, nextOverdueState);
diff --git a/overdue/src/test/java/com/ning/billing/overdue/calculator/TestBillingStateCalculatorBundle.java b/overdue/src/test/java/com/ning/billing/overdue/calculator/TestBillingStateCalculatorBundle.java
index 67b63ab..bb07af4 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/calculator/TestBillingStateCalculatorBundle.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/calculator/TestBillingStateCalculatorBundle.java
@@ -30,6 +30,8 @@ import org.testng.annotations.Test;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+
 import com.ning.billing.catalog.MockPlan;
 import com.ning.billing.catalog.MockPriceList;
 import com.ning.billing.catalog.api.Plan;
@@ -57,6 +59,26 @@ public class TestBillingStateCalculatorBundle extends TestBillingStateCalculator
     }
 
     @Test(groups = "fast")
+    public void testBillingStateAfterCancellation() throws Exception {
+        Mockito.when(invoiceApi.getUnpaidInvoicesByAccountId(Mockito.<UUID>any(), Mockito.<DateTime>any())).thenReturn(ImmutableList.<Invoice>of());
+
+        final UUID bundleId = UUID.randomUUID();
+        final SubscriptionBundle bundle = Mockito.mock(SubscriptionBundle.class);
+        Mockito.when(bundle.getId()).thenReturn(bundleId);
+
+        final EntitlementUserApi entitlementApi = Mockito.mock(EntitlementUserApi.class);
+        final Subscription subscription = Mockito.mock(Subscription.class);
+        Mockito.when(entitlementApi.getBaseSubscription(bundleId)).thenReturn(subscription);
+
+        final BillingStateCalculatorBundle calc = new BillingStateCalculatorBundle(entitlementApi, invoiceApi, clock);
+        final BillingStateBundle billingStateBundle = calc.calculateBillingState(bundle);
+        Assert.assertNull(billingStateBundle.getBasePlanBillingPeriod());
+        Assert.assertNull(billingStateBundle.getBasePlanPhaseType());
+        Assert.assertNull(billingStateBundle.getBasePlanPriceList());
+        Assert.assertNull(billingStateBundle.getBasePlanProduct());
+    }
+
+    @Test(groups = "fast")
     public void testUnpaidInvoiceForBundle() {
         final UUID thisBundleId = new UUID(0L, 0L);
         final UUID thatBundleId = new UUID(0L, 1L);