use of org.killbill.billing.invoice.api.InvoiceItem in project killbill by killbill.
the class TestBundleTransfer method testBundleTransferWithBPMonthlyOnly.
@Test(groups = "slow")
public void testBundleTransferWithBPMonthlyOnly() throws Exception {
// Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
final DateTime initialDate = new DateTime(2012, 4, 1, 0, 15, 42, 0, testTimeZone);
clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.MONTHLY;
final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
//
// CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
//
final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
assertNotNull(bpEntitlement);
assertListenerStatus();
assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
// Move out of trials for interesting invoices adjustments
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(32);
assertListenerStatus();
// BUNDLE TRANSFER
final Account newAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
busHandler.pushExpectedEvents(NextEvent.TRANSFER, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
transferApi.transferBundle(account.getId(), newAccount.getId(), "externalKey", clock.getUTCNow(), false, false, callContext);
assertListenerStatus();
// Verify the BCD of the new account
final Integer oldBCD = accountUserApi.getAccountById(account.getId(), callContext).getBillCycleDayLocal();
final Integer newBCD = accountUserApi.getAccountById(newAccount.getId(), callContext).getBillCycleDayLocal();
assertEquals(oldBCD, (Integer) 1);
// Day of the transfer
assertEquals(newBCD, (Integer) 3);
final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(newAccount.getId(), false, callContext);
assertEquals(invoices.size(), 1);
final List<InvoiceItem> invoiceItems = invoices.get(0).getInvoiceItems();
assertEquals(invoiceItems.size(), 1);
final InvoiceItem theItem = invoiceItems.get(0);
assertTrue(theItem.getStartDate().compareTo(new LocalDate(2012, 5, 3)) == 0);
assertTrue(theItem.getEndDate().compareTo(new LocalDate(2012, 6, 3)) == 0);
assertTrue(theItem.getAmount().compareTo(new BigDecimal("249.95")) == 0);
checkNoMoreInvoiceToGenerate(account);
}
use of org.killbill.billing.invoice.api.InvoiceItem in project killbill by killbill.
the class TestIntegrationParentInvoice method testParentInvoiceGeneration.
@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);
}
use of org.killbill.billing.invoice.api.InvoiceItem in project killbill by killbill.
the class DefaultInvoiceGenerator method generateInvoice.
/*
* adjusts target date to the maximum invoice target date, if future invoices exist
*/
@Override
public InvoiceWithMetadata generateInvoice(final ImmutableAccountData account, @Nullable final BillingEventSet events, @Nullable final List<Invoice> existingInvoices, final LocalDate targetDate, final Currency targetCurrency, final InternalCallContext context) throws InvoiceApiException {
if ((events == null) || (events.size() == 0) || events.isAccountAutoInvoiceOff()) {
return new InvoiceWithMetadata(null, ImmutableMap.<UUID, SubscriptionFutureNotificationDates>of());
}
validateTargetDate(targetDate, context);
final LocalDate adjustedTargetDate = adjustTargetDate(existingInvoices, targetDate);
final LocalDate invoiceDate = context.toLocalDate(context.getCreatedDate());
final DefaultInvoice invoice = new DefaultInvoice(account.getId(), invoiceDate, adjustedTargetDate, targetCurrency);
final UUID invoiceId = invoice.getId();
final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates = new HashMap<UUID, SubscriptionFutureNotificationDates>();
final List<InvoiceItem> fixedAndRecurringItems = recurringInvoiceItemGenerator.generateItems(account, invoiceId, events, existingInvoices, adjustedTargetDate, targetCurrency, perSubscriptionFutureNotificationDates, context);
invoice.addInvoiceItems(fixedAndRecurringItems);
final List<InvoiceItem> usageItems = usageInvoiceItemGenerator.generateItems(account, invoiceId, events, existingInvoices, adjustedTargetDate, targetCurrency, perSubscriptionFutureNotificationDates, context);
invoice.addInvoiceItems(usageItems);
return new InvoiceWithMetadata(invoice.getInvoiceItems().isEmpty() ? null : invoice, perSubscriptionFutureNotificationDates);
}
use of org.killbill.billing.invoice.api.InvoiceItem in project killbill by killbill.
the class FixedAndRecurringInvoiceItemGenerator method processRecurringBillingEvents.
private void processRecurringBillingEvents(final UUID invoiceId, final UUID accountId, final BillingEventSet events, final LocalDate targetDate, final Currency currency, final List<InvoiceItem> proposedItems, final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate, @Nullable final List<Invoice> existingInvoices, final InternalCallContext internalCallContext) throws InvoiceApiException {
if (events.isEmpty()) {
return;
}
// Pretty-print the generated invoice items from the junction events
final InvoiceItemGeneratorLogger invoiceItemGeneratorLogger = new InvoiceItemGeneratorLogger(invoiceId, accountId, "recurring", log);
final Iterator<BillingEvent> eventIt = events.iterator();
BillingEvent nextEvent = eventIt.next();
while (eventIt.hasNext()) {
final BillingEvent thisEvent = nextEvent;
nextEvent = eventIt.next();
if (!events.getSubscriptionIdsWithAutoInvoiceOff().contains(thisEvent.getSubscription().getId())) {
// don't consider events for subscriptions that have auto_invoice_off
final BillingEvent adjustedNextEvent = (thisEvent.getSubscription().getId() == nextEvent.getSubscription().getId()) ? nextEvent : null;
final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, thisEvent, adjustedNextEvent, targetDate, currency, invoiceItemGeneratorLogger, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, internalCallContext);
proposedItems.addAll(newProposedItems);
}
}
final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, nextEvent, null, targetDate, currency, invoiceItemGeneratorLogger, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, internalCallContext);
proposedItems.addAll(newProposedItems);
invoiceItemGeneratorLogger.logItems();
}
use of org.killbill.billing.invoice.api.InvoiceItem in project killbill by killbill.
the class FixedAndRecurringInvoiceItemGenerator method updatePerSubscriptionNextNotificationDate.
private void updatePerSubscriptionNextNotificationDate(final UUID subscriptionId, final LocalDate nextBillingCycleDate, final List<InvoiceItem> newProposedItems, final BillingMode billingMode, final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates) {
LocalDate nextNotificationDate = null;
switch(billingMode) {
case IN_ADVANCE:
for (final InvoiceItem item : newProposedItems) {
if ((item.getEndDate() != null) && (item.getAmount() == null || item.getAmount().compareTo(BigDecimal.ZERO) >= 0)) {
if (nextNotificationDate == null) {
nextNotificationDate = item.getEndDate();
} else {
nextNotificationDate = nextNotificationDate.compareTo(item.getEndDate()) > 0 ? nextNotificationDate : item.getEndDate();
}
}
}
break;
case IN_ARREAR:
nextNotificationDate = nextBillingCycleDate;
break;
default:
throw new IllegalStateException("Unrecognized billing mode " + billingMode);
}
if (nextNotificationDate != null) {
SubscriptionFutureNotificationDates subscriptionFutureNotificationDates = perSubscriptionFutureNotificationDates.get(subscriptionId);
if (subscriptionFutureNotificationDates == null) {
subscriptionFutureNotificationDates = new SubscriptionFutureNotificationDates(billingMode);
perSubscriptionFutureNotificationDates.put(subscriptionId, subscriptionFutureNotificationDates);
}
subscriptionFutureNotificationDates.updateNextRecurringDateIfRequired(nextNotificationDate);
}
}
Aggregations