use of org.killbill.billing.subscription.events.SubscriptionBaseEvent in project killbill by killbill.
the class TestPlanAligner method createSubscription.
private DefaultSubscriptionBase createSubscription(final DateTime bundleStartDate, final DateTime alignStartDate, final String productName, final PhaseType phaseType) throws CatalogApiException {
final SubscriptionBuilder builder = new SubscriptionBuilder();
builder.setBundleStartDate(bundleStartDate);
// Make sure to set the dates apart
builder.setAlignStartDate(new DateTime(alignStartDate));
// Create the transitions
final DefaultSubscriptionBase defaultSubscriptionBase = new DefaultSubscriptionBase(builder, null, clock);
final SubscriptionBaseEvent event = createSubscriptionEvent(builder.getAlignStartDate(), productName, phaseType, ApiEventType.CREATE);
final List<SubscriptionBaseEvent> events = new ArrayList<SubscriptionBaseEvent>();
events.add(event);
defaultSubscriptionBase.rebuildTransitions(events, catalog);
Assert.assertEquals(defaultSubscriptionBase.getAllTransitions().size(), 1);
Assert.assertNull(defaultSubscriptionBase.getAllTransitions().get(0).getPreviousPhase());
Assert.assertNotNull(defaultSubscriptionBase.getAllTransitions().get(0).getNextPhase());
return defaultSubscriptionBase;
}
use of org.killbill.billing.subscription.events.SubscriptionBaseEvent in project killbill by killbill.
the class DefaultSubscriptionBase method rebuildTransitions.
public void rebuildTransitions(final List<SubscriptionBaseEvent> inputEvents, final SubscriptionCatalog catalog) throws CatalogApiException {
if (inputEvents == null) {
return;
}
this.events = inputEvents;
Collections.sort(inputEvents, new Comparator<SubscriptionBaseEvent>() {
@Override
public int compare(final SubscriptionBaseEvent o1, final SubscriptionBaseEvent o2) {
final int res = o1.getEffectiveDate().compareTo(o2.getEffectiveDate());
if (res != 0) {
return res;
}
// In-memory events have a total order of 0, make sure they are after on disk event
if (o1.getTotalOrdering() == 0 && o2.getTotalOrdering() > 0) {
return 1;
} else if (o1.getTotalOrdering() > 0 && o2.getTotalOrdering() == 0) {
return -1;
} else if (o1.getTotalOrdering() == o2.getTotalOrdering()) {
return 0;
} else {
return o1.getTotalOrdering() < (o2.getTotalOrdering()) ? -1 : 1;
}
}
});
removeEverythingPastCancelEvent(events);
final UUID nextUserToken = null;
UUID nextEventId;
DateTime nextCreatedDate;
EntitlementState nextState = null;
String nextPlanName = null;
String nextPhaseName = null;
Integer nextBillingCycleDayLocal = null;
UUID prevEventId = null;
DateTime prevCreatedDate = null;
EntitlementState previousState = null;
PriceList previousPriceList = null;
Plan previousPlan = null;
PlanPhase previousPhase = null;
Integer previousBillingCycleDayLocal = null;
transitions = new LinkedList<SubscriptionBaseTransition>();
// Track each time we change Plan to fetch the Plan from the right catalog version
DateTime lastPlanChangeTime = null;
for (final SubscriptionBaseEvent cur : inputEvents) {
if (!cur.isActive()) {
continue;
}
ApiEventType apiEventType = null;
boolean isFromDisk = true;
nextEventId = cur.getId();
nextCreatedDate = cur.getCreatedDate();
switch(cur.getType()) {
case PHASE:
final PhaseEvent phaseEV = (PhaseEvent) cur;
nextPhaseName = phaseEV.getPhase();
break;
case BCD_UPDATE:
final BCDEvent bcdEvent = (BCDEvent) cur;
nextBillingCycleDayLocal = bcdEvent.getBillCycleDayLocal();
break;
case API_USER:
final ApiEvent userEV = (ApiEvent) cur;
apiEventType = userEV.getApiEventType();
isFromDisk = userEV.isFromDisk();
switch(apiEventType) {
case TRANSFER:
case CREATE:
prevEventId = null;
prevCreatedDate = null;
previousState = null;
previousPlan = null;
previousPhase = null;
previousPriceList = null;
nextState = EntitlementState.ACTIVE;
nextPlanName = userEV.getEventPlan();
nextPhaseName = userEV.getEventPlanPhase();
lastPlanChangeTime = cur.getEffectiveDate();
break;
case CHANGE:
nextPlanName = userEV.getEventPlan();
nextPhaseName = userEV.getEventPlanPhase();
lastPlanChangeTime = cur.getEffectiveDate();
break;
case CANCEL:
nextState = EntitlementState.CANCELLED;
nextPlanName = null;
nextPhaseName = null;
break;
case UNCANCEL:
case UNDO_CHANGE:
default:
throw new SubscriptionBaseError(String.format("Unexpected UserEvent type = %s", userEV.getApiEventType().toString()));
}
break;
default:
throw new SubscriptionBaseError(String.format("Unexpected Event type = %s", cur.getType()));
}
final Plan nextPlan = (nextPlanName != null) ? catalog.findPlan(nextPlanName, cur.getEffectiveDate(), lastPlanChangeTime) : null;
final PlanPhase nextPhase = (nextPlan != null && nextPhaseName != null) ? nextPlan.findPhase(nextPhaseName) : null;
final PriceList nextPriceList = (nextPlan != null) ? nextPlan.getPriceList() : null;
final SubscriptionBaseTransitionData transition = new SubscriptionBaseTransitionData(cur.getId(), id, bundleId, bundleExternalKey, cur.getType(), apiEventType, cur.getEffectiveDate(), prevEventId, prevCreatedDate, previousState, previousPlan, previousPhase, previousPriceList, previousBillingCycleDayLocal, nextEventId, nextCreatedDate, nextState, nextPlan, nextPhase, nextPriceList, nextBillingCycleDayLocal, cur.getTotalOrdering(), cur.getCreatedDate(), nextUserToken, isFromDisk);
transitions.add(transition);
previousState = nextState;
previousPlan = nextPlan;
previousPhase = nextPhase;
previousPriceList = nextPriceList;
prevEventId = nextEventId;
prevCreatedDate = nextCreatedDate;
previousBillingCycleDayLocal = nextBillingCycleDayLocal;
}
}
use of org.killbill.billing.subscription.events.SubscriptionBaseEvent in project killbill by killbill.
the class SubscriptionEventModelDao method toSubscriptionEvent.
public static SubscriptionBaseEvent toSubscriptionEvent(final SubscriptionEventModelDao src) {
if (src == null) {
return null;
}
final EventBaseBuilder<?> base;
if (src.getEventType() == EventType.PHASE) {
base = new PhaseEventBuilder();
} else if (src.getEventType() == EventType.BCD_UPDATE) {
base = new BCDEventBuilder();
} else {
base = new ApiEventBuilder();
}
base.setTotalOrdering(src.getTotalOrdering()).setUuid(src.getId()).setSubscriptionId(src.getSubscriptionId()).setCreatedDate(src.getCreatedDate()).setUpdatedDate(src.getUpdatedDate()).setEffectiveDate(src.getEffectiveDate()).setActive(src.isActive());
SubscriptionBaseEvent result;
if (src.getEventType() == EventType.PHASE) {
result = (new PhaseEventBuilder(base).setPhaseName(src.getPhaseName())).build();
} else if (src.getEventType() == EventType.API_USER) {
final ApiEventBuilder builder = new ApiEventBuilder(base).setEventPlan(src.getPlanName()).setEventPlanPhase(src.getPhaseName()).setEventPriceList(src.getPriceListName()).setApiEventType(src.getUserType()).setApiEventType(src.getUserType()).setFromDisk(true);
result = builder.build();
} else if (src.getEventType() == EventType.BCD_UPDATE) {
result = (new BCDEventBuilder(base).setBillCycleDayLocal(src.getBillingCycleDayLocal())).build();
} else {
throw new SubscriptionBaseError(String.format("Can't figure out event %s", src.getEventType()));
}
return result;
}
use of org.killbill.billing.subscription.events.SubscriptionBaseEvent in project killbill by killbill.
the class DefaultSubscriptionInternalApi method populateDryRunEvents.
private void populateDryRunEvents(@Nullable final UUID bundleId, @Nullable final DryRunArguments dryRunArguments, final Collection<SubscriptionBaseEvent> outputDryRunEvents, final Collection<SubscriptionBase> outputSubscriptions, final InternalTenantContext context) throws SubscriptionBaseApiException {
if (dryRunArguments == null || dryRunArguments.getAction() == null) {
return;
}
final DateTime utcNow = clock.getUTCNow();
List<SubscriptionBaseEvent> dryRunEvents = null;
try {
final PlanPhaseSpecifier inputSpec = dryRunArguments.getPlanPhaseSpecifier();
final boolean isInputSpecNullOrEmpty = inputSpec == null || (inputSpec.getPlanName() == null && inputSpec.getProductName() == null && inputSpec.getBillingPeriod() == null);
final Catalog catalog = catalogService.getFullCatalog(true, true, context);
// Create an overridesWithContext with a null context to indicate this is dryRun and no price overriden plan should be created.
final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(dryRunArguments.getPlanPhasePriceOverrides(), null);
final Plan plan = isInputSpecNullOrEmpty ? null : catalog.createOrFindPlan(inputSpec, overridesWithContext, utcNow);
final TenantContext tenantContext = internalCallContextFactory.createTenantContext(context);
switch(dryRunArguments.getAction()) {
case START_BILLING:
final DefaultSubscriptionBase baseSubscription = (DefaultSubscriptionBase) dao.getBaseSubscription(bundleId, context);
final DateTime startEffectiveDate = dryRunArguments.getEffectiveDate() != null ? context.toUTCDateTime(dryRunArguments.getEffectiveDate()) : utcNow;
final DateTime bundleStartDate = getBundleStartDateWithSanity(bundleId, baseSubscription, plan, startEffectiveDate, context);
final UUID subscriptionId = UUIDs.randomUUID();
dryRunEvents = apiService.getEventsOnCreation(bundleId, subscriptionId, startEffectiveDate, bundleStartDate, plan, inputSpec.getPhaseType(), plan.getPriceListName(), startEffectiveDate, utcNow, context);
final SubscriptionBuilder builder = new SubscriptionBuilder().setId(subscriptionId).setBundleId(bundleId).setBundleExternalKey(null).setCategory(plan.getProduct().getCategory()).setBundleStartDate(bundleStartDate).setAlignStartDate(startEffectiveDate);
final DefaultSubscriptionBase newSubscription = new DefaultSubscriptionBase(builder, apiService, clock);
newSubscription.rebuildTransitions(dryRunEvents, catalog);
outputSubscriptions.add(newSubscription);
break;
case CHANGE:
final DefaultSubscriptionBase subscriptionForChange = (DefaultSubscriptionBase) dao.getSubscriptionFromId(dryRunArguments.getSubscriptionId(), context);
DateTime changeEffectiveDate = dryRunArguments.getEffectiveDate() != null ? context.toUTCDateTime(dryRunArguments.getEffectiveDate()) : null;
if (changeEffectiveDate == null) {
BillingActionPolicy policy = dryRunArguments.getBillingActionPolicy();
if (policy == null) {
final PlanChangeResult planChangeResult = apiService.getPlanChangeResult(subscriptionForChange, inputSpec, utcNow, tenantContext);
policy = planChangeResult.getPolicy();
}
// We pass null for billingAlignment, accountTimezone, account BCD because this is not available which means that dryRun with START_OF_TERM BillingPolicy will fail
changeEffectiveDate = subscriptionForChange.getPlanChangeEffectiveDate(policy, null, null, -1, context);
}
dryRunEvents = apiService.getEventsOnChangePlan(subscriptionForChange, plan, plan.getPriceListName(), changeEffectiveDate, utcNow, true, context);
break;
case STOP_BILLING:
final DefaultSubscriptionBase subscriptionForCancellation = (DefaultSubscriptionBase) dao.getSubscriptionFromId(dryRunArguments.getSubscriptionId(), context);
DateTime cancelEffectiveDate = dryRunArguments.getEffectiveDate() != null ? context.toUTCDateTime(dryRunArguments.getEffectiveDate()) : null;
if (dryRunArguments.getEffectiveDate() == null) {
BillingActionPolicy policy = dryRunArguments.getBillingActionPolicy();
if (policy == null) {
final Plan currentPlan = subscriptionForCancellation.getCurrentPlan();
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(currentPlan.getName(), subscriptionForCancellation.getCurrentPhase().getPhaseType());
policy = catalogService.getFullCatalog(true, true, context).planCancelPolicy(spec, utcNow);
}
// We pass null for billingAlignment, accountTimezone, account BCD because this is not available which means that dryRun with START_OF_TERM BillingPolicy will fail
cancelEffectiveDate = subscriptionForCancellation.getPlanChangeEffectiveDate(policy, null, null, -1, context);
}
dryRunEvents = apiService.getEventsOnCancelPlan(subscriptionForCancellation, cancelEffectiveDate, utcNow, true, context);
break;
default:
throw new IllegalArgumentException("Unexpected dryRunArguments action " + dryRunArguments.getAction());
}
} catch (final CatalogApiException e) {
throw new SubscriptionBaseApiException(e);
}
if (dryRunEvents != null && !dryRunEvents.isEmpty()) {
outputDryRunEvents.addAll(dryRunEvents);
}
}
use of org.killbill.billing.subscription.events.SubscriptionBaseEvent in project killbill by killbill.
the class DefaultSubscriptionInternalApi method getNextFutureEventForSubscriptions.
@Override
public Map<UUID, DateTime> getNextFutureEventForSubscriptions(final SubscriptionBaseTransitionType eventType, final InternalCallContext internalCallContext) {
final Iterable<SubscriptionBaseEvent> events = dao.getFutureEventsForAccount(internalCallContext);
final Iterable<SubscriptionBaseEvent> filteredEvents = Iterables.filter(events, new Predicate<SubscriptionBaseEvent>() {
@Override
public boolean apply(final SubscriptionBaseEvent input) {
switch(input.getType()) {
case PHASE:
return eventType == SubscriptionBaseTransitionType.PHASE;
case BCD_UPDATE:
return eventType == SubscriptionBaseTransitionType.BCD_CHANGE;
case API_USER:
default:
return true;
}
}
});
final Map<UUID, DateTime> result = filteredEvents.iterator().hasNext() ? new HashMap<UUID, DateTime>() : ImmutableMap.<UUID, DateTime>of();
for (final SubscriptionBaseEvent cur : filteredEvents) {
final DateTime targetDate = result.get(cur.getSubscriptionId());
if (targetDate == null || targetDate.compareTo(cur.getEffectiveDate()) > 0) {
result.put(cur.getSubscriptionId(), cur.getEffectiveDate());
}
}
return result;
}
Aggregations