use of org.killbill.billing.entitlement.api.Subscription in project killbill by killbill.
the class TestWithBCDUpdate method testBCDChangeAfterTrialFollowOtherBCDChange.
@Test(groups = "slow")
public void testBCDChangeAfterTrialFollowOtherBCDChange() throws Exception {
final DateTime initialDate = new DateTime(2016, 4, 1, 0, 13, 42, 0, testTimeZone);
clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
assertNotNull(account);
// BP creation : Will set Account BCD to the first (2016-4-1 + 30 days = 2016-5-1)
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.MONTHLY;
final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
// 2016-5-1 : BP out of TRIAL
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(30);
assertListenerStatus();
// Set next BCD to be the 15
subscriptionBaseInternalApi.updateBCD(baseEntitlement.getId(), 15, null, internalCallContext);
Thread.sleep(1000);
assertListenerStatus();
// 2016-5-15 : Catch BCD_CHANGE event and repair invoice accordingly
busHandler.pushExpectedEvents(NextEvent.BCD_CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(14);
assertListenerStatus();
final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, false, callContext);
expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 6, 1), new LocalDate(2016, 6, 15), InvoiceItemType.RECURRING, new BigDecimal("112.88")));
invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, expectedInvoices);
expectedInvoices.clear();
// 2016-6-01 : Original notification for 2016-6-01 (prior BCD change)
busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE);
clock.addDays(17);
assertListenerStatus();
// 2016-6-15
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(14);
assertListenerStatus();
invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, false, callContext);
expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 6, 15), new LocalDate(2016, 7, 15), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
invoiceChecker.checkInvoice(invoices.get(3).getId(), callContext, expectedInvoices);
expectedInvoices.clear();
// Set next BCD to be the 10
subscriptionBaseInternalApi.updateBCD(baseEntitlement.getId(), 10, null, internalCallContext);
Thread.sleep(1000);
assertListenerStatus();
// 2016-7-10
busHandler.pushExpectedEvents(NextEvent.BCD_CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(25);
assertListenerStatus();
invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, false, callContext);
expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 7, 15), new LocalDate(2016, 8, 10), InvoiceItemType.RECURRING, new BigDecimal("209.64")));
invoiceChecker.checkInvoice(invoices.get(4).getId(), callContext, expectedInvoices);
expectedInvoices.clear();
clock.addDays(3);
busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.INVOICE);
final Entitlement cancelledEntitlement = baseEntitlement.cancelEntitlementWithPolicyOverrideBillingPolicy(EntitlementActionPolicy.IMMEDIATE, BillingActionPolicy.START_OF_TERM, null, callContext);
assertListenerStatus();
final Subscription subscription = subscriptionApi.getSubscriptionForEntitlementId(cancelledEntitlement.getId(), callContext);
assertEquals(subscription.getEffectiveEndDate().compareTo(new LocalDate(2016, 7, 13)), 0);
assertEquals(subscription.getBillingEndDate().compareTo(new LocalDate(2016, 7, 10)), 0);
}
use of org.killbill.billing.entitlement.api.Subscription in project killbill by killbill.
the class TestWithBCDUpdate method testBCDChangeInTrial.
@Test(groups = "slow")
public void testBCDChangeInTrial() throws Exception {
final DateTime initialDate = new DateTime(2016, 4, 1, 0, 13, 42, 0, testTimeZone);
clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
assertNotNull(account);
// BP creation : Will set Account BCD to the first (2016-4-1 + 30 days = 2016-5-1)
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.MONTHLY;
final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
// 2016-4-4 : (BP still in TRIAL)
clock.addDays(3);
// Set next BCD to be the 15
subscriptionBaseInternalApi.updateBCD(baseEntitlement.getId(), 15, null, internalCallContext);
Thread.sleep(1000);
assertListenerStatus();
// 2016-5-15 : Catch BCD_CHANGE event
busHandler.pushExpectedEvents(NextEvent.BCD_CHANGE, NextEvent.NULL_INVOICE);
clock.addDays(11);
assertListenerStatus();
// 2016-5-1 : BP out of TRIAL
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(16);
assertListenerStatus();
final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, false, callContext);
expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 1), new LocalDate(2016, 5, 15), InvoiceItemType.RECURRING, new BigDecimal("116.64")));
invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, expectedInvoices);
expectedInvoices.clear();
// 2016-5-15 : NEW BCD
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(14);
assertListenerStatus();
expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 15), new LocalDate(2016, 6, 15), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, false, callContext);
invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, expectedInvoices);
expectedInvoices.clear();
// Add cancellation with START_OF_TERM to verify BCD update is correctly interpreted
clock.addDays(3);
busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.INVOICE);
final Entitlement cancelledEntitlement = baseEntitlement.cancelEntitlementWithPolicyOverrideBillingPolicy(EntitlementActionPolicy.IMMEDIATE, BillingActionPolicy.START_OF_TERM, null, callContext);
assertListenerStatus();
final Subscription subscription = subscriptionApi.getSubscriptionForEntitlementId(cancelledEntitlement.getId(), callContext);
assertEquals(subscription.getEffectiveEndDate().compareTo(new LocalDate(2016, 5, 18)), 0);
assertEquals(subscription.getBillingEndDate().compareTo(new LocalDate(2016, 5, 15)), 0);
}
use of org.killbill.billing.entitlement.api.Subscription in project killbill by killbill.
the class TestCatalogWithEvents method testChangeWithUsagePlan.
@Test(groups = "slow")
public void testChangeWithUsagePlan() throws Exception {
final LocalDate today = new LocalDate(2020, 1, 1);
clock.setDay(today);
final VersionedCatalog catalog = catalogUserApi.getCatalog("foo", callContext);
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("water-monthly", null);
busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
final UUID subscriptionId = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec, null, null, null), UUID.randomUUID().toString(), clock.getUTCToday(), clock.getUTCToday(), false, true, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
recordUsageData(subscriptionId, "t1", "liter", new LocalDate(2020, 1, 1), 10L, callContext);
recordUsageData(subscriptionId, "t2", "liter", new LocalDate(2020, 1, 23), 10L, callContext);
// 2020-2-1
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addMonths(1);
assertListenerStatus();
final Invoice invoice1 = invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2020, 1, 1), new LocalDate(2020, 2, 1), InvoiceItemType.USAGE, new BigDecimal("30.00")));
invoiceChecker.checkTrackingIds(invoice1, ImmutableSet.of("t1", "t2"), internalCallContext);
Assert.assertTrue(invoice1.getInvoiceItems().get(0).getCatalogEffectiveDate().toDate().compareTo(catalog.getVersions().get(0).getEffectiveDate()) == 0);
final Subscription subscription1 = subscriptionApi.getSubscriptionForEntitlementId(subscriptionId, callContext);
final List<SubscriptionEvent> events1 = subscription1.getSubscriptionEvents();
Assert.assertEquals(events1.size(), 2);
Assert.assertTrue(events1.get(0).getNextPlan().getCatalog().getEffectiveDate().compareTo(catalog.getVersions().get(0).getEffectiveDate()) == 0);
// 2020-2-16 (V2 effDt = 2020-2-15)
clock.addDays(15);
busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE);
subscription1.changePlanWithDate(new DefaultEntitlementSpecifier(spec), clock.getUTCToday(), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
final Subscription subscription2 = subscriptionApi.getSubscriptionForEntitlementId(subscriptionId, callContext);
final List<SubscriptionEvent> events2 = subscription2.getSubscriptionEvents();
Assert.assertEquals(events2.size(), 3);
Assert.assertTrue(events2.get(0).getNextPlan().getCatalog().getEffectiveDate().compareTo(catalog.getVersions().get(0).getEffectiveDate()) == 0);
// 2020-03-01
busHandler.pushExpectedEvents(NextEvent.INVOICE);
clock.addDays(14);
assertListenerStatus();
// 2020-3-16 (V3 effDt = 2020-3-15)
clock.addDays(15);
busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE);
subscription1.changePlanWithDate(new DefaultEntitlementSpecifier(spec), clock.getUTCToday(), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
final Subscription subscription3 = subscriptionApi.getSubscriptionForEntitlementId(subscriptionId, callContext);
final List<SubscriptionEvent> events3 = subscription3.getSubscriptionEvents();
Assert.assertEquals(events3.size(), 4);
Assert.assertTrue(events3.get(0).getNextPlan().getCatalog().getEffectiveDate().compareTo(catalog.getVersions().get(0).getEffectiveDate()) == 0);
Assert.assertTrue(events3.get(1).getNextPlan().getCatalog().getEffectiveDate().compareTo(catalog.getVersions().get(0).getEffectiveDate()) == 0);
// Change catalog V2
Assert.assertTrue(events3.get(2).getNextPlan().getCatalog().getEffectiveDate().compareTo(catalog.getVersions().get(1).getEffectiveDate()) == 0);
// Change catalog V3
Assert.assertTrue(events3.get(3).getNextPlan().getCatalog().getEffectiveDate().compareTo(catalog.getVersions().get(2).getEffectiveDate()) == 0);
}
use of org.killbill.billing.entitlement.api.Subscription in project killbill by killbill.
the class TestCatalogRetireElements method testChangePlanTwiceWithNewPlan.
@Test(groups = "slow", description = "See https://github.com/killbill/killbill/issues/1110")
public void testChangePlanTwiceWithNewPlan() throws Exception {
// Catalog v1 starts in 2011-01-01
// Catalog v2 starts in 2015-12-01
// -> Start on catalog V1
final LocalDate today = new LocalDate(2015, 11, 5);
// Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
clock.setDay(today);
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(null));
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.MONTHLY;
final PlanPhaseSpecifier spec1 = new PlanPhaseSpecifier(productName, term, "DEFAULT", null);
busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
final UUID bpEntitlementId = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec1), "externalKey", null, null, false, true, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
Entitlement bpEntitlement = entitlementApi.getEntitlementForId(bpEntitlementId, callContext);
// Move out a month. Date > catalog V2
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addMonths(1);
assertListenerStatus();
// Current date is > catalog V2
// Change to a plan that exists in V2 but not in V1
final PlanPhaseSpecifier spec2 = new PlanPhaseSpecifier("bazooka-monthly", null);
busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
bpEntitlement = bpEntitlement.changePlanWithDate(new DefaultEntitlementSpecifier(spec2), clock.getUTCToday(), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
// Change back to original plan: The code (subscription) chooses the latest version of the catalog when making the change and therefore the call succeeds
busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE);
bpEntitlement.changePlanWithDate(new DefaultEntitlementSpecifier(spec1), clock.getUTCToday(), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
//
// The code normally goes through the grandfathering logic to find the version but specifies the transitionTime of the latest CHANGE (and not the subscriptionStartDate)
// and therefore correctly find the latest catalog version, invoicing at the new price 295.95
//
Invoice curInvoice = invoiceChecker.checkInvoice(account.getId(), 4, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2015, 12, 5), new LocalDate(2016, 1, 5), InvoiceItemType.RECURRING, new BigDecimal("295.95")), new ExpectedInvoiceItemCheck(new LocalDate(2015, 12, 5), new LocalDate(2016, 1, 5), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-500.00")), new ExpectedInvoiceItemCheck(new LocalDate(2015, 12, 5), new LocalDate(2015, 12, 5), InvoiceItemType.CBA_ADJ, new BigDecimal("204.05")));
final VersionedCatalog catalog = catalogUserApi.getCatalog("foo", callContext);
// RECURRING should be set against V2
Assert.assertEquals(curInvoice.getInvoiceItems().get(0).getCatalogEffectiveDate().toDate().compareTo(catalog.getVersions().get(1).getEffectiveDate()), 0);
Assert.assertNull(curInvoice.getInvoiceItems().get(1).getCatalogEffectiveDate());
Assert.assertNull(curInvoice.getInvoiceItems().get(2).getCatalogEffectiveDate());
final Subscription bpSubscription = subscriptionApi.getSubscriptionForEntitlementId(bpEntitlementId, callContext);
final List<SubscriptionEvent> events = bpSubscription.getSubscriptionEvents();
// We are seeing START_ENTITLEMENT, START_BILLING, and the **last CHANGE**
// Note that the PHASE and intermediate CHANGE are not being returned (is_active = '0') because all coincided on the same date. This is debatable
// whether this is a good semantics. See #1030
assertEquals(events.size(), 3);
// Verify what we return is the price from the correct catalog version. See #1120
assertEquals(events.get(2).getNextPhase().getRecurring().getRecurringPrice().getPrice(account.getCurrency()).compareTo(new BigDecimal("295.95")), 0);
}
use of org.killbill.billing.entitlement.api.Subscription in project killbill by killbill.
the class AccountResource method closeAccount.
@TimedResource
@DELETE
@Path("/{accountId:" + UUID_PATTERN + "}")
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Close account")
@ApiResponses(value = { @ApiResponse(code = 204, message = "Successful operation"), @ApiResponse(code = 400, message = "Invalid account id supplied") })
public Response closeAccount(@PathParam("accountId") final UUID accountId, @QueryParam(QUERY_CANCEL_ALL_SUBSCRIPTIONS) @DefaultValue("false") final Boolean cancelAllSubscriptions, @QueryParam(QUERY_WRITE_OFF_UNPAID_INVOICES) @DefaultValue("false") final Boolean writeOffUnpaidInvoices, @QueryParam(QUERY_ITEM_ADJUST_UNPAID_INVOICES) @DefaultValue("false") final Boolean itemAdjustUnpaidInvoices, @QueryParam(QUERY_REMOVE_FUTURE_NOTIFICATIONS) @DefaultValue("true") final Boolean removeFutureNotifications, @HeaderParam(HDR_CREATED_BY) final String createdBy, @HeaderParam(HDR_REASON) final String reason, @HeaderParam(HDR_COMMENT) final String comment, @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, AccountApiException, EntitlementApiException, InvoiceApiException, TagApiException {
final CallContext callContext = context.createCallContextWithAccountId(accountId, createdBy, reason, comment, request);
tagUserApi.addTag(accountId, ObjectType.ACCOUNT, ControlTagType.AUTO_INVOICING_OFF.getId(), callContext);
if (cancelAllSubscriptions) {
final List<SubscriptionBundle> bundles = subscriptionApi.getSubscriptionBundlesForAccountId(accountId, callContext);
final Iterable<Subscription> subscriptions = Iterables.concat(Iterables.transform(bundles, new Function<SubscriptionBundle, List<Subscription>>() {
@Override
public List<Subscription> apply(final SubscriptionBundle input) {
return input.getSubscriptions();
}
}));
final Iterable<Subscription> toBeCancelled = Iterables.filter(subscriptions, new Predicate<Subscription>() {
@Override
public boolean apply(final Subscription input) {
return input.getLastActiveProductCategory() != ProductCategory.ADD_ON && input.getBillingEndDate() == null;
}
});
for (final Subscription cur : toBeCancelled) {
cur.cancelEntitlementWithPolicyOverrideBillingPolicy(EntitlementActionPolicy.IMMEDIATE, BillingActionPolicy.END_OF_TERM, ImmutableList.<PluginProperty>of(), callContext);
}
}
final Collection<Invoice> unpaidInvoices = writeOffUnpaidInvoices || itemAdjustUnpaidInvoices ? invoiceApi.getUnpaidInvoicesByAccountId(accountId, null, null, callContext) : ImmutableList.<Invoice>of();
if (writeOffUnpaidInvoices) {
for (final Invoice cur : unpaidInvoices) {
invoiceApi.tagInvoiceAsWrittenOff(cur.getId(), callContext);
}
} else if (itemAdjustUnpaidInvoices) {
final List<InvoiceItemType> ADJUSTABLE_TYPES = ImmutableList.<InvoiceItemType>of(InvoiceItemType.EXTERNAL_CHARGE, InvoiceItemType.FIXED, InvoiceItemType.RECURRING, InvoiceItemType.TAX, InvoiceItemType.USAGE, InvoiceItemType.PARENT_SUMMARY);
final String description = comment != null ? comment : "Close Account";
for (final Invoice invoice : unpaidInvoices) {
for (final InvoiceItem item : invoice.getInvoiceItems()) {
if (ADJUSTABLE_TYPES.contains(item.getInvoiceItemType())) {
invoiceApi.insertInvoiceItemAdjustment(accountId, invoice.getId(), item.getId(), clock.getUTCToday(), description, null, null, callContext);
}
}
}
}
final BlockingStateJson blockingState = new BlockingStateJson(accountId, "CLOSE_ACCOUNT", "account-service", true, false, false, null, BlockingStateType.ACCOUNT, null);
addBlockingState(blockingState, accountId, accountId, BlockingStateType.ACCOUNT, null, ImmutableList.<String>of(), createdBy, reason, comment, request, null);
if (removeFutureNotifications) {
final Long tenantRecordId = recordIdApi.getRecordId(callContext.getTenantId(), ObjectType.TENANT, callContext);
final Long accountRecordId = accountId == null ? null : recordIdApi.getRecordId(accountId, ObjectType.ACCOUNT, callContext);
for (final NotificationQueue notificationQueue : notificationQueueService.getNotificationQueues()) {
log.debug("Removing future notifications for queueName={}", notificationQueue.getFullQName());
notificationQueue.removeFutureNotificationsForSearchKeys(accountRecordId, tenantRecordId);
}
}
return Response.status(Status.NO_CONTENT).build();
}
Aggregations