killbill-uncached

Details

diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java
index ef0a637..a0d22f9 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java
@@ -787,7 +787,7 @@ public class TestIntegrationParentInvoice extends TestIntegrationBase {
         BigDecimal parentAccountCBA = invoiceUserApi.getAccountCBA(parentAccount.getId(), callContext);
         assertEquals(parentAccountCBA.compareTo(BigDecimal.ZERO), 0);
 
-        busHandler.pushExpectedEvents(NextEvent.INVOICE);
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE);
         invoiceUserApi.transferChildCreditToParent(childAccount.getId(), callContext);
         assertListenerStatus();
 
@@ -812,13 +812,11 @@ public class TestIntegrationParentInvoice extends TestIntegrationBase {
         assertEquals(parentInvoices.size(), 1);
 
         final Invoice parentInvoice = parentInvoices.get(0);
-        assertEquals(parentInvoice.getNumberOfItems(), 3);
+        assertEquals(parentInvoice.getNumberOfItems(), 2);
         assertEquals(parentInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.CREDIT_ADJ);
         assertEquals(parentInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(-250)), 0);
         assertEquals(parentInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
         assertEquals(parentInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(250)), 0);
-        assertEquals(parentInvoice.getInvoiceItems().get(2).getInvoiceItemType(), InvoiceItemType.PARENT_SUMMARY);
-        assertEquals(parentInvoice.getInvoiceItems().get(2).getAmount().compareTo(BigDecimal.ZERO), 0);
     }
 
     // Scenario 6-b: Transfer credit
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
index 10d3f5f..d9cbe1c 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -534,12 +534,12 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
     }
 
     @Override
-    public void transferChildCreditToParent(final UUID childAccountId, final CallContext callContext) throws InvoiceApiException {
+    public void transferChildCreditToParent(final UUID childAccountId, final CallContext context) throws InvoiceApiException {
 
         final Account childAccount;
-        final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(childAccountId, ObjectType.INVOICE, callContext);
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(childAccountId, ObjectType.ACCOUNT, context);
         try {
-            childAccount = accountUserApi.getAccountById(childAccountId, internalContext);
+            childAccount = accountUserApi.getAccountById(childAccountId, internalCallContext);
         } catch (AccountApiException e) {
             throw new InvoiceApiException(e);
         }
@@ -548,12 +548,12 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
             throw new InvoiceApiException(ErrorCode.ACCOUNT_DOES_NOT_HAVE_PARENT_ACCOUNT, childAccountId);
         }
 
-        final BigDecimal accountCBA = getAccountCBA(childAccountId, callContext);
+        final BigDecimal accountCBA = getAccountCBA(childAccountId, context);
         if (accountCBA.compareTo(BigDecimal.ZERO) <= 0) {
             throw new InvoiceApiException(ErrorCode.CHILD_ACCOUNT_MISSING_CREDIT, childAccountId);
         }
 
-        dao.transferChildCreditToParent(childAccount, callContext);
+        dao.transferChildCreditToParent(childAccount, internalCallContext);
 
     }
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
index 11863c3..911c311 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
@@ -57,7 +57,6 @@ import org.killbill.billing.invoice.notification.ParentInvoiceCommitmentPoster;
 import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.cache.Cachable.CacheType;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
-import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.config.definition.InvoiceConfig;
 import org.killbill.billing.util.dao.NonEntityDao;
@@ -1106,21 +1105,23 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
     }
 
     @Override
-    public void transferChildCreditToParent(final Account childAccount, final CallContext context) throws InvoiceApiException {
+    public void transferChildCreditToParent(final Account childAccount, final InternalCallContext childAccountContext) throws InvoiceApiException {
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
             public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
 
-                final InternalCallContext childInternalCallContext = internalCallContextFactory.createInternalCallContext(childAccount.getId(), context);
-                final InternalCallContext parentInternalCallContext = internalCallContextFactory.createInternalCallContext(childAccount.getParentAccountId(), context);
+                // Need to create an internalCallContext for parent account because it's needed to save the correct accountRecordId in Invoice tables.
+                // Then it's used to load invoices by account.
+                final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(childAccount.getParentAccountId(), childAccountContext);
+                final InternalCallContext parentAccountContext = internalCallContextFactory.createInternalCallContext(internalTenantContext.getAccountRecordId(), childAccountContext);
 
                 final InvoiceSqlDao invoiceSqlDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
                 final InvoiceItemSqlDao transInvoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
 
                 // create child and parent invoices
 
-                final DateTime effectiveDate = context.getCreatedDate();
-                final BigDecimal accountCBA = getAccountCBA(childAccount.getId(), childInternalCallContext);
+                final DateTime effectiveDate = childAccountContext.getCreatedDate();
+                final BigDecimal accountCBA = getAccountCBA(childAccount.getId(), childAccountContext);
 
                 // create external charge to child account
                 final Invoice invoiceForExternalCharge = new DefaultInvoice(childAccount.getId(), effectiveDate.toLocalDate(),
@@ -1140,7 +1141,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
 
                 // create credit to parent account
                 final Invoice invoiceForCredit = new DefaultInvoice(childAccount.getParentAccountId(), effectiveDate.toLocalDate(), effectiveDate.toLocalDate(),
-                                                                    childAccount.getCurrency(), InvoiceStatus.DRAFT);
+                                                                    childAccount.getCurrency(), InvoiceStatus.COMMITTED);
                 final String creditDescription = "Credit migrated from child account " + childAccount.getId();
                 final InvoiceItem creditItem = new CreditAdjInvoiceItem(UUIDs.randomUUID(),
                                                                         effectiveDate,
@@ -1156,18 +1157,19 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
 
                 // save invoices and invoice items
                 InvoiceModelDao childInvoice = new InvoiceModelDao(invoiceForExternalCharge);
-                invoiceSqlDao.create(childInvoice, childInternalCallContext);
-                createInvoiceItemFromTransaction(transInvoiceItemSqlDao, new InvoiceItemModelDao(externalChargeItem), childInternalCallContext);
+                invoiceSqlDao.create(childInvoice, childAccountContext);
+                createInvoiceItemFromTransaction(transInvoiceItemSqlDao, new InvoiceItemModelDao(externalChargeItem), childAccountContext);
 
                 InvoiceModelDao parentInvoice = new InvoiceModelDao(invoiceForCredit);
-                invoiceSqlDao.create(parentInvoice, parentInternalCallContext);
-                createInvoiceItemFromTransaction(transInvoiceItemSqlDao, new InvoiceItemModelDao(creditItem), parentInternalCallContext);
+                invoiceSqlDao.create(parentInvoice, parentAccountContext);
+                createInvoiceItemFromTransaction(transInvoiceItemSqlDao, new InvoiceItemModelDao(creditItem), parentAccountContext);
 
                 // add CBA complexity and notify bus on child invoice creation
-                cbaDao.addCBAComplexityFromTransaction(childInvoice.getId(), entitySqlDaoWrapperFactory, childInternalCallContext);
-                notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, childInvoice, childInternalCallContext);
+                cbaDao.addCBAComplexityFromTransaction(childInvoice.getId(), entitySqlDaoWrapperFactory, childAccountContext);
+                notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, childInvoice, childAccountContext);
 
-                cbaDao.addCBAComplexityFromTransaction(parentInvoice.getId(), entitySqlDaoWrapperFactory, parentInternalCallContext);
+                cbaDao.addCBAComplexityFromTransaction(parentInvoice.getId(), entitySqlDaoWrapperFactory, parentAccountContext);
+                notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, parentInvoice, parentAccountContext);
 
                 return null;
             }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
index 216e47d..d1d16f1 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
@@ -34,7 +34,6 @@ import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceStatus;
-import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.EntityDao;
 
@@ -200,8 +199,8 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
      * Move a given child credit to the parent level
      *
      * @param childAccount the child account
-     * @param context the tenant context
+     * @param childAccountContext the tenant context for the child account id
      * @throws InvoiceApiException if any unexpected error occurs
      */
-    void transferChildCreditToParent(Account childAccount, CallContext context) throws InvoiceApiException;
+    void transferChildCreditToParent(Account childAccount, InternalCallContext childAccountContext) throws InvoiceApiException;
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
index 6a5da4d..2aeba34 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -737,8 +737,7 @@ public class InvoiceDispatcher {
             invoices.add(draftParentInvoice);
             invoiceDao.createInvoices(invoices, parentContext);
         } else {
-            // do nothing if child invoice has negative amount because it's a credit and it will be use in next invoice
-            if (childInvoiceAmount.compareTo(BigDecimal.ZERO) < 0) return;
+            if (shouldIgnoreChildInvoice(childInvoice, childInvoiceAmount)) return;
 
             draftParentInvoice = new InvoiceModelDao(account.getParentAccountId(), today.toLocalDate(), account.getCurrency(), InvoiceStatus.DRAFT, true);
             InvoiceItem parentInvoiceItem = new ParentInvoiceItem(UUID.randomUUID(), today, draftParentInvoice.getId(), account.getParentAccountId(), account.getId(), childInvoiceAmount, account.getCurrency(), description);
@@ -756,6 +755,25 @@ public class InvoiceDispatcher {
 
     }
 
+    private boolean shouldIgnoreChildInvoice(final Invoice childInvoice, final BigDecimal childInvoiceAmount) {
+
+        switch (childInvoiceAmount.compareTo(BigDecimal.ZERO)) {
+            case -1 : {
+                // do nothing if child invoice has negative amount because it's a credit and it will be use in next invoice
+                return true;
+            }
+            case 1 : return false;
+            case 0 : {
+                // only ignore if amount == 0 and any item is not FIXED or RECURRING
+                for (InvoiceItem item : childInvoice.getInvoiceItems()) {
+                    if (item.getInvoiceItemType().equals(InvoiceItemType.FIXED) || item.getInvoiceItemType().equals(InvoiceItemType.RECURRING)) return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
     public void processParentInvoiceForAdjustments(final ImmutableAccountData account, final UUID childInvoiceId, final InternalCallContext context) throws InvoiceApiException {
 
         final InvoiceModelDao childInvoiceModelDao = invoiceDao.getById(childInvoiceId, context);
@@ -775,7 +793,8 @@ public class InvoiceDispatcher {
                 return input.getType().equals(InvoiceItemType.ITEM_ADJ) || input.getType().equals(InvoiceItemType.REPAIR_ADJ);
             }
         });
-        // TODO should I add a NPE check?
+        if (childInvoiceItemAdjustment == null) return;
+
         final BigDecimal childInvoiceAdjustmentAmount = childInvoiceItemAdjustment.getAmount();
 
         final Long parentAccountRecordId = internalCallContextFactory.getRecordIdFromObject(account.getParentAccountId(), ObjectType.ACCOUNT, buildTenantContext(context));
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
index 7844d52..9148a33 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
@@ -37,7 +37,6 @@ import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.killbill.billing.invoice.api.user.DefaultInvoiceCreationEvent;
-import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.entity.DefaultPagination;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.MockEntityDaoBase;
@@ -403,7 +402,7 @@ public class MockInvoiceDao extends MockEntityDaoBase<InvoiceModelDao, Invoice, 
     }
 
     @Override
-    public void transferChildCreditToParent(final Account childAccount, final CallContext context) throws InvoiceApiException {
+    public void transferChildCreditToParent(final Account childAccount, final InternalCallContext context) throws InvoiceApiException {
         throw new UnsupportedOperationException();
     }
 }