/*
* Copyright 2014-2016 Groupon, Inc
* Copyright 2014-2016 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.killbill.billing.beatrix.integration;
import java.math.BigDecimal;
import java.util.List;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.api.TestApiListener.NextEvent;
import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.entitlement.api.DefaultEntitlement;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.payment.api.Payment;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
public class TestIntegrationParentInvoice extends TestIntegrationBase {
@Test(groups = "slow")
public void testParentInvoiceGeneration() throws Exception {
final int billingDay = 14;
final DateTime initialCreationDate = new DateTime(2015, 5, 15, 0, 0, 0, 0, testTimeZone);
// set clock to the initial start date
clock.setTime(initialCreationDate);
log.info("Beginning test with BCD of " + billingDay);
final Account parentAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
final Account child1Account = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
final Account child2Account = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
// CREATE SUBSCRIPTIONS AND EXPECT BOTH EVENTS EACH: NextEvent.CREATE NextEvent.INVOICE
createBaseEntitlementAndCheckForCompletion(child1Account.getId(), "bundleKey1", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
createBaseEntitlementAndCheckForCompletion(child2Account.getId(), "bundleKey2", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
// First Parent invoice over TRIAL period
List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 1);
Invoice parentInvoice = parentInvoices.get(0);
assertEquals(parentInvoice.getNumberOfItems(), 2);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.DRAFT);
assertTrue(parentInvoice.isParentInvoice());
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
// Moving a day the NotificationQ calls the commitInvoice. No payment is expected
busHandler.pushExpectedEvents(NextEvent.INVOICE);
clock.addDays(1);
assertListenerStatus();
// reload parent invoice
parentInvoice = invoiceUserApi.getInvoice(parentInvoice.getId(), callContext);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
// Move through time and verify new parent Invoice. No payments are expected yet.
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE,
NextEvent.INVOICE, NextEvent.INVOICE);
clock.addDays(29);
assertListenerStatus();
// Second Parent invoice over Recurring period
parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 2);
parentInvoice = parentInvoices.get(1);
assertEquals(parentInvoice.getNumberOfItems(), 2);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.DRAFT);
assertTrue(parentInvoice.isParentInvoice());
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.valueOf(279.90)), 0);
assertEquals(parentInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.PARENT_SUMMARY);
assertEquals(parentInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.PARENT_SUMMARY);
// Check Child Balance. It should be > 0 here because Parent invoice is unpaid yet.
List<Invoice> child1Invoices = invoiceUserApi.getInvoicesByAccount(child1Account.getId(), false, callContext);
assertEquals(child1Invoices.size(), 2);
assertTrue(child1Invoices.get(1).getBalance().compareTo(BigDecimal.ZERO) > 0);
// Moving a day the NotificationQ calls the commitInvoice. Payment is expected.
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(1);
assertListenerStatus();
parentInvoice = invoiceUserApi.getInvoice(parentInvoice.getId(), callContext);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
// Check Child Balance. It should be = 0 because parent invoice had already paid.
child1Invoices = invoiceUserApi.getInvoicesByAccount(child1Account.getId(), false, callContext);
assertEquals(child1Invoices.size(), 2);
assertTrue(parentInvoice.getBalance().compareTo(BigDecimal.ZERO) == 0);
assertTrue(child1Invoices.get(1).getBalance().compareTo(BigDecimal.ZERO) == 0);
}
@Test(groups = "slow")
public void testParentInvoiceGenerationMultipleActionsSameDay() throws Exception {
final int billingDay = 14;
final DateTime initialCreationDate = new DateTime(2015, 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
DefaultEntitlement baseEntitlementChild = createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
// Moving a day the NotificationQ calls the commitInvoice. No payment is expected.
busHandler.pushExpectedEvents(NextEvent.INVOICE);
clock.addDays(1);
assertListenerStatus();
// Move through time and verify new parent Invoice. No payments are expected yet.
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
clock.addDays(29);
assertListenerStatus();
// check parent Invoice with child plan amount
List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 2);
Invoice parentInvoice = parentInvoices.get(1);
assertEquals(parentInvoice.getNumberOfItems(), 1);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.DRAFT);
assertTrue(parentInvoice.isParentInvoice());
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.valueOf(29.95)), 0);
// upgrade plan
busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE);
baseEntitlementChild.changePlanOverrideBillingPolicy("Shotgun", BillingPeriod.MONTHLY, baseEntitlementChild.getLastActivePriceList().getName(), null, clock.getToday(childAccount.getTimeZone()), BillingActionPolicy.IMMEDIATE, null, callContext);
assertListenerStatus();
// check parent invoice. Expected to have the same invoice item with the amount updated
final List<Invoice> childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
BigDecimal totalAmount = childInvoices.get(1).getBalance().add(childInvoices.get(2).getBalance());
parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 2);
parentInvoice = parentInvoices.get(1);
assertEquals(parentInvoice.getNumberOfItems(), 1);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.DRAFT);
assertTrue(parentInvoice.isParentInvoice());
assertEquals(parentInvoice.getBalance(), totalAmount);
// Moving a day the NotificationQ calls the commitInvoice. Now payment is expected
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(1);
assertListenerStatus();
parentInvoice = invoiceUserApi.getInvoice(parentInvoice.getId(), callContext);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
}
@Test(groups = "slow")
public void testParentInvoiceGenerationChildCreditUnpaidInvoice() throws Exception {
final int billingDay = 14;
final DateTime initialCreationDate = new DateTime(2015, 5, 15, 0, 0, 0, 0, testTimeZone);
// set clock to the initial start date
clock.setTime(initialCreationDate);
log.info("Beginning test with BCD of " + billingDay);
final Account parentAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
final Account childAccount = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
DefaultEntitlement baseEntitlementChild = createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
// Moving a day the NotificationQ calls the commitInvoice. No payment is expected.
busHandler.pushExpectedEvents(NextEvent.INVOICE);
clock.addDays(1);
assertListenerStatus();
// Move through time and verify new parent Invoice. No payments are expected yet.
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
clock.addDays(29);
assertListenerStatus();
// add credit to child account when invoice is still unpaid
busHandler.pushExpectedEvents(NextEvent.INVOICE);
invoiceUserApi.insertCredit(childAccount.getId(), BigDecimal.TEN, clock.getUTCToday(), Currency.USD, true, "test", callContext);
assertListenerStatus();
final List<Invoice> childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
assertEquals(childInvoices.size(), 3);
// invoice monthly with credit
final Invoice childInvoice = childInvoices.get(1);
assertEquals(childInvoice.getNumberOfItems(), 2);
assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(29.95)), 0);
assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(-10.00)), 0);
assertEquals(childInvoice.getBalance().compareTo(BigDecimal.valueOf(19.95)), 0);
// check parent Invoice with child plan amount
List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 2);
Invoice parentInvoice = parentInvoices.get(1);
assertEquals(parentInvoice.getNumberOfItems(), 1);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.DRAFT);
assertTrue(parentInvoice.isParentInvoice());
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.valueOf(19.95)), 0);
// Moving a day the NotificationQ calls the commitInvoice.
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(1);
assertListenerStatus();
parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 2);
parentInvoice = parentInvoices.get(1);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
assertTrue(parentInvoice.isParentInvoice());
assertEquals(parentInvoice.getChargedAmount().compareTo(BigDecimal.valueOf(19.95)), 0);
assertEquals(parentInvoice.getCreditedAmount().compareTo(BigDecimal.ZERO), 0);
final List<Payment> accountPayments = paymentApi.getAccountPayments(parentAccount.getId(), false, null, callContext);
assertEquals(accountPayments.size(), 1);
assertEquals(accountPayments.get(0).getPurchasedAmount().setScale(2).compareTo(BigDecimal.valueOf(19.95)), 0);
assertEquals(accountPayments.get(0).getCreditedAmount().compareTo(BigDecimal.ZERO), 0);
}
@Test(groups = "slow")
public void testParentInvoiceGenerationChildCreditPaidInvoice() throws Exception {
final int billingDay = 14;
final DateTime initialCreationDate = new DateTime(2015, 5, 15, 0, 0, 0, 0, testTimeZone);
// set clock to the initial start date
clock.setTime(initialCreationDate);
log.info("Beginning test with BCD of " + billingDay);
final Account parentAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
final Account childAccount = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
// Moving a day the NotificationQ calls the commitInvoice. No payment is expected.
busHandler.pushExpectedEvents(NextEvent.INVOICE);
clock.addDays(1);
assertListenerStatus();
// Move through time and verify new parent Invoice. No payments are expected yet.
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
clock.addDays(29);
assertListenerStatus();
// Moving a day the NotificationQ calls the commitInvoice.
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(1);
assertListenerStatus();
// add credit to child account after invoice has been paid
busHandler.pushExpectedEvents(NextEvent.INVOICE);
invoiceUserApi.insertCredit(childAccount.getId(), BigDecimal.TEN, clock.getUTCToday(), Currency.USD, true, "test", callContext);
assertListenerStatus();
List<Invoice> childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
assertEquals(childInvoices.size(), 3);
// invoice monthly with credit
Invoice childInvoice = childInvoices.get(1);
assertEquals(childInvoice.getNumberOfItems(), 1);
assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(29.95)), 0);
// check parent Invoice with child plan amount
List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 2);
Invoice parentInvoice = parentInvoices.get(1);
assertEquals(parentInvoice.getNumberOfItems(), 1);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
assertTrue(parentInvoice.isParentInvoice());
assertEquals(parentInvoice.getChargedAmount().compareTo(BigDecimal.valueOf(29.95)), 0);
final List<Payment> accountPayments = paymentApi.getAccountPayments(parentAccount.getId(), false, null, callContext);
assertEquals(accountPayments.size(), 1);
assertEquals(accountPayments.get(0).getPurchasedAmount().setScale(2).compareTo(BigDecimal.valueOf(29.95)), 0);
assertEquals(accountPayments.get(0).getCreditedAmount().compareTo(BigDecimal.ZERO), 0);
}
// Scenario 1.a: Follow up Invoice Item Adjustment on unpaid DRAFT invoice
@Test(groups = "slow")
public void testParentInvoiceItemAdjustmentUnpaidDraftInvoice() 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();
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.DRAFT);
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);
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
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(), 1);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.DRAFT);
assertTrue(parentInvoice.isParentInvoice());
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.valueOf(239.95)), 0);
}
// 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
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.
clock.addDeltaFromReality(1000);
// issue a $5 adj when invoice is unpaid
insertInvoiceItemAdjustmentToChildInvoice(childAccount, childInvoice, BigDecimal.valueOf(5));
clock.addDeltaFromReality(1000);
// issue a $10 adj when invoice is unpaid
insertInvoiceItemAdjustmentToChildInvoice(childAccount, childInvoice, BigDecimal.TEN);
// move one day
busHandler.pushExpectedEvents();
clock.addDays(1);
assertListenerStatus();
// issue a $5 adj when invoice is unpaid
insertInvoiceItemAdjustmentToChildInvoice(childAccount, childInvoice, BigDecimal.valueOf(5));
clock.addDeltaFromReality(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(), 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(1).getAmount().compareTo(BigDecimal.valueOf(-10)), 0);
assertEquals(childInvoice.getInvoiceItems().get(2).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
assertEquals(childInvoice.getInvoiceItems().get(2).getAmount().compareTo(BigDecimal.valueOf(-5)), 0);
assertEquals(childInvoice.getInvoiceItems().get(3).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
assertEquals(childInvoice.getInvoiceItems().get(3).getAmount().compareTo(BigDecimal.valueOf(-10)), 0);
assertEquals(childInvoice.getInvoiceItems().get(4).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
assertEquals(childInvoice.getInvoiceItems().get(4).getAmount().compareTo(BigDecimal.valueOf(-5)), 0);
assertEquals(childInvoice.getInvoiceItems().get(5).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
assertEquals(childInvoice.getInvoiceItems().get(5).getAmount().compareTo(BigDecimal.valueOf(-10)), 0);
// reload parent invoice
parentInvoice = invoiceUserApi.getInvoice(parentInvoice.getId(), callContext);
assertEquals(parentInvoice.getNumberOfItems(), 6);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
assertTrue(parentInvoice.isParentInvoice());
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(1).getAmount().compareTo(BigDecimal.valueOf(-10)), 0);
assertEquals(parentInvoice.getInvoiceItems().get(2).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
assertEquals(parentInvoice.getInvoiceItems().get(2).getAmount().compareTo(BigDecimal.valueOf(-5)), 0);
assertEquals(parentInvoice.getInvoiceItems().get(3).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
assertEquals(parentInvoice.getInvoiceItems().get(3).getAmount().compareTo(BigDecimal.valueOf(-10)), 0);
assertEquals(parentInvoice.getInvoiceItems().get(4).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
assertEquals(parentInvoice.getInvoiceItems().get(4).getAmount().compareTo(BigDecimal.valueOf(-5)), 0);
assertEquals(parentInvoice.getInvoiceItems().get(5).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
assertEquals(parentInvoice.getInvoiceItems().get(5).getAmount().compareTo(BigDecimal.valueOf(-10)), 0);
}
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 {
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();
// move one day to have parent invoice paid
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(1);
assertListenerStatus();
final List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
final 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.ZERO), 0);
assertEquals(parentInvoice.getChargedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
// issue a $10 adj in a paid invoice
busHandler.pushExpectedEvents(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
// CBA_ADJ : $ +10
childInvoice = invoiceUserApi.getInvoice(childInvoice.getId(), callContext);
assertEquals(childInvoice.getNumberOfItems(), 3);
assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(-10)), 0);
assertEquals(childInvoice.getInvoiceItems().get(2).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
assertEquals(childInvoice.getInvoiceItems().get(2).getAmount().compareTo(BigDecimal.valueOf(10)), 0);
// check parent invoices
parentInvoice = invoiceUserApi.getInvoice(parentInvoice.getId(), callContext);
assertEquals(parentInvoice.getNumberOfItems(), 1);
assertEquals(parentInvoice.getPaidAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
assertEquals(parentInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.PARENT_SUMMARY);
}
// Scenario 3: Repair (early cancellation, plan downgrade) on unpaid invoice
@Test(groups = "slow")
public void testParentInvoiceEarlyCancellationUnpaidInvoice() 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
DefaultEntitlement baseEntitlementChild = 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 ----
// moving one extra day to get some cancellation difference
// Move through time and verify new parent Invoice. No payments are expected.
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
clock.addMonths(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.DRAFT);
assertTrue(parentInvoice.isParentInvoice());
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.valueOf(249.95)), 0);
// cancel subscription
busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.INVOICE);
baseEntitlementChild.cancelEntitlementWithDateOverrideBillingPolicy(clock.getToday(childAccount.getTimeZone()), BillingActionPolicy.IMMEDIATE, null, callContext);
assertListenerStatus();
// expected invoices
// invoice 2:
// REPAIR_ADJ $ -233.29
// CBA_ADJ $ 233.29
// Invoice 1:
// RECURRING : $ 249.95
// CBA_ADJ $ -233.29
childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
// invoice 1
childInvoice = childInvoices.get(1);
assertEquals(childInvoice.getNumberOfItems(), 2);
assertEquals(childInvoice.getBalance().compareTo(BigDecimal.valueOf(16.66)), 0);
assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(-233.29)), 0);
// invoice 2
childInvoice = childInvoices.get(2);
assertEquals(childInvoice.getNumberOfItems(), 2);
assertEquals(childInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(-233.29)), 0);
assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(233.29)), 0);
// check if parent invoice was updated
parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 2);
parentInvoice = parentInvoices.get(1);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.DRAFT);
assertTrue(parentInvoice.isParentInvoice());
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.valueOf(16.66)), 0);
assertEquals(parentInvoice.getNumberOfItems(), 1);
assertEquals(parentInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.PARENT_SUMMARY);
assertEquals(parentInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(16.66)), 0);
}
// Scenario 4: Repair (early cancellation, plan downgrade) on PAID invoice
@Test(groups = "slow")
public void testParentInvoiceEarlyCancellationPaidInvoice() 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
DefaultEntitlement baseEntitlementChild = 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();
// move one day to have parent invoice paid
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
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.getChargedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.INVOICE);
baseEntitlementChild.cancelEntitlementWithDateOverrideBillingPolicy(clock.getToday(childAccount.getTimeZone()), BillingActionPolicy.IMMEDIATE, null, callContext);
assertListenerStatus();
// expected child invoices
// Invoice 2:
// REPAIR_ADJ $ -241.62
// CBA_ADJ $ 241.62
// Invoice 1: # unchanged
// RECURRING : $ 249.95
childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
// invoice 1
childInvoice = childInvoices.get(1);
assertEquals(childInvoice.getNumberOfItems(), 1);
assertEquals(childInvoice.getChargedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
// invoice 2
childInvoice = childInvoices.get(2);
assertEquals(childInvoice.getNumberOfItems(), 2);
assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(-241.62)), 0);
assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(241.62)), 0);
// check equal parent invoice
parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 2);
parentInvoice = parentInvoices.get(1);
assertEquals(parentInvoice.getNumberOfItems(), 1);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
assertTrue(parentInvoice.isParentInvoice());
assertEquals(parentInvoice.getChargedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
}
// Scenario 5: Use of credit
@Test(groups = "slow")
public void testParentInvoiceEarlyCancellationUseCredit() 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
DefaultEntitlement baseEntitlementChild = 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();
// move one day to have parent invoice paid
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
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.getChargedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.INVOICE);
baseEntitlementChild.cancelEntitlementWithDateOverrideBillingPolicy(clock.getToday(childAccount.getTimeZone()), BillingActionPolicy.IMMEDIATE, null, callContext);
assertListenerStatus();
// expected child invoices
// Invoice 2:
// REPAIR_ADJ $ -241.62
// CBA_ADJ $ 241.62
// Invoice 1: # unchanged
// RECURRING : $ 249.95
childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
// invoice 1
childInvoice = childInvoices.get(1);
assertEquals(childInvoice.getNumberOfItems(), 1);
assertEquals(childInvoice.getChargedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
// invoice 2
childInvoice = childInvoices.get(2);
assertEquals(childInvoice.getNumberOfItems(), 2);
assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(-241.62)), 0);
assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(241.62)), 0);
// check equal parent invoice
parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 2);
parentInvoice = parentInvoices.get(1);
assertEquals(parentInvoice.getNumberOfItems(), 1);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
assertTrue(parentInvoice.isParentInvoice());
assertEquals(parentInvoice.getChargedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
// ------
busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE);
final DateTime date = new DateTime(2014, 7, 15, 0, 0, 0, 0, testTimeZone);
clock.setTime(date);
assertListenerStatus();
createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
// Move through time and verify new parent Invoice.
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.INVOICE);
clock.addDays(30);
assertListenerStatus();
childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
assertEquals(childInvoices.size(), 5);
childInvoice = childInvoices.get(4);
assertEquals(childInvoice.getNumberOfItems(), 2);
assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(-241.62)), 0);
// check equal parent invoice
parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 4);
parentInvoice = parentInvoices.get(3);
assertEquals(parentInvoice.getNumberOfItems(), 1);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.DRAFT);
assertTrue(parentInvoice.isParentInvoice());
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.valueOf(8.33)), 0);
}
// Scenario 6: Transfer credit
@Test(groups = "slow")
public void testParentInvoiceTransferCredit() 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));
busHandler.pushExpectedEvents(NextEvent.INVOICE);
invoiceUserApi.insertCredit(childAccount.getId(), new BigDecimal("250"), new LocalDate(clock.getUTCNow(), childAccount.getTimeZone()), childAccount.getCurrency(), true, null, callContext);
assertListenerStatus();
BigDecimal childAccountCBA = invoiceUserApi.getAccountCBA(childAccount.getId(), callContext);
assertEquals(childAccountCBA.compareTo(BigDecimal.valueOf(250)), 0);
BigDecimal parentAccountCBA = invoiceUserApi.getAccountCBA(parentAccount.getId(), callContext);
assertEquals(parentAccountCBA.compareTo(BigDecimal.ZERO), 0);
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE);
invoiceUserApi.transferChildCreditToParent(childAccount.getId(), callContext);
assertListenerStatus();
childAccountCBA = invoiceUserApi.getAccountCBA(childAccount.getId(), callContext);
assertEquals(childAccountCBA.compareTo(BigDecimal.ZERO), 0);
parentAccountCBA = invoiceUserApi.getAccountCBA(parentAccount.getId(), callContext);
assertEquals(parentAccountCBA.compareTo(BigDecimal.valueOf(250)), 0);
final List<Invoice> childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
assertEquals(childInvoices.size(), 2);
final Invoice childInvoice = childInvoices.get(1);
assertEquals(childInvoice.getNumberOfItems(), 2);
assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.EXTERNAL_CHARGE);
assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(250)), 0);
assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(-250)), 0);
// check equal parent invoice
final List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 1);
final Invoice parentInvoice = parentInvoices.get(0);
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);
}
// Scenario 6-b: Transfer credit
@Test(groups = "slow", expectedExceptions = InvoiceApiException.class,
expectedExceptionsMessageRegExp = ".* does not have credit")
public void testParentInvoiceTransferCreditAccountWithoutCredit() 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));
BigDecimal childAccountCBA = invoiceUserApi.getAccountCBA(childAccount.getId(), callContext);
assertEquals(childAccountCBA.compareTo(BigDecimal.ZERO), 0);
BigDecimal parentAccountCBA = invoiceUserApi.getAccountCBA(parentAccount.getId(), callContext);
assertEquals(parentAccountCBA.compareTo(BigDecimal.ZERO), 0);
invoiceUserApi.transferChildCreditToParent(childAccount.getId(), callContext);
}
// Scenario 6-c: Transfer credit
@Test(groups = "slow", expectedExceptions = InvoiceApiException.class,
expectedExceptionsMessageRegExp = ".* does not have a Parent Account associated")
public void testParentInvoiceTransferCreditAccountNoParent() 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 account = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, null, true));
BigDecimal childAccountCBA = invoiceUserApi.getAccountCBA(account.getId(), callContext);
assertEquals(childAccountCBA.compareTo(BigDecimal.ZERO), 0);
invoiceUserApi.transferChildCreditToParent(account.getId(), callContext);
}
}