use of org.killbill.billing.invoice.optimizer.InvoiceOptimizerBase.AccountInvoices in project killbill by killbill.
the class TestDefaultInvoiceGenerator method testInvoiceGeneration.
private void testInvoiceGeneration(final UUID accountId, final BillingEventSet events, final List<Invoice> existingInvoices, final LocalDate targetDate, final int expectedNumberOfItems, final BigDecimal expectedAmount) throws InvoiceApiException {
final Currency currency = Currency.USD;
final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, new AccountInvoices(null, null, existingInvoices), null, targetDate, currency, null, internalCallContext);
final Invoice invoice = invoiceWithMetadata.getInvoice();
assertNotNull(invoice);
assertEquals(invoice.getNumberOfItems(), expectedNumberOfItems);
existingInvoices.add(invoice);
distributeItems(existingInvoices);
assertEquals(invoice.getBalance(), KillBillMoney.of(expectedAmount, invoice.getCurrency()));
}
use of org.killbill.billing.invoice.optimizer.InvoiceOptimizerBase.AccountInvoices in project killbill by killbill.
the class TestDefaultInvoiceGenerator method testWithSingleThirtyDaysEvent.
@Test(groups = "fast")
public void testWithSingleThirtyDaysEvent() throws InvoiceApiException, CatalogApiException {
final BillingEventSet events = new MockBillingEventSet();
final SubscriptionBase sub = createSubscription();
final LocalDate startDate = invoiceUtil.buildDate(2011, 9, 1);
final Plan plan = new MockPlan();
final BigDecimal rate1 = TEN;
final PlanPhase phase = createMockThirtyDaysPlanPhase(rate1);
final BillingEvent event = createBillingEvent(sub.getId(), sub.getBundleId(), startDate, plan, phase, 1);
events.add(event);
final LocalDate targetDate = invoiceUtil.buildDate(2011, 10, 3);
final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, new AccountInvoices(), null, targetDate, Currency.USD, null, internalCallContext);
final Invoice invoice = invoiceWithMetadata.getInvoice();
assertNotNull(invoice);
assertEquals(invoice.getNumberOfItems(), 2);
assertEquals(invoice.getBalance(), KillBillMoney.of(TWENTY, invoice.getCurrency()));
assertEquals(invoice.getInvoiceItems().get(0).getSubscriptionId(), sub.getId());
assertEquals(invoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
assertEquals(invoice.getInvoiceItems().get(0).getStartDate(), new LocalDate(2011, 9, 1));
assertEquals(invoice.getInvoiceItems().get(0).getEndDate(), new LocalDate(2011, 10, 1));
assertEquals(invoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.RECURRING);
assertEquals(invoice.getInvoiceItems().get(1).getStartDate(), new LocalDate(2011, 10, 1));
assertEquals(invoice.getInvoiceItems().get(1).getEndDate(), new LocalDate(2011, 10, 31));
}
use of org.killbill.billing.invoice.optimizer.InvoiceOptimizerBase.AccountInvoices in project killbill by killbill.
the class TestDefaultInvoiceGenerator method testOnePlan_TwoMonthlyPhases_ChangeImmediate.
@Test(groups = "fast")
public void testOnePlan_TwoMonthlyPhases_ChangeImmediate() throws InvoiceApiException, CatalogApiException {
final BillingEventSet events = new MockBillingEventSet();
final Plan plan1 = new MockPlan();
final BigDecimal rate1 = FIVE;
final PlanPhase phase1 = createMockMonthlyPlanPhase(rate1);
final SubscriptionBase sub = createSubscription();
final BillingEvent event1 = createBillingEvent(sub.getId(), sub.getBundleId(), invoiceUtil.buildDate(2011, 9, 1), plan1, phase1, 1);
events.add(event1);
final BigDecimal rate2 = TEN;
final PlanPhase phase2 = createMockMonthlyPlanPhase(rate2);
final BillingEvent event2 = createBillingEvent(sub.getId(), sub.getBundleId(), invoiceUtil.buildDate(2011, 10, 15), plan1, phase2, 15);
events.add(event2);
final LocalDate targetDate = invoiceUtil.buildDate(2011, 12, 3);
final UUID accountId = UUID.randomUUID();
final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, new AccountInvoices(), null, targetDate, Currency.USD, null, internalCallContext);
final Invoice invoice = invoiceWithMetadata.getInvoice();
assertNotNull(invoice);
assertEquals(invoice.getNumberOfItems(), 4);
final BigDecimal numberOfCyclesEvent1;
numberOfCyclesEvent1 = ONE.add(FOURTEEN.divide(THIRTY_ONE, KillBillMoney.ROUNDING_METHOD));
final BigDecimal numberOfCyclesEvent2 = TWO;
BigDecimal expectedValue;
expectedValue = numberOfCyclesEvent1.multiply(rate1);
expectedValue = expectedValue.add(numberOfCyclesEvent2.multiply(rate2));
expectedValue = KillBillMoney.of(expectedValue, invoice.getCurrency());
assertEquals(invoice.getBalance(), expectedValue);
}
use of org.killbill.billing.invoice.optimizer.InvoiceOptimizerBase.AccountInvoices in project killbill by killbill.
the class InvoiceDispatcher method processAccountInternal.
private Invoice processAccountInternal(final boolean isApiCall, final boolean parkedAccount, final UUID accountId, @Nullable final LocalDate inputTargetDateMaybeNull, @Nullable final DryRunArguments dryRunArguments, final boolean isRescheduled, final InternalCallContext context) throws InvoiceApiException {
final boolean isDryRun = dryRunArguments != null;
final boolean upcomingInvoiceDryRun = isDryRun && DryRunType.UPCOMING_INVOICE.equals(dryRunArguments.getDryRunType());
LocalDate inputTargetDate = inputTargetDateMaybeNull;
// A null inputTargetDate is only allowed in UPCOMING_INVOICE dryRun mode to have the system compute it
if (inputTargetDate == null && !upcomingInvoiceDryRun) {
inputTargetDate = context.toLocalDate(clock.getUTCNow());
}
Preconditions.checkArgument(inputTargetDate != null || upcomingInvoiceDryRun, "inputTargetDate is required in non dryRun mode");
// Passed through invoice code to be propagated to usage module/plugins
final LocalDate dryRunInfoDate = isDryRun && dryRunArguments.getDryRunType() == DryRunType.SUBSCRIPTION_ACTION ? dryRunArguments.getEffectiveDate() : inputTargetDate;
final DryRunInfo dryRunInfo = isDryRun ? new DryRunInfo(dryRunArguments.getDryRunType(), dryRunInfoDate) : null;
final Map<InvoiceTiming, Long> invoiceTimings = new HashMap<>();
try {
long startNano = System.nanoTime();
final AccountInvoices accountInvoices = invoiceOptimizer.getInvoices(context);
invoiceTimings.put(InvoiceTiming.FETCH_INVOICES, System.nanoTime() - startNano);
// Make sure to first set the BCD if needed then get the account object (to have the BCD set)
startNano = System.nanoTime();
final BillingEventSet billingEvents = billingApi.getBillingEventsForAccountAndUpdateAccountBCD(accountId, dryRunArguments, accountInvoices.getBillingEventCutoffDate(), context);
invoiceTimings.put(InvoiceTiming.BILLING_EVENTS, System.nanoTime() - startNano);
if (!isApiCall && billingEvents.isAccountAutoInvoiceOff()) {
return null;
}
final Invoice invoice;
if (!isDryRun) {
final InvoiceWithFutureNotifications invoiceWithFutureNotifications = processAccountWithLockAndInputTargetDate(accountId, inputTargetDate, billingEvents, accountInvoices, dryRunInfo, isRescheduled, Lists.newLinkedList(), invoiceTimings, context);
invoice = invoiceWithFutureNotifications != null ? invoiceWithFutureNotifications.getInvoice() : null;
if (parkedAccount) {
try {
log.info("Illegal invoicing state fixed for accountId='{}', unparking account", accountId);
parkedAccountsManager.unparkAccount(accountId, context);
} catch (final TagApiException ignored) {
log.warn("Unable to unpark account", ignored);
}
}
} else /* Dry run use cases */
{
final NotificationQueue notificationQueue = notificationQueueService.getNotificationQueue(KILLBILL_SERVICES.INVOICE_SERVICE.getServiceName(), DefaultNextBillingDateNotifier.NEXT_BILLING_DATE_NOTIFIER_QUEUE);
final Iterable<NotificationEventWithMetadata<NextBillingDateNotificationKey>> futureNotificationsIterable = notificationQueue.getFutureNotificationForSearchKeys(context.getAccountRecordId(), context.getTenantRecordId());
// Copy the results as retrieving the iterator will issue a query each time. This also makes sure the underlying JDBC connection is closed.
final List<NotificationEventWithMetadata<NextBillingDateNotificationKey>> futureNotifications = ImmutableList.<NotificationEventWithMetadata<NextBillingDateNotificationKey>>copyOf(futureNotificationsIterable);
final Map<UUID, DateTime> nextScheduledSubscriptionsEventMap = getNextTransitionsForSubscriptions(billingEvents);
// List of all existing invoice notifications
final Set<LocalDate> allCandidateTargetDates = getUpcomingInvoiceCandidateDates(futureNotifications, nextScheduledSubscriptionsEventMap, ImmutableList.<UUID>of(), context);
if (dryRunArguments.getDryRunType() == DryRunType.UPCOMING_INVOICE) {
final Iterable<UUID> filteredSubscriptionIdsForDryRun = getFilteredSubscriptionIdsFor_UPCOMING_INVOICE_DryRun(dryRunArguments, billingEvents);
// List of existing invoice notifications associated to the filter set of subscriptionIds
final Set<LocalDate> filteredCandidateTargetDates = Iterables.isEmpty(filteredSubscriptionIdsForDryRun) ? allCandidateTargetDates : getUpcomingInvoiceCandidateDates(futureNotifications, nextScheduledSubscriptionsEventMap, filteredSubscriptionIdsForDryRun, context);
if (Iterables.isEmpty(filteredSubscriptionIdsForDryRun)) {
invoice = processDryRun_UPCOMING_INVOICE_Invoice(accountId, allCandidateTargetDates, billingEvents, accountInvoices, dryRunInfo, invoiceTimings, context);
} else {
invoice = processDryRun_UPCOMING_INVOICE_FILTERING_Invoice(accountId, filteredCandidateTargetDates, allCandidateTargetDates, billingEvents, accountInvoices, dryRunInfo, invoiceTimings, context);
}
} else /* DryRunType.TARGET_DATE, SUBSCRIPTION_ACTION */
{
invoice = processDryRun_TARGET_DATE_Invoice(accountId, inputTargetDate, allCandidateTargetDates, billingEvents, accountInvoices, dryRunInfo, invoiceTimings, context);
}
}
printInvoiceTiming(invoiceTimings);
return invoice;
} catch (final CatalogApiException e) {
log.warn("Failed to retrieve BillingEvents for accountId='{}', dryRunArguments='{}'", accountId, dryRunArguments, e);
return null;
} catch (final AccountApiException e) {
log.warn("Failed to retrieve BillingEvents for accountId='{}', dryRunArguments='{}'", accountId, dryRunArguments, e);
return null;
} catch (final SubscriptionBaseApiException e) {
log.warn("Failed to retrieve BillingEvents for accountId='{}', dryRunArguments='{}'", accountId, dryRunArguments, e);
return null;
} catch (final InvoiceApiException e) {
if (e.getCode() == ErrorCode.INVOICE_PLUGIN_API_ABORTED.getCode()) {
return null;
}
if (e.getCode() == ErrorCode.UNEXPECTED_ERROR.getCode() && !isDryRun) {
log.warn("Illegal invoicing state detected for accountId='{}', dryRunArguments='{}', parking account", accountId, dryRunArguments, e);
parkAccount(accountId, context);
}
throw e;
} catch (final NoSuchNotificationQueue e) {
throw new InvoiceApiException(ErrorCode.UNEXPECTED_ERROR, "Failed to retrieve future notifications from notificationQ");
}
}
use of org.killbill.billing.invoice.optimizer.InvoiceOptimizerBase.AccountInvoices in project killbill by killbill.
the class TestInvoiceDao method testInvoiceForFreeTrial.
@Test(groups = "slow")
public void testInvoiceForFreeTrial() throws InvoiceApiException, CatalogApiException {
final Currency currency = Currency.USD;
final DefaultPrice price = new DefaultPrice(BigDecimal.ZERO, Currency.USD);
final MockInternationalPrice fixedPrice = new MockInternationalPrice(price);
final MockPlanPhase phase = new MockPlanPhase(null, fixedPrice);
final MockPlan plan = new MockPlan(phase);
final SubscriptionBase subscription = getZombieSubscription();
final DateTime effectiveDate = invoiceUtil.buildDate(2011, 1, 1).toDateTimeAtStartOfDay();
final BillingEvent event = invoiceUtil.createMockBillingEvent(null, subscription, effectiveDate, plan, phase, fixedPrice.getPrice(currency), null, currency, BillingPeriod.MONTHLY, 15, BillingMode.IN_ADVANCE, "testEvent", 1L, SubscriptionBaseTransitionType.CREATE);
final BillingEventSet events = new MockBillingEventSet();
events.add(event);
final LocalDate targetDate = invoiceUtil.buildDate(2011, 1, 15);
final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, new AccountInvoices(), null, targetDate, Currency.USD, null, context);
final Invoice invoice = invoiceWithMetadata.getInvoice();
assertNotNull(invoice);
}
Aggregations