/*
* Copyright 2014-2017 Groupon, Inc
* Copyright 2014-2017 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.account.api.DefaultAccount;
import org.killbill.billing.account.dao.AccountModelDao;
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.PlanSpecifier;
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.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.payment.api.Payment;
import org.killbill.billing.payment.api.PluginProperty;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNull;
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());
// balance is 0 because parent invoice status is DRAFT
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
// total 279.95
assertEquals(parentInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.PARENT_SUMMARY);
assertEquals(parentInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
assertEquals(parentInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.PARENT_SUMMARY);
assertEquals(parentInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(29.95)), 0);
// 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);
// child balance is 0 because parent invoice status is DRAFT at this point
assertEquals(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);
// load children invoice items
final List<InvoiceItem> childrenInvoiceItems = invoiceUserApi.getInvoiceItemsByParentInvoice(parentInvoice.getId(), callContext);
assertEquals(childrenInvoiceItems.size(), 2);
assertEquals(childrenInvoiceItems.get(0).getAccountId(), child1Account.getId());
assertEquals(childrenInvoiceItems.get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
assertEquals(childrenInvoiceItems.get(1).getAccountId(), child2Account.getId());
assertEquals(childrenInvoiceItems.get(1).getAmount().compareTo(BigDecimal.valueOf(29.95)), 0);
// loading children items from non parent account should return empty list
assertEquals(invoiceUserApi.getInvoiceItemsByParentInvoice(child1Invoices.get(1).getId(), callContext).size(), 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());
// balance is 0 because parent invoice status is DRAFT
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
assertEquals(parentInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(29.95)), 0);
// upgrade plan
busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE);
baseEntitlementChild.changePlanWithDate(new PlanSpecifier("Shotgun", BillingPeriod.MONTHLY, baseEntitlementChild.getLastActivePriceList().getName()), null, null,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);
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());
// balance is 0 because parent invoice status is DRAFT
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
// 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);
// child balance is 0 because parent invoice status is DRAFT at this point
assertEquals(childInvoice.getBalance().compareTo(BigDecimal.ZERO), 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());
// balance is 0 because parent invoice status is DRAFT
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
assertEquals(parentInvoice.getInvoiceItems().get(0).getAmount().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, 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, 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());
// balance is 0 because parent invoice status is DRAFT
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
assertEquals(parentInvoice.getInvoiceItems().get(0).getAmount().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);
// child balance is 0 because parent invoice status is DRAFT at this point
assertEquals(childInvoice.getBalance().compareTo(BigDecimal.ZERO), 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.ITEM_ADJ);
assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(-10.00)), 0);
// 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());
// balance is 0 because parent invoice status is DRAFT
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
assertEquals(parentInvoice.getInvoiceItems().get(0).getAmount().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());
// balance is 0 because parent invoice status is DRAFT
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
assertEquals(parentInvoice.getInvoiceItems().get(0).getAmount().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);
// child balance is 0 because parent invoice status is DRAFT at this point
assertEquals(childInvoice.getBalance().compareTo(BigDecimal.ZERO), 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());
// balance is 0 because parent invoice status is DRAFT
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 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());
// balance is 0 because parent invoice status is DRAFT
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
assertEquals(parentInvoice.getInvoiceItems().get(0).getAmount().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);
}
@Test(groups = "slow")
public void testUnParentingWithUnpaidInvoice() 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));
Account childAccount = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
// Verify mapping
childAccount = accountUserApi.getAccountById(childAccount.getId(), callContext);
assertEquals(childAccount.getParentAccountId(), parentAccount.getId());
assertTrue(childAccount.isPaymentDelegatedToParent());
List<Account> childrenAccounts = accountUserApi.getChildrenAccounts(parentAccount.getId(), callContext);
assertEquals(childrenAccounts.size(), 1);
assertEquals(childrenAccounts.get(0).getId(), childAccount.getId());
// Create subscription
createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Shotgun", 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();
// 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(), 1);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
assertTrue(parentInvoice.isParentInvoice());
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
// First child invoice over TRIAL period
List<Invoice> childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
assertEquals(childInvoices.size(), 1);
assertEquals(childInvoices.get(0).getBalance().compareTo(BigDecimal.ZERO), 0);
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
clock.addDays(29);
assertListenerStatus();
paymentPlugin.makeNextPaymentFailWithError();
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
clock.addDays(1);
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(), 1);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
assertTrue(parentInvoice.isParentInvoice());
assertEquals(parentInvoice.getBalance().compareTo(new BigDecimal("249.95")), 0);
// The parent has attempted to pay the child invoice
assertEquals(parentInvoice.getPayments().size(), 1);
assertEquals(paymentApi.getPayment(parentInvoice.getPayments().get(0).getPaymentId(), false, false, ImmutableList.<PluginProperty>of(), callContext).getPaymentMethodId(),
parentAccount.getPaymentMethodId());
// Second child invoice over Recurring period
childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
assertEquals(childInvoices.size(), 2);
assertEquals(childInvoices.get(1).getBalance().compareTo(new BigDecimal("249.95")), 0);
// Verify balances
assertEquals(invoiceUserApi.getAccountBalance(parentAccount.getId(), callContext).compareTo(new BigDecimal("249.95")), 0);
assertEquals(invoiceUserApi.getAccountBalance(childAccount.getId(), callContext).compareTo(new BigDecimal("249.95")), 0);
// Un-parent the child
final AccountModelDao childAccountModelDao = new AccountModelDao(childAccount.getId(), childAccount);
childAccountModelDao.setParentAccountId(null);
childAccountModelDao.setIsPaymentDelegatedToParent(false);
accountUserApi.updateAccount(new DefaultAccount(childAccountModelDao), callContext);
// Verify mapping
childAccount = accountUserApi.getAccountById(childAccount.getId(), callContext);
assertNull(childAccount.getParentAccountId());
assertFalse(childAccount.isPaymentDelegatedToParent());
childrenAccounts = accountUserApi.getChildrenAccounts(parentAccount.getId(), callContext);
assertEquals(childrenAccounts.size(), 0);
// Verify balances
// TODO Should we automatically adjust the invoice at the parent level or should it be the responsibility of the user?
assertEquals(invoiceUserApi.getAccountBalance(parentAccount.getId(), callContext).compareTo(new BigDecimal("249.95")), 0);
assertEquals(invoiceUserApi.getAccountBalance(childAccount.getId(), callContext).compareTo(new BigDecimal("249.95")), 0);
final int nbDaysBeforeRetry = paymentConfig.getPaymentFailureRetryDays(internalCallContext).get(0);
// Move time for retry to happen
busHandler.pushExpectedEvents(NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(nbDaysBeforeRetry + 1);
assertListenerStatus();
// Second Parent invoice over Recurring period
parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
// Note that the parent still owns both invoices
assertEquals(parentInvoices.size(), 2);
parentInvoice = parentInvoices.get(1);
assertEquals(parentInvoice.getNumberOfItems(), 1);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
assertTrue(parentInvoice.isParentInvoice());
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
// Even if the child-parent mapping has been removed, the parent has retried and successfully paid the summary invoice
// TODO Should we automatically disable payment retries when un-parenting?
assertEquals(parentInvoice.getPayments().size(), 1);
assertEquals(paymentApi.getPayment(parentInvoice.getPayments().get(0).getPaymentId(), false, false, ImmutableList.<PluginProperty>of(), callContext).getPaymentMethodId(),
parentAccount.getPaymentMethodId());
// Second child invoice over Recurring period
childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
assertEquals(childInvoices.size(), 2);
assertEquals(childInvoices.get(1).getBalance().compareTo(BigDecimal.ZERO), 0);
// Verify balances (the parent has paid the summary invoice, so the child invoice is automatically paid)
assertEquals(invoiceUserApi.getAccountBalance(parentAccount.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
assertEquals(invoiceUserApi.getAccountBalance(childAccount.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addDays(29 - nbDaysBeforeRetry - 1);
assertListenerStatus();
// No new invoice for the parent
parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 2);
// Third child invoice over second Recurring period
childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
assertEquals(childInvoices.size(), 3);
assertEquals(childInvoices.get(2).getBalance().compareTo(BigDecimal.ZERO), 0);
// Verify the child paid the invoice this time
assertEquals(childInvoices.get(2).getPayments().size(), 1);
assertEquals(paymentApi.getPayment(childInvoices.get(2).getPayments().get(0).getPaymentId(), false, false, ImmutableList.<PluginProperty>of(), callContext).getPaymentMethodId(),
childAccount.getPaymentMethodId());
// Verify balances
assertEquals(invoiceUserApi.getAccountBalance(parentAccount.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
assertEquals(invoiceUserApi.getAccountBalance(childAccount.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
}
@Test(groups = "slow")
public void testParentingWithFuturePhaseEvent() 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));
Account childAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
// Verify mapping
childAccount = accountUserApi.getAccountById(childAccount.getId(), callContext);
assertNull(childAccount.getParentAccountId());
assertFalse(childAccount.isPaymentDelegatedToParent());
List<Account> childrenAccounts = accountUserApi.getChildrenAccounts(parentAccount.getId(), callContext);
assertEquals(childrenAccounts.size(), 0);
// Create subscription
createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
// First child invoice over TRIAL period
List<Invoice> childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
assertEquals(childInvoices.size(), 1);
assertEquals(childInvoices.get(0).getBalance().compareTo(BigDecimal.ZERO), 0);
// Add parent to the child -- the child still pays its invoices though
AccountModelDao childAccountModelDao = new AccountModelDao(childAccount.getId(), childAccount);
childAccountModelDao.setParentAccountId(parentAccount.getId());
childAccountModelDao.setIsPaymentDelegatedToParent(false);
accountUserApi.updateAccount(new DefaultAccount(childAccountModelDao), callContext);
// Verify mapping
childAccount = accountUserApi.getAccountById(childAccount.getId(), callContext);
assertEquals(childAccount.getParentAccountId(), parentAccount.getId());
assertFalse(childAccount.isPaymentDelegatedToParent());
childrenAccounts = accountUserApi.getChildrenAccounts(parentAccount.getId(), callContext);
assertEquals(childrenAccounts.size(), 1);
assertEquals(childrenAccounts.get(0).getId(), childAccount.getId());
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addDays(30);
assertListenerStatus();
// The parent still has no invoice
List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 0);
// Second child invoice over Recurring period
childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
assertEquals(childInvoices.size(), 2);
assertEquals(childInvoices.get(1).getBalance().compareTo(BigDecimal.ZERO), 0);
assertEquals(childInvoices.get(1).getPayments().size(), 1);
assertEquals(paymentApi.getPayment(childInvoices.get(1).getPayments().get(0).getPaymentId(), false, false, ImmutableList.<PluginProperty>of(), callContext).getPaymentMethodId(),
childAccount.getPaymentMethodId());
// The child now delegates its payments
childAccountModelDao = new AccountModelDao(childAccount.getId(), childAccount);
childAccountModelDao.setIsPaymentDelegatedToParent(true);
accountUserApi.updateAccount(new DefaultAccount(childAccountModelDao), callContext);
// Verify mapping
childAccount = accountUserApi.getAccountById(childAccount.getId(), callContext);
assertEquals(childAccount.getParentAccountId(), parentAccount.getId());
assertTrue(childAccount.isPaymentDelegatedToParent());
childrenAccounts = accountUserApi.getChildrenAccounts(parentAccount.getId(), callContext);
assertEquals(childrenAccounts.size(), 1);
assertEquals(childrenAccounts.get(0).getId(), childAccount.getId());
busHandler.pushExpectedEvents(NextEvent.INVOICE);
clock.addDays(30);
assertListenerStatus();
// Moving a day the NotificationQ calls the commitInvoice. No payment is expected
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addDays(1);
assertListenerStatus();
// The parent now owns the invoice
parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
assertEquals(parentInvoices.size(), 1);
final Invoice parentInvoice = parentInvoices.get(0);
assertEquals(parentInvoice.getNumberOfItems(), 1);
assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
assertTrue(parentInvoice.isParentInvoice());
assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
assertEquals(parentInvoice.getPayments().size(), 1);
assertEquals(paymentApi.getPayment(parentInvoice.getPayments().get(0).getPaymentId(), false, false, ImmutableList.<PluginProperty>of(), callContext).getPaymentMethodId(),
parentAccount.getPaymentMethodId());
// Third child invoice over Recurring period
childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
assertEquals(childInvoices.size(), 3);
assertEquals(childInvoices.get(2).getBalance().compareTo(BigDecimal.ZERO), 0);
}
}