Skip to content

Commit

Permalink
#553 - adding missing adjuntment for unpaid COMMITTED parent invoice
Browse files Browse the repository at this point in the history
  • Loading branch information
matias-aguero-hs committed Jul 11, 2016
1 parent 33934f5 commit c1d1fdb
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 18 deletions.
Expand Up @@ -315,9 +315,9 @@ public void testParentInvoiceGenerationChildCreditPaidInvoice() throws Exception

}

// Scenario 1: Follow up Invoice Item Adjustment on unpaid invoice
// Scenario 1.a: Follow up Invoice Item Adjustment on unpaid DRAFT invoice
@Test(groups = "slow")
public void testParentInvoiceItemAdjustmentUnpaidInvoice() throws Exception {
public void testParentInvoiceItemAdjustmentUnpaidDraftInvoice() throws Exception {

final int billingDay = 14;
final DateTime initialCreationDate = new DateTime(2014, 5, 15, 0, 0, 0, 0, testTimeZone);
Expand Down Expand Up @@ -385,6 +385,89 @@ public void testParentInvoiceItemAdjustmentUnpaidInvoice() throws Exception {

}

// Scenario 1.b: Follow up Invoice Item Adjustment on unpaid COMMITTED invoice
@Test(groups = "slow")
public void testParentInvoiceItemAdjustmentUnpaidCommittedInvoice() throws Exception {

final int billingDay = 14;
final DateTime initialCreationDate = new DateTime(2014, 5, 15, 0, 0, 0, 0, testTimeZone);
// set clock to the initial start date
clock.setTime(initialCreationDate);

final Account parentAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
final Account childAccount = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));

// CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);

// ---- trial period ----
// Moving a day the NotificationQ calls the commitInvoice. No payment is expected because balance is 0
busHandler.pushExpectedEvents(NextEvent.INVOICE);
clock.addDays(1);
assertListenerStatus();

// ---- recurring period ----
// Move through time and verify new parent Invoice. No payments are expected.
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
clock.addDays(29);
assertListenerStatus();

paymentPlugin.makeNextPaymentFailWithError();

// move one day to have parent invoice paid
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
clock.addDays(1);
assertListenerStatus();

List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
List<Invoice> childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
// get last child invoice
Invoice childInvoice = childInvoices.get(1);
assertEquals(childInvoice.getNumberOfItems(), 1);

// Second Parent invoice over Recurring period
assertEquals(parentInvoices.size(), 2);

Invoice parentInvoice = parentInvoices.get(1);
assertEquals(parentInvoice.getNumberOfItems(), 1);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
assertTrue(parentInvoice.isParentInvoice());
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);
assertListenerStatus();

// expected child invoice
// RECURRING : $ 249.95
// ITEM_ADJ : $ -10

// expected parent invoice
// PARENT_SUMMARY : $ 249.95
// ITEM_ADJ : $ -10

childInvoice = invoiceUserApi.getInvoice(childInvoice.getId(), callContext);
assertEquals(childInvoice.getNumberOfItems(), 2);
assertEquals(childInvoice.getBalance().compareTo(BigDecimal.valueOf(239.95)), 0);
assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
assertEquals(childInvoice.getInvoiceItems().get(1).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.getStatus(), InvoiceStatus.COMMITTED);
assertTrue(parentInvoice.isParentInvoice());
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.valueOf(239.95)), 0);
assertEquals(parentInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.PARENT_SUMMARY);
assertEquals(parentInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);

}

// Scenario 2: Follow up Invoice Item Adjustment on PAID invoice
@Test(groups = "slow")
public void testParentInvoiceItemAdjustmentPaidInvoice() throws Exception {
Expand Down
Expand Up @@ -70,6 +70,7 @@
import org.killbill.billing.invoice.dao.InvoiceDao;
import org.killbill.billing.invoice.dao.InvoiceItemModelDao;
import org.killbill.billing.invoice.dao.InvoiceModelDao;
import org.killbill.billing.invoice.dao.InvoiceModelDaoHelper;
import org.killbill.billing.invoice.dao.InvoiceParentChildModelDao;
import org.killbill.billing.invoice.generator.InvoiceGenerator;
import org.killbill.billing.invoice.generator.InvoiceWithMetadata;
Expand All @@ -78,6 +79,7 @@
import org.killbill.billing.invoice.model.DefaultInvoice;
import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
import org.killbill.billing.invoice.model.InvoiceItemFactory;
import org.killbill.billing.invoice.model.ItemAdjInvoiceItem;
import org.killbill.billing.invoice.model.ParentInvoiceItem;
import org.killbill.billing.invoice.model.RecurringInvoiceItem;
import org.killbill.billing.invoice.notification.DefaultNextBillingDateNotifier;
Expand All @@ -88,6 +90,7 @@
import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
import org.killbill.billing.util.UUIDs;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.callcontext.TenantContext;
Expand Down Expand Up @@ -777,41 +780,56 @@ private boolean shouldIgnoreChildInvoice(final Invoice childInvoice, final BigDe
public void processParentInvoiceForAdjustments(final ImmutableAccountData account, final UUID childInvoiceId, final InternalCallContext context) throws InvoiceApiException {

final InvoiceModelDao childInvoiceModelDao = invoiceDao.getById(childInvoiceId, context);
final Invoice childInvoice = new DefaultInvoice(childInvoiceModelDao);
final InvoiceModelDao parentInvoice = childInvoiceModelDao.getParentInvoice();
final InvoiceModelDao parentInvoiceModelDao = childInvoiceModelDao.getParentInvoice();

if (parentInvoice == null) {
if (parentInvoiceModelDao == null) {
throw new InvoiceApiException(ErrorCode.INVOICE_MISSING_PARENT_INVOICE, childInvoiceModelDao.getId());
} else if (parentInvoice.getStatus().equals(InvoiceStatus.COMMITTED)) {
// ignore parent invoice adjustment if it's in COMMITTED status.
return;
}

final Long parentAccountRecordId = internalCallContextFactory.getRecordIdFromObject(account.getParentAccountId(), ObjectType.ACCOUNT, buildTenantContext(context));
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>() {
@Override
public boolean apply(@Nullable final InvoiceItemModelDao input) {
return input.getType().equals(InvoiceItemType.ITEM_ADJ) || input.getType().equals(InvoiceItemType.REPAIR_ADJ);
}
});
if (childInvoiceItemAdjustment == null) return;

final BigDecimal childInvoiceAdjustmentAmount = childInvoiceItemAdjustment.getAmount();

final Long parentAccountRecordId = internalCallContextFactory.getRecordIdFromObject(account.getParentAccountId(), ObjectType.ACCOUNT, buildTenantContext(context));
final InternalCallContext parentContext = internalCallContextFactory.createInternalCallContext(parentAccountRecordId, context);
final DateTime today = clock.getNow(account.getTimeZone());
final String description = "Adjustment for account ".concat(account.getExternalKey());

final InvoiceItemModelDao parentInvoiceItemForChild = Iterables.find(parentInvoice.getInvoiceItems(), new Predicate<InvoiceItemModelDao>() {
final InvoiceItemModelDao parentSummaryInvoiceItem = Iterables.find(parentInvoiceModelDao.getInvoiceItems(), new Predicate<InvoiceItemModelDao>() {
@Override
public boolean apply(@Nullable final InvoiceItemModelDao input) {
return childInvoice.getAccountId().equals(input.getChildAccountId());
return input.getType().equals(InvoiceItemType.PARENT_SUMMARY)
&& input.getChildAccountId().equals(childInvoiceModelDao.getAccountId());
}
});

if (parentInvoiceModelDao.getStatus().equals(InvoiceStatus.COMMITTED)) {
if (InvoiceModelDaoHelper.getBalance(parentInvoiceModelDao).compareTo(BigDecimal.ZERO) > 0) {

ItemAdjInvoiceItem adj = new ItemAdjInvoiceItem(UUIDs.randomUUID(),
context.getCreatedDate(),
parentSummaryInvoiceItem.getInvoiceId(),
parentSummaryInvoiceItem.getAccountId(),
clock.getUTCToday(),
description,
childInvoiceAdjustmentAmount,
parentInvoiceModelDao.getCurrency(),
parentSummaryInvoiceItem.getId());
parentInvoiceModelDao.addInvoiceItem(new InvoiceItemModelDao(adj));
invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(parentInvoiceModelDao), parentContext);
}

// ignore parent invoice adjustment if it's already paid.
return;
}

// update item amount
BigDecimal newParentInvoiceItemAmount = childInvoiceAdjustmentAmount.add(parentInvoiceItemForChild.getAmount());
invoiceDao.updateInvoiceItemAmount(parentInvoiceItemForChild.getId(), newParentInvoiceItemAmount, parentContext);
BigDecimal newParentInvoiceItemAmount = childInvoiceAdjustmentAmount.add(parentSummaryInvoiceItem.getAmount());
invoiceDao.updateInvoiceItemAmount(parentSummaryInvoiceItem.getId(), newParentInvoiceItemAmount, parentContext);
}

}

0 comments on commit c1d1fdb

Please sign in to comment.