final Currency targetCurrency, final InternalCallContext context) throws InvoiceApiException { if ((events == null) || (events.size() == 0) || events.isAccountAutoInvoiceOff()) { return new InvoiceWithMetadata(null, ImmutableSet.of(), ImmutableMap.<UUID, SubscriptionFutureNotificationDates>of()); final InvoiceStatus invoiceStatus = events.isAccountAutoInvoiceDraft() ? InvoiceStatus.DRAFT : InvoiceStatus.COMMITTED; final DefaultInvoice invoice = targetInvoiceId != null ? new DefaultInvoice(targetInvoiceId, account.getId(), null, invoiceDate, adjustedTargetDate, targetCurrency, false, invoiceStatus) :
@Test(groups = "fast") public void testZeroDollarEvents() throws InvoiceApiException, CatalogApiException { final Plan plan = new MockPlan(); final PlanPhase planPhase = createMockMonthlyPlanPhase(ZERO); final BillingEventSet events = new MockBillingEventSet(); final LocalDate targetDate = invoiceUtil.buildDate(2011, 1, 1); events.add(createBillingEvent(UUID.randomUUID(), UUID.randomUUID(), targetDate, plan, planPhase, 1)); final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, null, targetDate, Currency.USD, internalCallContext); final Invoice invoice = invoiceWithMetadata.getInvoice(); assertNotNull(invoice); assertEquals(invoice.getInvoiceItems().size(), 1); assertEquals(invoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.ZERO), 0); }
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, 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, thisEvent.getPlan().getRecurringBillingMode(), perSubscriptionFutureNotificationDate, internalCallContext); proposedItems.addAll(newProposedItems); } } final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, nextEvent, null, targetDate, currency, invoiceItemGeneratorLogger, nextEvent.getPlan().getRecurringBillingMode(), perSubscriptionFutureNotificationDate, internalCallContext); proposedItems.addAll(newProposedItems); invoiceItemGeneratorLogger.logItems(); }
@VisibleForTesting void processFixedBillingEvents(final UUID invoiceId, final UUID accountId, final BillingEventSet events, final LocalDate targetDate, final Currency currency, final List<InvoiceItem> proposedItems, final InternalCallContext internalCallContext) throws InvoiceApiException { if (events.isEmpty()) { return; } InvoiceItem prevItem = null; // Pretty-print the generated invoice items from the junction events final InvoiceItemGeneratorLogger invoiceItemGeneratorLogger = new InvoiceItemGeneratorLogger(invoiceId, accountId, "fixed", log); final Iterator<BillingEvent> eventIt = events.iterator(); while (eventIt.hasNext()) { final BillingEvent thisEvent = eventIt.next(); final InvoiceItem currentFixedPriceItem = generateFixedPriceItem(invoiceId, accountId, thisEvent, targetDate, currency, invoiceItemGeneratorLogger, internalCallContext); if (!isSameDayAndSameSubscription(prevItem, thisEvent, internalCallContext) && prevItem != null) { proposedItems.add(prevItem); } prevItem = currentFixedPriceItem; } // The last one if not null can always be inserted as there is nothing after to cancel it off. if (prevItem != null) { proposedItems.add(prevItem); } invoiceItemGeneratorLogger.logItems(); }
final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates, final InternalCallContext internalCallContext) throws InvoiceApiException { final Map<UUID, List<InvoiceItem>> perSubscriptionInArrearUsageItems = extractPerSubscriptionExistingInArrearUsageItems(eventSet.getUsages(), existingInvoices); try { final Iterator<BillingEvent> events = eventSet.iterator(); rawUsgRes = rawUsageOptimizer.getInArrearUsage(minBillingEventDate, targetDate, Iterables.concat(perSubscriptionInArrearUsageItems.values()), eventSet.getUsages(), internalCallContext);
if (billingEvents.isEmpty()) { return null; final List<Invoice> existingInvoices = billingEvents.isAccountAutoInvoiceOff() ? ImmutableList.<Invoice>of() : ImmutableList.<Invoice>copyOf(Collections2.transform(invoiceDao.getInvoicesByAccount(false, context),
events.add(createBillingEvent(originalSubscription.getId(), originalSubscription.getBundleId(), april25, originalPlan, originalPlanEvergreen, 25)); events.clear(); final SubscriptionBase newSubscription = createSubscription(); final Plan newPlan = new MockPlan("new plan"); final MockInternationalPrice price5 = new MockInternationalPrice(new DefaultPrice(FIVE, Currency.USD)); final PlanPhase newPlanEvergreen = new MockPlanPhase(price5, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN); events.add(createBillingEvent(newSubscription.getId(), originalSubscription.getBundleId(), april25, newPlan, newPlanEvergreen, 25));
private InvoiceWithMetadata generateKillBillInvoice(final ImmutableAccountData account, final LocalDate targetDate, final BillingEventSet billingEvents, final List<Invoice> existingInvoices, final InternalCallContext context) throws InvoiceApiException { final UUID targetInvoiceId; // Filter out DRAFT invoices for computation of existing items unless Account is in AUTO_INVOICING_REUSE_DRAFT if (billingEvents.isAccountAutoInvoiceReuseDraft()) { final Invoice existingDraft = Iterables.tryFind(existingInvoices, new Predicate<Invoice>() { @Override public boolean apply(final Invoice input) { return input.getStatus() == InvoiceStatus.DRAFT; } }).orNull(); targetInvoiceId = existingDraft != null ? existingDraft.getId() : null; } else { targetInvoiceId = null; } return generator.generateInvoice(account, billingEvents, existingInvoices, targetInvoiceId, targetDate, account.getCurrency(), context); }
private void processSubscriptionStartRequestedDateWithLock(final UUID accountId, final RequestedSubscriptionInternalEvent transition, final InternalCallContext context) { try { final BillingEventSet billingEvents = billingApi.getBillingEventsForAccountAndUpdateAccountBCD(accountId, null, context); if (billingEvents.isEmpty()) { return; } final FutureAccountNotificationsBuilder notificationsBuilder = new FutureAccountNotificationsBuilder(); populateNextFutureDryRunNotificationDate(billingEvents, notificationsBuilder, context); final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, context); commitInvoiceAndSetFutureNotifications(account, notificationsBuilder.build(), context); } catch (final SubscriptionBaseApiException e) { log.warn("Failed handling SubscriptionBase change.", new InvoiceApiException(ErrorCode.INVOICE_NO_ACCOUNT_ID_FOR_SUBSCRIPTION_ID, transition.getSubscriptionId().toString())); } catch (final AccountApiException e) { log.warn("Failed to retrieve BillingEvents for accountId='{}'", accountId, e); } catch (final CatalogApiException e) { log.warn("Failed to retrieve BillingEvents for accountId='{}'", accountId, e); } }
for (final InvoiceItem item : invoice.getInvoiceItems()) { if (item.getSubscriptionId() == null || // Always include migration invoices, credits, external charges etc. !eventSet.getSubscriptionIdsWithAutoInvoiceOff()
@Test(groups = "fast") public void testProcessFixedBillingEventsWithCancellationOnSameDay() throws InvoiceApiException { final LocalDate targetDate = new LocalDate("2016-01-08"); final UUID invoiceId = UUID.randomUUID(); final BillingEventSet events = new MockBillingEventSet(); final BigDecimal fixedPriceAmount = BigDecimal.TEN; final MockInternationalPrice fixedPrice = new MockInternationalPrice(new DefaultPrice(fixedPriceAmount, Currency.USD)); final Plan plan = new MockPlan("my-plan"); final PlanPhase phase = new MockPlanPhase(null, fixedPrice, BillingPeriod.NO_BILLING_PERIOD, PhaseType.TRIAL); final BillingEvent event1 = invoiceUtil.createMockBillingEvent(account, subscription, new DateTime("2016-01-08"), plan, phase, fixedPriceAmount, null, Currency.USD, BillingPeriod.NO_BILLING_PERIOD, 1, BillingMode.IN_ADVANCE, "Billing Event Desc", 1L, SubscriptionBaseTransitionType.CREATE); events.add(event1); final BillingEvent event2 = invoiceUtil.createMockBillingEvent(account, subscription, new DateTime("2016-01-08"), plan, phase, null, null, Currency.USD, BillingPeriod.NO_BILLING_PERIOD, 1, BillingMode.IN_ADVANCE, "Billing Event Desc", 2L, SubscriptionBaseTransitionType.CANCEL); events.add(event2); final List<InvoiceItem> proposedItems = new ArrayList<InvoiceItem>(); fixedAndRecurringInvoiceItemGenerator.processFixedBillingEvents(invoiceId, account.getId(), events, targetDate, Currency.USD, proposedItems, internalCallContext); assertTrue(proposedItems.isEmpty()); }
@Test(groups = "fast") public void testProcessFixedBillingEventsWithCancellationOnNextDay() throws InvoiceApiException { final LocalDate targetDate = new LocalDate("2016-01-08"); final UUID invoiceId = UUID.randomUUID(); final BillingEventSet events = new MockBillingEventSet(); final BigDecimal fixedPriceAmount = BigDecimal.TEN; final MockInternationalPrice fixedPrice = new MockInternationalPrice(new DefaultPrice(fixedPriceAmount, Currency.USD)); final Plan plan = new MockPlan("my-plan"); final PlanPhase phase = new MockPlanPhase(null, fixedPrice, BillingPeriod.NO_BILLING_PERIOD, PhaseType.TRIAL); final BillingEvent event1 = invoiceUtil.createMockBillingEvent(account, subscription, new DateTime("2016-01-08"), plan, phase, fixedPriceAmount, null, Currency.USD, BillingPeriod.NO_BILLING_PERIOD, 1, BillingMode.IN_ADVANCE, "Billing Event Desc", 1L, SubscriptionBaseTransitionType.CREATE); events.add(event1); final BillingEvent event2 = invoiceUtil.createMockBillingEvent(account, subscription, new DateTime("2016-01-09"), plan, phase, null, null, Currency.USD, BillingPeriod.NO_BILLING_PERIOD, 1, BillingMode.IN_ADVANCE, "Billing Event Desc", 2L, SubscriptionBaseTransitionType.CANCEL); events.add(event2); final List<InvoiceItem> proposedItems = new ArrayList<InvoiceItem>(); fixedAndRecurringInvoiceItemGenerator.processFixedBillingEvents(invoiceId, account.getId(), events, targetDate, Currency.USD, proposedItems, internalCallContext); assertEquals(proposedItems.size(), 1); assertEquals(proposedItems.get(0).getInvoiceItemType(), InvoiceItemType.FIXED); assertEquals(proposedItems.get(0).getAmount().compareTo(fixedPriceAmount), 0); }
public UUID generateRegularInvoice(final Account account, final BigDecimal fixedPrice, final BigDecimal recurringPrice, final LocalDate targetDate, final CallContext callContext) throws Exception { final SubscriptionBase subscription = Mockito.mock(SubscriptionBase.class); Mockito.when(subscription.getId()).thenReturn(UUID.randomUUID()); Mockito.when(subscription.getBundleId()).thenReturn(new UUID(0L, 0L)); final BillingEventSet events = new MockBillingEventSet(); final Plan plan = MockPlan.createBicycleNoTrialEvergreen1USD(); final PlanPhase planPhase = MockPlanPhase.create1USDMonthlyEvergreen(); final DateTime effectiveDate = new DateTime().minusDays(1); final Currency currency = Currency.USD; events.add(createMockBillingEvent(account, subscription, effectiveDate, plan, planPhase, fixedPrice, recurringPrice, currency, BillingPeriod.MONTHLY, 1, BillingMode.IN_ADVANCE, "", 1L, SubscriptionBaseTransitionType.CREATE)); Mockito.when(billingApi.getBillingEventsForAccountAndUpdateAccountBCD(Mockito.<UUID>any(), Mockito.<DryRunArguments>any(), Mockito.<InternalCallContext>any())).thenReturn(events); final InternalCallContext context = internalCallContextFactory.createInternalCallContext(account.getId(), callContext); Invoice invoice = generateInvoice(account.getId(), targetDate, new DryRunFutureDateArguments(), context); Assert.assertNotNull(invoice); List<InvoiceModelDao> invoices = invoiceDao.getInvoicesByAccount(false, context); Assert.assertEquals(invoices.size(), 0); invoice = generateInvoice(account.getId(), targetDate, null, context); Assert.assertNotNull(invoice); invoices = invoiceDao.getInvoicesByAccount(false, context); Assert.assertEquals(invoices.size(), 1); return invoice.getId(); }
@Test(groups = "fast", expectedExceptions = {InvoiceApiException.class}) public void testTargetDateRestrictionFailure() throws InvoiceApiException, CatalogApiException { final LocalDate targetDate = clock.getUTCToday().plusMonths(60); final BillingEventSet events = new MockBillingEventSet(); final Plan plan1 = new MockPlan(); final PlanPhase phase1 = createMockMonthlyPlanPhase(null, ZERO, PhaseType.TRIAL); events.add(createBillingEvent(UUID.randomUUID(), UUID.randomUUID(), clock.getUTCToday(), plan1, phase1, 1)); generator.generateInvoice(account, events, null, null, targetDate, Currency.USD, internalCallContext); }
events.add(invoiceUtil.createMockBillingEvent(account, subscription, new DateTime("2012-05-01T00:03:42.000Z"), bicycleTrialEvergreen1USD, new MockPlanPhase(bicycleTrialEvergreen1USD, PhaseType.TRIAL), BigDecimal.ZERO, null, account.getCurrency(), BillingPeriod.NO_BILLING_PERIOD, 31, BillingMode.IN_ADVANCE, "CREATE", 1L, SubscriptionBaseTransitionType.CREATE)); events.add(invoiceUtil.createMockBillingEvent(account, subscription, new DateTime("2012-05-31T00:03:42.000Z"), bicycleTrialEvergreen1USD, new MockPlanPhase(bicycleTrialEvergreen1USD, PhaseType.EVERGREEN), null, new BigDecimal("249.95"), account.getCurrency(), BillingPeriod.MONTHLY, 31, BillingMode.IN_ADVANCE, "PHASE", 2L, SubscriptionBaseTransitionType.PHASE)); events.add(invoiceUtil.createMockBillingEvent(account, subscription, new DateTime("2012-07-15T00:00:00.000Z"), bicycleTrialEvergreen1USD, new MockPlanPhase(bicycleTrialEvergreen1USD, PhaseType.EVERGREEN), null, null, account.getCurrency(), BillingPeriod.NO_BILLING_PERIOD, 31, BillingMode.IN_ADVANCE, "", 0L, SubscriptionBaseTransitionType.START_BILLING_DISABLED)); events.add(invoiceUtil.createMockBillingEvent(account, subscription, new DateTime("2012-07-25T00:00:00.000Z"), bicycleTrialEvergreen1USD, new MockPlanPhase(bicycleTrialEvergreen1USD, PhaseType.EVERGREEN), null, new BigDecimal("249.95"), account.getCurrency(), BillingPeriod.MONTHLY, 31, BillingMode.IN_ADVANCE, "", 1L, SubscriptionBaseTransitionType.END_BILLING_DISABLED)); events.add(invoiceUtil.createMockBillingEvent(account, subscription, new DateTime("2012-07-25T00:04:00.000Z"), jetTrialEvergreen1000USD, new MockPlanPhase(jetTrialEvergreen1000USD, PhaseType.EVERGREEN), null, new BigDecimal("1000"), account.getCurrency(), BillingPeriod.MONTHLY, 31, BillingMode.IN_ADVANCE, "CHANGE", 3L, SubscriptionBaseTransitionType.CHANGE));
final Currency currency = Currency.USD; final BigDecimal fixedPrice = null; events.add(invoiceUtil.createMockBillingEvent(account, subscription, effectiveDate, plan, planPhase, fixedPrice, BigDecimal.ONE, currency, BillingPeriod.MONTHLY, 1, BillingMode.IN_ADVANCE, "", 1L, SubscriptionBaseTransitionType.CREATE));
@Test(groups = "fast") public void testEndDateIsCorrect() throws InvoiceApiException, CatalogApiException { final Plan plan = new MockPlan(); final PlanPhase planPhase = createMockMonthlyPlanPhase(ONE); final BillingEventSet events = new MockBillingEventSet(); final LocalDate startDate = clock.getUTCToday().minusDays(1); final LocalDate targetDate = startDate.plusDays(1); events.add(createBillingEvent(UUID.randomUUID(), UUID.randomUUID(), startDate, plan, planPhase, startDate.getDayOfMonth())); final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, null, targetDate, Currency.USD, internalCallContext); final Invoice invoice = invoiceWithMetadata.getInvoice(); final RecurringInvoiceItem item = (RecurringInvoiceItem) invoice.getInvoiceItems().get(0); // end date of the invoice item should be equal to exactly one month later (rounded) assertEquals(item.getEndDate(), startDate.plusMonths(1)); }
@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, null, null, targetDate, Currency.USD, 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)); }
@Test(groups = "fast") public void testWithSingleMonthlyEvent() 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 = createMockMonthlyPlanPhase(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, null, null, targetDate, Currency.USD, 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, 11, 1)); }