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 14ce36e..111ccfa 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
@@ -435,39 +435,81 @@ public class TestIntegrationParentInvoice extends TestIntegrationBase {
         assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.valueOf(249.95)), 0);
 
         // issue a $10 adj when invoice is unpaid
-        busHandler.pushExpectedEvents(NextEvent.INVOICE_ADJUSTMENT, NextEvent.INVOICE_ADJUSTMENT);
-        invoiceUserApi.insertInvoiceItemAdjustment(childAccount.getId(), childInvoice.getId(),
-                                                   childInvoice.getInvoiceItems().get(0).getId(),
-                                                   clock.getToday(childAccount.getTimeZone()), BigDecimal.TEN,
-                                                   childAccount.getCurrency(), "test adjustment", callContext);
+        insertInvoiceItemAdjustmentToChildInvoice(childAccount, childInvoice, BigDecimal.TEN);
+        // make sure there is time difference between item adjustments.
+        // Otherwise they are created with same id and createdDate and it's used to sort them.
+        Thread.sleep(1000);
+
+        // issue a $5 adj when invoice is unpaid
+        insertInvoiceItemAdjustmentToChildInvoice(childAccount, childInvoice, BigDecimal.valueOf(5));
+        Thread.sleep(1000);
+
+        // issue a $10 adj when invoice is unpaid
+        insertInvoiceItemAdjustmentToChildInvoice(childAccount, childInvoice, BigDecimal.TEN);
+        Thread.sleep(1000);
+
+        // move one day
+        busHandler.pushExpectedEvents();
+        clock.addDays(1);
         assertListenerStatus();
 
+        // issue a $5 adj when invoice is unpaid
+        insertInvoiceItemAdjustmentToChildInvoice(childAccount, childInvoice, BigDecimal.valueOf(5));
+        Thread.sleep(1000);
+
+        // issue a $10 adj when invoice is unpaid
+        insertInvoiceItemAdjustmentToChildInvoice(childAccount, childInvoice, BigDecimal.TEN);
+
         // expected child invoice
         // RECURRING : $ 249.95
         // ITEM_ADJ : $ -10
+        // ITEM_ADJ : $ -5
+        // ITEM_ADJ : $ -10
+        // ITEM_ADJ : $ -5
+        // ITEM_ADJ : $ -10
 
         // expected parent invoice
         // PARENT_SUMMARY : $ 249.95
         // ITEM_ADJ : $ -10
+        // ITEM_ADJ : $ -5
+        // ITEM_ADJ : $ -10
+        // ITEM_ADJ : $ -5
+        // ITEM_ADJ : $ -10
 
         childInvoice = invoiceUserApi.getInvoice(childInvoice.getId(), callContext);
-        assertEquals(childInvoice.getNumberOfItems(), 2);
-        assertEquals(childInvoice.getBalance().compareTo(BigDecimal.valueOf(239.95)), 0);
+        assertEquals(childInvoice.getNumberOfItems(), 6);
+        assertEquals(childInvoice.getBalance().compareTo(BigDecimal.valueOf(209.95)), 0);
         assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
         assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(2).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(3).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(4).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(5).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
 
         // reload parent invoice
         parentInvoice = invoiceUserApi.getInvoice(parentInvoice.getId(), callContext);
-        // check parent invoice is updated and still in DRAFT status
-        assertEquals(parentInvoice.getNumberOfItems(), 2);
+        assertEquals(parentInvoice.getNumberOfItems(), 6);
         assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
         assertTrue(parentInvoice.isParentInvoice());
-        assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.valueOf(239.95)), 0);
+        assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.valueOf(209.95)), 0);
         assertEquals(parentInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.PARENT_SUMMARY);
         assertEquals(parentInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(parentInvoice.getInvoiceItems().get(2).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(parentInvoice.getInvoiceItems().get(3).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(parentInvoice.getInvoiceItems().get(4).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(parentInvoice.getInvoiceItems().get(5).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
 
     }
 
+    private void insertInvoiceItemAdjustmentToChildInvoice(final Account childAccount, final Invoice childInvoice, BigDecimal amount) throws InvoiceApiException {
+        busHandler.pushExpectedEvents(NextEvent.INVOICE_ADJUSTMENT, NextEvent.INVOICE_ADJUSTMENT);
+        invoiceUserApi.insertInvoiceItemAdjustment(childAccount.getId(), childInvoice.getId(),
+                                                   childInvoice.getInvoiceItems().get(0).getId(),
+                                                   clock.getToday(childAccount.getTimeZone()), amount,
+                                                   childAccount.getCurrency(), "test adjustment", callContext);
+        assertListenerStatus();
+    }
+
     // Scenario 2: Follow up Invoice Item Adjustment on PAID invoice
     @Test(groups = "slow")
     public void testParentInvoiceItemAdjustmentPaidInvoice() throws Exception {
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 8d2210d..ea4ced8 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -21,6 +21,8 @@ package org.killbill.billing.invoice;
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -790,37 +792,48 @@ public class InvoiceDispatcher {
         final InternalCallContext parentContext = internalCallContextFactory.createInternalCallContext(parentAccountRecordId, context);
         final String description = "Adjustment for account ".concat(account.getExternalKey());
 
-        final InvoiceItemModelDao childInvoiceItemAdjustment = Iterables.find(childInvoiceModelDao.getInvoiceItems(), new Predicate<InvoiceItemModelDao>() {
+        // find PARENT_SUMMARY invoice item for this child account
+        final InvoiceItemModelDao parentSummaryInvoiceItem = Iterables.find(parentInvoiceModelDao.getInvoiceItems(), new Predicate<InvoiceItemModelDao>() {
             @Override
             public boolean apply(@Nullable final InvoiceItemModelDao input) {
-                return input.getType().equals(InvoiceItemType.ITEM_ADJ) || input.getType().equals(InvoiceItemType.REPAIR_ADJ);
+                return input.getType().equals(InvoiceItemType.PARENT_SUMMARY)
+                       && input.getChildAccountId().equals(childInvoiceModelDao.getAccountId());
             }
         });
-        if (childInvoiceItemAdjustment == null) return;
-        final BigDecimal childInvoiceAdjustmentAmount = childInvoiceItemAdjustment.getAmount();
 
-        final InvoiceItemModelDao parentSummaryInvoiceItem = Iterables.find(parentInvoiceModelDao.getInvoiceItems(), new Predicate<InvoiceItemModelDao>() {
+        final Iterable<InvoiceItemModelDao> childAdjustments = Iterables.filter(childInvoiceModelDao.getInvoiceItems(), new Predicate<InvoiceItemModelDao>() {
             @Override
             public boolean apply(@Nullable final InvoiceItemModelDao input) {
-                return input.getType().equals(InvoiceItemType.PARENT_SUMMARY)
-                       && input.getChildAccountId().equals(childInvoiceModelDao.getAccountId());
+                return input.getType().equals(InvoiceItemType.ITEM_ADJ);
             }
         });
 
+        // find last ITEM_ADJ invoice added in child invoice
+        Comparator<InvoiceItemModelDao> cmp = new Comparator<InvoiceItemModelDao>() {
+            @Override
+            public int compare(InvoiceItemModelDao o1, InvoiceItemModelDao o2) {
+                return o1.getCreatedDate().compareTo(o2.getCreatedDate());
+            }
+        };
+        final InvoiceItemModelDao lastChildInvoiceItemAdjustment = Collections.max(Lists.newArrayList(childAdjustments), cmp);
+
+        final BigDecimal childInvoiceAdjustmentAmount = lastChildInvoiceItemAdjustment.getAmount();
+
         if (parentInvoiceModelDao.getStatus().equals(InvoiceStatus.COMMITTED)) {
             if (InvoiceModelDaoHelper.getBalance(parentInvoiceModelDao).compareTo(BigDecimal.ZERO) > 0) {
 
                 ItemAdjInvoiceItem adj = new ItemAdjInvoiceItem(UUIDs.randomUUID(),
-                                                                context.getCreatedDate(),
+                                                                lastChildInvoiceItemAdjustment.getCreatedDate(),
                                                                 parentSummaryInvoiceItem.getInvoiceId(),
                                                                 parentSummaryInvoiceItem.getAccountId(),
-                                                                clock.getUTCToday(),
+                                                                lastChildInvoiceItemAdjustment.getStartDate(),
                                                                 description,
                                                                 childInvoiceAdjustmentAmount,
                                                                 parentInvoiceModelDao.getCurrency(),
                                                                 parentSummaryInvoiceItem.getId());
                 parentInvoiceModelDao.addInvoiceItem(new InvoiceItemModelDao(adj));
                 invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(parentInvoiceModelDao), parentContext);
+                return;
             }
 
             // ignore parent invoice adjustment if it's already paid.
@@ -828,7 +841,7 @@ public class InvoiceDispatcher {
         }
 
         // update item amount
-        BigDecimal newParentInvoiceItemAmount = childInvoiceAdjustmentAmount.add(parentSummaryInvoiceItem.getAmount());
+        final BigDecimal newParentInvoiceItemAmount = childInvoiceAdjustmentAmount.add(parentSummaryInvoiceItem.getAmount());
         invoiceDao.updateInvoiceItemAmount(parentSummaryInvoiceItem.getId(), newParentInvoiceItemAmount, parentContext);
     }