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);
}