use of org.killbill.billing.junction.DefaultBlockingState in project killbill by killbill.
the class DefaultEntitlement method cancelEntitlementWithDate.
@Override
public Entitlement cancelEntitlementWithDate(@Nullable final LocalDate entitlementEffectiveDate, final boolean overrideBillingEffectiveDate, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
logCancelEntitlement(log, this, entitlementEffectiveDate, overrideBillingEffectiveDate, null, null);
checkForPermissions(Permission.ENTITLEMENT_CAN_CANCEL, callContext);
// Get the latest state from disk
refresh(callContext);
if (entitlementEffectiveDate != null && entitlementEffectiveDate.compareTo(getEffectiveStartDate()) < 0) {
throw new EntitlementApiException(ErrorCode.SUB_INVALID_REQUESTED_DATE, entitlementEffectiveDate, getEffectiveStartDate());
}
final LocalDate billingEffectiveDate = overrideBillingEffectiveDate ? entitlementEffectiveDate : null;
final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = new DefaultBaseEntitlementWithAddOnsSpecifier(getBundleId(), getBundleExternalKey(), null, entitlementEffectiveDate, billingEffectiveDate, false);
final List<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifierList = new ArrayList<BaseEntitlementWithAddOnsSpecifier>();
baseEntitlementWithAddOnsSpecifierList.add(baseEntitlementWithAddOnsSpecifier);
final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.CANCEL_SUBSCRIPTION, getAccountId(), null, baseEntitlementWithAddOnsSpecifierList, null, properties, callContext);
final WithEntitlementPlugin<Entitlement> cancelEntitlementWithPlugin = new WithEntitlementPlugin<Entitlement>() {
@Override
public Entitlement doCall(final EntitlementApi entitlementApi, final DefaultEntitlementContext updatedPluginContext) throws EntitlementApiException {
if (eventsStream.isEntitlementCancelled()) {
throw new EntitlementApiException(ErrorCode.SUB_CANCEL_BAD_STATE, getId(), EntitlementState.CANCELLED);
}
final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
final DateTime billingEffectiveCancelDate = dateHelper.fromLocalDateAndReferenceTimeWithMinimum(billingEffectiveDate, getEventsStream().getSubscriptionBase().getStartDate(), updatedPluginContext.getCreatedDate(), contextWithValidAccountRecordId);
try {
if (overrideBillingEffectiveDate) {
getSubscriptionBase().cancelWithDate(billingEffectiveCancelDate, callContext);
} else {
getSubscriptionBase().cancel(callContext);
}
} catch (final SubscriptionBaseApiException e) {
throw new EntitlementApiException(e);
}
final DateTime entitlementEffectiveCancelDate = dateHelper.fromLocalDateAndReferenceTimeWithMinimum(entitlementEffectiveDate, getEventsStream().getEntitlementEffectiveStartDateTime(), updatedPluginContext.getCreatedDate(), contextWithValidAccountRecordId);
final BlockingState newBlockingState = new DefaultBlockingState(getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_CANCELLED, KILLBILL_SERVICES.ENTITLEMENT_SERVICE.getServiceName(), true, true, false, entitlementEffectiveCancelDate);
final Collection<NotificationEvent> notificationEvents = new ArrayList<NotificationEvent>();
final Collection<BlockingState> addOnsBlockingStates = computeAddOnBlockingStates(entitlementEffectiveCancelDate, notificationEvents, callContext, contextWithValidAccountRecordId);
// Record the new state first, then insert the notifications to avoid race conditions
setBlockingStates(newBlockingState, addOnsBlockingStates, contextWithValidAccountRecordId);
for (final NotificationEvent notificationEvent : notificationEvents) {
recordFutureNotification(entitlementEffectiveCancelDate, notificationEvent, contextWithValidAccountRecordId);
}
return entitlementApi.getEntitlementForId(getId(), callContext);
}
};
return pluginExecution.executeWithPlugin(cancelEntitlementWithPlugin, pluginContext);
}
use of org.killbill.billing.junction.DefaultBlockingState in project killbill by killbill.
the class TestDefaultInternalBillingApi method testBlockingStatesWithSameEffectiveDate.
// This test was originally for https://github.com/killbill/killbill/issues/123.
// The invocationCount > 0 was to trigger an issue where events would come out-of-order randomly.
// While the bug shouldn't occur anymore, we're keeping it just in case (the test will also try to insert the events out-of-order manually).
// This test also checks we don't generate billing events for blocking durations less than a day (https://github.com/killbill/killbill/issues/267).
@Test(groups = "slow", description = "Check blocking states with same effective date are correctly handled", invocationCount = 10)
public void testBlockingStatesWithSameEffectiveDate() throws Exception {
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
final Account account = createAccount(getAccountData(7));
testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
final UUID entitlementId = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec), account.getExternalKey(), null, null, false, true, ImmutableList.<PluginProperty>of(), callContext);
final Entitlement entitlement = entitlementApi.getEntitlementForId(entitlementId, callContext);
final SubscriptionBase subscription = subscriptionInternalApi.getSubscriptionFromId(entitlement.getId(), internalCallContext);
assertListenerStatus();
final DateTime block1Date = clock.getUTCNow();
testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
final DefaultBlockingState state1 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, DefaultEntitlementApi.ENT_STATE_BLOCKED, KILLBILL_SERVICES.ENTITLEMENT_SERVICE.getServiceName(), true, true, true, block1Date);
blockingInternalApi.setBlockingState(state1, internalCallContext);
// Same date, we'll order by record id asc
final DefaultBlockingState state2 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, DefaultEntitlementApi.ENT_STATE_CLEAR, KILLBILL_SERVICES.ENTITLEMENT_SERVICE.getServiceName(), false, false, false, block1Date);
blockingInternalApi.setBlockingState(state2, internalCallContext);
assertListenerStatus();
clock.addDays(5);
final DateTime block2Date = clock.getUTCNow();
testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
final DefaultBlockingState state3 = new DefaultBlockingState(entitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, DefaultEntitlementApi.ENT_STATE_BLOCKED, KILLBILL_SERVICES.ENTITLEMENT_SERVICE.getServiceName(), true, true, true, block2Date);
blockingInternalApi.setBlockingState(state3, internalCallContext);
// Same date, we'll order by record id asc
final DefaultBlockingState state4 = new DefaultBlockingState(entitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, DefaultEntitlementApi.ENT_STATE_CLEAR, KILLBILL_SERVICES.ENTITLEMENT_SERVICE.getServiceName(), false, false, false, block2Date);
blockingInternalApi.setBlockingState(state4, internalCallContext);
assertListenerStatus();
final DateTime block3Date = block2Date.plusDays(3);
// Pass the phase
testListener.pushExpectedEvent(NextEvent.PHASE);
clock.addDays(50);
assertListenerStatus();
final DateTime block4Date = clock.getUTCNow();
final DateTime block5Date = block4Date.plusDays(3);
// Only one event on the bus (for state5)
testListener.pushExpectedEvents(NextEvent.BLOCK);
// Insert the clear state first, to make sure the order in which we insert blocking states doesn't matter
// Since we are already in an ENT_STATE_CLEAR state for service ENTITLEMENT_SERVICE_NAME, we need to use a different
// state name to simulate this behavior (otherwise, by design, this event won't be created)
final DefaultBlockingState state6 = new DefaultBlockingState(entitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, DefaultEntitlementApi.ENT_STATE_CLEAR + "-something", KILLBILL_SERVICES.ENTITLEMENT_SERVICE.getServiceName(), false, false, false, block5Date);
blockingInternalApi.setBlockingState(state6, internalCallContext);
final DefaultBlockingState state5 = new DefaultBlockingState(entitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, DefaultEntitlementApi.ENT_STATE_BLOCKED + "-something", KILLBILL_SERVICES.ENTITLEMENT_SERVICE.getServiceName(), true, true, true, block4Date);
blockingInternalApi.setBlockingState(state5, internalCallContext);
assertListenerStatus();
// Now, add back blocking states at an earlier date, for a different blockable id, to make sure the effective
// date ordering is correctly respected when computing blocking durations
testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
final DefaultBlockingState state7 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, DefaultEntitlementApi.ENT_STATE_BLOCKED + "-something2", KILLBILL_SERVICES.ENTITLEMENT_SERVICE.getServiceName(), true, true, true, block3Date);
blockingInternalApi.setBlockingState(state7, internalCallContext);
final DefaultBlockingState state8 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, DefaultEntitlementApi.ENT_STATE_CLEAR + "-something2", KILLBILL_SERVICES.ENTITLEMENT_SERVICE.getServiceName(), false, false, false, block4Date);
blockingInternalApi.setBlockingState(state8, internalCallContext);
assertListenerStatus();
// Advance for state6 to be active
testListener.pushExpectedEvents(NextEvent.BLOCK);
clock.addDays(5);
assertListenerStatus();
// Expected blocking duration:
// * 2013-08-15 to 2013-10-04 [2013-08-15 to 2013-10-01 (block3Date -> block4Date) and 2013-10-01 to 2013-10-04 (block4Date -> block5Date)]
final List<BillingEvent> events = ImmutableList.<BillingEvent>copyOf(billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), null, null, internalCallContext));
Assert.assertEquals(events.size(), 3);
Assert.assertEquals(events.get(0).getTransitionType(), SubscriptionBaseTransitionType.CREATE);
Assert.assertEquals(events.get(0).getEffectiveDate(), subscription.getStartDate());
Assert.assertEquals(events.get(1).getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
Assert.assertEquals(events.get(1).getEffectiveDate().compareTo(block3Date), 0);
Assert.assertEquals(events.get(2).getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
Assert.assertEquals(events.get(2).getEffectiveDate().compareTo(block5Date), 0);
}
use of org.killbill.billing.junction.DefaultBlockingState in project killbill by killbill.
the class TestBlockingCalculator method testInsertBlockingEventsForBundle.
// S1-S2-S3 subscriptions in B1
// B1 -----[--------]
// S1 --A-------------------------------------
// S2 --B------C------------------------------
// S3 ------------------D---------------------
// Result
// S1 --A--[-------]--------------------------
// S2 --B--[-------]--------------------------
// S3 ------------------D---------------------
@Test(groups = "fast")
public void testInsertBlockingEventsForBundle() throws CatalogApiException {
final DateTime now = clock.getUTCNow();
final BillingEvent A = createRealEvent(subscription1, now.minusDays(1).minusHours(1));
final BillingEvent B = createRealEvent(subscription2, now.minusDays(1));
final BillingEvent C = createRealEvent(subscription2, now.plusDays(1));
final BillingEvent D = createRealEvent(subscription3, now.plusDays(3));
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
billingEvents.add(A);
billingEvents.add(B);
billingEvents.add(C);
billingEvents.add(D);
final BlockingState blockingState1 = new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now);
final BlockingState blockingState2 = new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2));
blockingStateDao.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableMap.<BlockingState, Optional<UUID>>of(blockingState1, Optional.<UUID>absent(), blockingState2, Optional.<UUID>absent()), internalCallContext);
blockingCalculator.insertBlockingEvents(billingEvents, new HashSet<UUID>(), subscriptionsForAccount, catalog, null, internalCallContext);
assertEquals(billingEvents.size(), 7);
final Iterable<BillingEvent> s1Events = Iterables.filter(billingEvents, new Predicate<BillingEvent>() {
@Override
public boolean apply(@Nullable final BillingEvent input) {
return input != null && input.getSubscriptionId().equals(subscription1.getId());
}
});
final Iterator<BillingEvent> it1 = s1Events.iterator();
assertEquals(it1.next(), A);
assertEquals(it1.next().getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
assertEquals(it1.next().getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
final Iterable<BillingEvent> s2Events = Iterables.filter(billingEvents, new Predicate<BillingEvent>() {
@Override
public boolean apply(@Nullable final BillingEvent input) {
return input != null && input.getSubscriptionId().equals(subscription2.getId());
}
});
final Iterator<BillingEvent> it2 = s2Events.iterator();
assertEquals(it2.next(), B);
assertEquals(it2.next().getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
assertEquals(it2.next().getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
final Iterable<BillingEvent> s3Events = Iterables.filter(billingEvents, new Predicate<BillingEvent>() {
@Override
public boolean apply(@Nullable final BillingEvent input) {
return input != null && input.getSubscriptionId().equals(subscription3.getId());
}
});
final Iterator<BillingEvent> it3 = s3Events.iterator();
assertEquals(it3.next(), D);
}
use of org.killbill.billing.junction.DefaultBlockingState in project killbill by killbill.
the class TestBlockingCalculator method testSimpleWithClearBlockingDuration.
@Test(groups = "fast")
public void testSimpleWithClearBlockingDuration() throws Exception {
final BillingEvent trial = createRealEvent(subscription1, new LocalDate(2012, 5, 1).toDateTimeAtStartOfDay(DateTimeZone.UTC), SubscriptionBaseTransitionType.CREATE);
final BillingEvent phase = createRealEvent(subscription1, new LocalDate(2012, 5, 31).toDateTimeAtStartOfDay(DateTimeZone.UTC), SubscriptionBaseTransitionType.PHASE);
final BillingEvent upgrade = createRealEvent(subscription1, new LocalDate(2012, 7, 25).toDateTimeAtStartOfDay(DateTimeZone.UTC), SubscriptionBaseTransitionType.CHANGE);
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
billingEvents.add(trial);
billingEvents.add(phase);
billingEvents.add(upgrade);
final BlockingState blockingState1 = new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, false, false, new LocalDate(2012, 7, 5).toDateTimeAtStartOfDay(DateTimeZone.UTC));
final BlockingState blockingState2 = new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, new LocalDate(2012, 7, 15).toDateTimeAtStartOfDay(DateTimeZone.UTC));
final BlockingState blockingState3 = new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, new LocalDate(2012, 7, 24).toDateTimeAtStartOfDay(DateTimeZone.UTC));
final BlockingState blockingState4 = new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, new LocalDate(2012, 7, 25).toDateTimeAtStartOfDay(DateTimeZone.UTC));
blockingStateDao.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableMap.<BlockingState, Optional<UUID>>of(blockingState1, Optional.<UUID>absent(), blockingState2, Optional.<UUID>absent(), blockingState3, Optional.<UUID>absent(), blockingState4, Optional.<UUID>absent()), internalCallContext);
blockingCalculator.insertBlockingEvents(billingEvents, new HashSet<UUID>(), subscriptionsForAccount, catalog, null, internalCallContext);
assertEquals(billingEvents.size(), 5);
final List<BillingEvent> events = new ArrayList<BillingEvent>(billingEvents);
assertEquals(events.get(0).getEffectiveDate(), new LocalDate(2012, 5, 1).toDateTimeAtStartOfDay(DateTimeZone.UTC));
assertEquals(events.get(0).getTransitionType(), SubscriptionBaseTransitionType.CREATE);
assertEquals(events.get(1).getEffectiveDate(), new LocalDate(2012, 5, 31).toDateTimeAtStartOfDay(DateTimeZone.UTC));
assertEquals(events.get(1).getTransitionType(), SubscriptionBaseTransitionType.PHASE);
assertEquals(events.get(2).getEffectiveDate(), new LocalDate(2012, 7, 15).toDateTimeAtStartOfDay(DateTimeZone.UTC));
assertEquals(events.get(2).getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
assertEquals(events.get(3).getEffectiveDate(), new LocalDate(2012, 7, 25).toDateTimeAtStartOfDay(DateTimeZone.UTC));
assertEquals(events.get(3).getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
assertEquals(events.get(4).getEffectiveDate(), new LocalDate(2012, 7, 25).toDateTimeAtStartOfDay(DateTimeZone.UTC));
assertEquals(events.get(4).getTransitionType(), SubscriptionBaseTransitionType.CHANGE);
}
use of org.killbill.billing.junction.DefaultBlockingState in project killbill by killbill.
the class TestBlockingCalculator method testCreateAndMergeDisablePairs.
@Test(groups = "fast")
public void testCreateAndMergeDisablePairs() {
final List<BlockingState> blockingEvents = new ArrayList<BlockingState>();
final UUID ovdId = UUID.randomUUID();
final DateTime entitlementStartDate = clock.getUTCNow();
final DateTime blockEffectiveDate = entitlementStartDate.plusSeconds(1);
final DateTime unblockEffectiveDate = blockEffectiveDate.plusDays(2);
// Similar to an entitlement start event
blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, entitlementStartDate));
List<DisabledDuration> pairs = blockingCalculator.createBlockingDurations(blockingEvents);
assertEquals(pairs.size(), 0);
blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, blockEffectiveDate));
pairs = blockingCalculator.createBlockingDurations(blockingEvents);
assertEquals(pairs.size(), 1);
assertEquals(pairs.get(0).getStart().compareTo(blockEffectiveDate), 0);
assertNull(pairs.get(0).getEnd());
blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, unblockEffectiveDate));
pairs = blockingCalculator.createBlockingDurations(blockingEvents);
assertEquals(pairs.size(), 1);
assertEquals(pairs.get(0).getStart().compareTo(blockEffectiveDate), 0);
assertEquals(pairs.get(0).getEnd().compareTo(unblockEffectiveDate), 0);
blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, unblockEffectiveDate));
pairs = blockingCalculator.createBlockingDurations(blockingEvents);
assertEquals(pairs.size(), 1);
assertEquals(pairs.get(0).getStart().compareTo(blockEffectiveDate), 0);
assertNull(pairs.get(0).getEnd());
}
Aggregations