use of org.killbill.billing.entitlement.api.DefaultEntitlementSpecifier in project killbill by killbill.
the class TestWithInArrearSubscriptions method testWithCancelation.
@Test(groups = "slow")
public void testWithCancelation() throws Exception {
final DateTime initialCreationDate = new DateTime(2020, 1, 1, 0, 0, 0, 0, testTimeZone);
clock.setTime(initialCreationDate);
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("basic-support-monthly-notrial", null);
busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
final UUID entitlementId = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec, null, null, null), "bundleExternalKey", null, null, false, true, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
final Entitlement entitlement = entitlementApi.getEntitlementForId(entitlementId, callContext);
// 2020-02-01
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addMonths(1);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2020, 1, 1), new LocalDate(2020, 2, 1), InvoiceItemType.RECURRING, new BigDecimal("100.00")));
// 2020-03-01
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addMonths(1);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2020, 2, 1), new LocalDate(2020, 3, 1), InvoiceItemType.RECURRING, new BigDecimal("100.00")));
// Cancel 2020-03-01
busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
entitlement.cancelEntitlementWithPolicyOverrideBillingPolicy(EntitlementActionPolicy.IMMEDIATE, BillingActionPolicy.IMMEDIATE, ImmutableList.of(), callContext);
assertListenerStatus();
busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE);
clock.addMonths(1);
assertListenerStatus();
for (int i = 0; i < 7; i++) {
clock.addMonths(1);
assertListenerStatus();
}
checkNoMoreInvoiceToGenerate(account);
}
use of org.killbill.billing.entitlement.api.DefaultEntitlementSpecifier in project killbill by killbill.
the class TestWithInArrearSubscriptions method testWithPauseResume.
@Test(groups = "slow")
public void testWithPauseResume() throws Exception {
final DateTime initialDate = new DateTime(2020, 1, 1, 0, 3, 42, 0, testTimeZone);
// set clock to the initial start date
clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
final UUID accountId = account.getId();
assertNotNull(account);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("basic-support-monthly-notrial", null);
busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
final UUID entitlementId = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec, null, null, null), "bundleExternalKey", null, null, false, true, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
// 2020-01-15
clock.addDays(14);
// Pause subscription. System will invoice for 2020-01-01 -> 2020-01-15
DefaultEntitlement entitlement = (DefaultEntitlement) entitlementApi.getEntitlementForId(entitlementId, callContext);
busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
entitlementApi.pause(entitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2020, 1, 1), new LocalDate(2020, 1, 15), InvoiceItemType.RECURRING, new BigDecimal("45.16")));
// 2020-01-20
clock.addDays(5);
// Resume subscription. System will invoice for remaining 2020-01-20 -> 2020-02-1
busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.NULL_INVOICE);
entitlementApi.resume(entitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
// 2020-02-01
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addDays(12);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2020, 1, 20), new LocalDate(2020, 2, 1), InvoiceItemType.RECURRING, new BigDecimal("38.71")));
// 2020-03-01
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addMonths(1);
assertListenerStatus();
checkNoMoreInvoiceToGenerate(account);
}
use of org.killbill.billing.entitlement.api.DefaultEntitlementSpecifier in project killbill by killbill.
the class TestWithInvoiceOptimization method testRecurringInArrear3.
@Test(groups = "slow")
public void testRecurringInArrear3() throws Exception {
invoiceConfig.setMaxInvoiceLimit(new Period("P1m"));
clock.setTime(new DateTime("2020-02-18T3:56:02"));
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
assertNotNull(account);
busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("blowdart-in-arrear-monthly-notrial");
final UUID entitlementId = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec), "Something", null, null, false, true, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
// 2020-03-01
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(12);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2020, 2, 18), new LocalDate(2020, 3, 1), InvoiceItemType.RECURRING, new BigDecimal("41.38")));
// 2020-04-01
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addMonths(1);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2020, 3, 1), new LocalDate(2020, 4, 1), InvoiceItemType.RECURRING, new BigDecimal("100.00")));
// 2020-04-01
clock.addDays(15);
assertListenerStatus();
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
invoiceUserApi.triggerInvoiceGeneration(account.getId(), new LocalDate(2020, 5, 1), callContext);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2020, 4, 1), new LocalDate(2020, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("100.00")));
}
use of org.killbill.billing.entitlement.api.DefaultEntitlementSpecifier in project killbill by killbill.
the class TestWithInvoiceOptimization method testBillRunInArrearWithUsageAndRecurring.
@Test(groups = "slow")
public void testBillRunInArrearWithUsageAndRecurring() throws Exception {
// Usage records are pulled using the most aggressive cuttoffDt
// 1. Because we set zeroAmountUsageDisabled=true, we don't rely on existing state (i.e the latest endDate of all usage items) to find the starting point of the cuttoffDt
// but instead, we default to 1 period (month) prior the date of the bill run
// 2. Because maxRawUsagePreviousPeriod=0, we don't go past the cuttoffDt computed in 1
// So, for bill run= Feb 1st -> (usage) cuttoffDt = Jan 1st - anything prior that will be ignored.
//
invoiceConfig.setMaxRawUsagePreviousPeriod(0);
invoiceConfig.setZeroAmountUsageDisabled(true);
// We make sure that our invoice optimization cuttOffDt is not greater than the usage optimization date, otherwise this could create issues
// as reflected by the WARN https://github.com/killbill/killbill/blob/killbill-0.22.27/invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java#L131
//
// To make it simpler we keep both cuttoffDt on the same value, but we could set any value, e.g P1m, P2m, ...
// So, for bill run= Feb 1st -> invoice cuttOffDt = Jan 1st
invoiceConfig.setMaxInvoiceLimit(new Period("P1m"));
clock.setTime(new DateTime("2021-01-15T3:56:02"));
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(15));
assertNotNull(account);
// Don't allow system generated invoices
add_AUTO_INVOICING_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
// Reuse invoice until committed
add_AUTO_INVOICING_DRAFT_Tag(account.getId(), ObjectType.ACCOUNT);
add_AUTO_INVOICING_REUSE_DRAFT_Tag(account.getId(), ObjectType.ACCOUNT);
// Create an in-arrear RECURRING subscription
final LocalDate effDt1 = new LocalDate(2020, 8, 15);
busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.BCD_CHANGE);
final PlanPhaseSpecifier spec1 = new PlanPhaseSpecifier("blowdart-in-arrear-monthly-notrial");
final UUID entitlementId1 = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec1, 15, null, null), null, effDt1, effDt1, false, true, ImmutableList.<PluginProperty>of(), callContext);
final Subscription sub1 = subscriptionApi.getSubscriptionForEntitlementId(entitlementId1, callContext);
assertListenerStatus();
// Create an in-arrear USAGE subscription
final LocalDate effDt2 = new LocalDate(2020, 9, 1);
busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.BCD_CHANGE);
final PlanPhaseSpecifier spec2 = new PlanPhaseSpecifier("training-usage-in-arrear");
final UUID entitlementId2 = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec2, 1, null, null), null, effDt2, effDt2, false, true, ImmutableList.<PluginProperty>of(), callContext);
final Subscription sub2 = subscriptionApi.getSubscriptionForEntitlementId(entitlementId2, callContext);
assertListenerStatus();
// Generate some usage data for past months
recordUsageData(sub2.getId(), "tracking-old-1", "hours", new LocalDate(2020, 9, 19), 1L, callContext);
recordUsageData(sub2.getId(), "tracking-old-2", "hours", new LocalDate(2020, 10, 19), 1L, callContext);
recordUsageData(sub2.getId(), "tracking-old-3", "hours", new LocalDate(2020, 11, 19), 1L, callContext);
recordUsageData(sub2.getId(), "tracking-old-4", "hours", new LocalDate(2020, 12, 19), 1L, callContext);
// Generate usage data for this month
recordUsageData(sub2.getId(), "tracking-1", "hours", new LocalDate(2021, 1, 19), 1L, callContext);
// 2021-02-01
// (Hum... Interestingly, there is no future notification set on the 1st although sub2#BCD is the 1st...)
clock.addDays(17);
Thread.sleep(1000);
// Bill run on the 2021-02-01 with targetDate end of month (EOM)
invoiceUserApi.triggerInvoiceGeneration(account.getId(), new LocalDate(2021, 2, 28), callContext);
Invoice invoice = getCurrentDraftInvoice(account.getId(), new Function<Invoice, Boolean>() {
@Override
public Boolean apply(final Invoice invoice) {
return invoice.getInvoiceItems().size() == 3;
}
}, 10);
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
invoiceUserApi.commitInvoice(invoice.getId(), callContext);
assertListenerStatus();
// The original invoice is a catchup invoice since there is no invoice and both subscriptions started way in the past
// However, with our aggressive cuttoffDt, we only see what we would normally expect to see on this invoice if we had invoiced up to this point
// (The catchup invoice already contains everything it should have and nothing more)
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2020, 12, 15), new LocalDate(2021, 1, 15), InvoiceItemType.RECURRING, new BigDecimal("100.00")), new ExpectedInvoiceItemCheck(new LocalDate(2021, 1, 15), new LocalDate(2021, 2, 15), InvoiceItemType.RECURRING, new BigDecimal("100.00")), new ExpectedInvoiceItemCheck(new LocalDate(2021, 1, 1), new LocalDate(2021, 2, 1), InvoiceItemType.USAGE, new BigDecimal("100.00")));
// 2021-02-15
// Invoice notification on the 15 (Recurring subscription)
clock.addDays(14);
Thread.sleep(1000);
recordUsageData(sub2.getId(), "tracking-2-a", "hours", new LocalDate(2021, 2, 15), 1L, callContext);
// 2021-03-01
clock.addDays(14);
Thread.sleep(1000);
recordUsageData(sub2.getId(), "tracking-2-b", "hours", new LocalDate(2021, 2, 25), 1L, callContext);
invoiceUserApi.triggerInvoiceGeneration(account.getId(), new LocalDate(2021, 3, 31), callContext);
invoice = getCurrentDraftInvoice(account.getId(), new Function<Invoice, Boolean>() {
@Override
public Boolean apply(final Invoice invoice) {
return invoice.getInvoiceItems().size() == 2;
}
}, 10);
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
invoiceUserApi.commitInvoice(invoice.getId(), callContext);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2021, 2, 15), new LocalDate(2021, 3, 15), InvoiceItemType.RECURRING, new BigDecimal("100.00")), new ExpectedInvoiceItemCheck(new LocalDate(2021, 2, 1), new LocalDate(2021, 3, 1), InvoiceItemType.USAGE, new BigDecimal("200.00")));
// 2021-03-15
// Invoice notification on the 15 (Recurring subscription)
clock.addDays(14);
Thread.sleep(1000);
recordUsageData(sub2.getId(), "tracking-3", "hours", new LocalDate(2021, 3, 15), 1L, callContext);
// 2021-04-01
clock.addDays(17);
Thread.sleep(1000);
invoiceUserApi.triggerInvoiceGeneration(account.getId(), new LocalDate(2021, 4, 30), callContext);
invoice = getCurrentDraftInvoice(account.getId(), new Function<Invoice, Boolean>() {
@Override
public Boolean apply(final Invoice invoice) {
return invoice.getInvoiceItems().size() == 2;
}
}, 10);
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
invoiceUserApi.commitInvoice(invoice.getId(), callContext);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2021, 3, 15), new LocalDate(2021, 4, 15), InvoiceItemType.RECURRING, new BigDecimal("100.00")), new ExpectedInvoiceItemCheck(new LocalDate(2021, 3, 1), new LocalDate(2021, 4, 1), InvoiceItemType.USAGE, new BigDecimal("100.00")));
// 2021-04-15
// Invoice notification on the 15 (Recurring subscription)
clock.addDays(14);
Thread.sleep(1000);
recordUsageData(sub2.getId(), "tracking-4", "hours", new LocalDate(2021, 4, 15), 1L, callContext);
// 2021-05-01
clock.addDays(16);
Thread.sleep(1000);
invoiceUserApi.triggerInvoiceGeneration(account.getId(), new LocalDate(2021, 5, 31), callContext);
invoice = getCurrentDraftInvoice(account.getId(), new Function<Invoice, Boolean>() {
@Override
public Boolean apply(final Invoice invoice) {
return invoice.getInvoiceItems().size() == 2;
}
}, 10);
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
invoiceUserApi.commitInvoice(invoice.getId(), callContext);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 4, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2021, 4, 15), new LocalDate(2021, 5, 15), InvoiceItemType.RECURRING, new BigDecimal("100.00")), new ExpectedInvoiceItemCheck(new LocalDate(2021, 4, 1), new LocalDate(2021, 5, 1), InvoiceItemType.USAGE, new BigDecimal("100.00")));
}
use of org.killbill.billing.entitlement.api.DefaultEntitlementSpecifier in project killbill by killbill.
the class TestWithInvoiceOptimization method testRecurringInAdvance.
@Test(groups = "slow")
public void testRecurringInAdvance() throws Exception {
invoiceConfig.setMaxInvoiceLimit(new Period("P1m"));
clock.setTime(new DateTime("2020-01-01T3:56:02"));
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
assertNotNull(account);
busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Blowdart", BillingPeriod.MONTHLY, "notrial", null);
final UUID entitlementId = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec), "Something", null, null, false, true, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2020, 1, 1), new LocalDate(2020, 2, 1), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
// 2020-02-01
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addMonths(1);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2020, 2, 1), new LocalDate(2020, 3, 1), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
// 2020-03-01
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addMonths(1);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2020, 3, 1), new LocalDate(2020, 4, 1), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
final Entitlement entitlement = entitlementApi.getEntitlementForId(entitlementId, callContext);
final PlanPhaseSpecifier spec2 = new PlanPhaseSpecifier("pistol-monthly-notrial");
// Trigger a change way in the past (prior the cuttoff date)
// org.killbill.invoice.maxInvoiceLimit= 1 month => cuttoff date = 2020-02-01
//
// We verify that invoice only tried to REPAIR from cutoff date -- and in particular the period 2020-01-15 - 2020-02-01 is left untouched.
busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE);
entitlement.changePlanWithDate(new DefaultEntitlementSpecifier(spec2, null, null, null), new LocalDate(2020, 1, 15), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 4, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2020, 2, 1), new LocalDate(2020, 3, 1), InvoiceItemType.RECURRING, new BigDecimal("19.95")), new ExpectedInvoiceItemCheck(new LocalDate(2020, 2, 1), new LocalDate(2020, 3, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-29.95")), new ExpectedInvoiceItemCheck(new LocalDate(2020, 3, 1), new LocalDate(2020, 4, 1), InvoiceItemType.RECURRING, new BigDecimal("19.95")), new ExpectedInvoiceItemCheck(new LocalDate(2020, 3, 1), new LocalDate(2020, 4, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-29.95")), new ExpectedInvoiceItemCheck(new LocalDate(2020, 3, 1), new LocalDate(2020, 3, 1), InvoiceItemType.CBA_ADJ, new BigDecimal("20.00")));
// Issue a first dry-run on 2020-03-02
// We should not see the items that were repaired a month prior now because of the cutoff date
final DateTime nextDate1 = clock.getUTCNow().plusDays(1);
checkNothingToInvoice(account.getId(), new LocalDate(nextDate1, testTimeZone), true);
// Issue a series of dry-run starting on 2020-04-01
DateTime nextDate = clock.getUTCNow().plusMonths(1);
for (int i = 0; i < 5; i++) {
final Invoice invoice = invoiceUserApi.triggerDryRunInvoiceGeneration(account.getId(), new LocalDate(nextDate, testTimeZone), new TestDryRunArguments(DryRunType.TARGET_DATE), callContext);
// Filter to eliminate CBA
final int actualRecurring = Iterables.size(Iterables.filter(invoice.getInvoiceItems(), new Predicate<InvoiceItem>() {
@Override
public boolean apply(final InvoiceItem invoiceItem) {
return invoiceItem.getInvoiceItemType() == InvoiceItemType.RECURRING;
}
}));
//
assertEquals(actualRecurring, 1);
nextDate = nextDate.plusMonths(1);
}
}
Aggregations