Search in sources :

Example 31 with SubscriptionBaseEvent

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;
}
Also used : ArrayList(java.util.ArrayList) SubscriptionBuilder(org.killbill.billing.subscription.api.user.SubscriptionBuilder) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) SubscriptionBaseEvent(org.killbill.billing.subscription.events.SubscriptionBaseEvent) DateTime(org.joda.time.DateTime)

Example 32 with SubscriptionBaseEvent

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;
    }
}
Also used : ApiEventType(org.killbill.billing.subscription.events.user.ApiEventType) PhaseEvent(org.killbill.billing.subscription.events.phase.PhaseEvent) BCDEvent(org.killbill.billing.subscription.events.bcd.BCDEvent) Plan(org.killbill.billing.catalog.api.Plan) DateTime(org.joda.time.DateTime) EntitlementState(org.killbill.billing.entitlement.api.Entitlement.EntitlementState) SubscriptionBaseError(org.killbill.billing.subscription.exceptions.SubscriptionBaseError) ApiEvent(org.killbill.billing.subscription.events.user.ApiEvent) PlanPhase(org.killbill.billing.catalog.api.PlanPhase) UUID(java.util.UUID) SubscriptionBaseEvent(org.killbill.billing.subscription.events.SubscriptionBaseEvent) PriceList(org.killbill.billing.catalog.api.PriceList)

Example 33 with SubscriptionBaseEvent

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;
}
Also used : ApiEventBuilder(org.killbill.billing.subscription.events.user.ApiEventBuilder) SubscriptionBaseError(org.killbill.billing.subscription.exceptions.SubscriptionBaseError) BCDEventBuilder(org.killbill.billing.subscription.events.bcd.BCDEventBuilder) PhaseEventBuilder(org.killbill.billing.subscription.events.phase.PhaseEventBuilder) SubscriptionBaseEvent(org.killbill.billing.subscription.events.SubscriptionBaseEvent)

Example 34 with SubscriptionBaseEvent

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);
    }
}
Also used : PlanPhaseSpecifier(org.killbill.billing.catalog.api.PlanPhaseSpecifier) BillingActionPolicy(org.killbill.billing.catalog.api.BillingActionPolicy) InternalTenantContext(org.killbill.billing.callcontext.InternalTenantContext) TenantContext(org.killbill.billing.util.callcontext.TenantContext) PlanChangeResult(org.killbill.billing.catalog.api.PlanChangeResult) SubscriptionBuilder(org.killbill.billing.subscription.api.user.SubscriptionBuilder) Plan(org.killbill.billing.catalog.api.Plan) DateTime(org.joda.time.DateTime) Catalog(org.killbill.billing.catalog.api.Catalog) PlanPhasePriceOverridesWithCallContext(org.killbill.billing.catalog.api.PlanPhasePriceOverridesWithCallContext) CatalogApiException(org.killbill.billing.catalog.api.CatalogApiException) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) UUID(java.util.UUID) SubscriptionBaseEvent(org.killbill.billing.subscription.events.SubscriptionBaseEvent) SubscriptionBaseApiException(org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)

Example 35 with SubscriptionBaseEvent

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;
}
Also used : UUID(java.util.UUID) SubscriptionBaseEvent(org.killbill.billing.subscription.events.SubscriptionBaseEvent) PlanPhasePriceOverride(org.killbill.billing.catalog.api.PlanPhasePriceOverride) DateTime(org.joda.time.DateTime) PlanPhasePriceOverride(org.killbill.billing.catalog.api.PlanPhasePriceOverride)

Aggregations

SubscriptionBaseEvent (org.killbill.billing.subscription.events.SubscriptionBaseEvent)75 DateTime (org.joda.time.DateTime)31 ApiEventBuilder (org.killbill.billing.subscription.events.user.ApiEventBuilder)26 LinkedList (java.util.LinkedList)25 ArrayList (java.util.ArrayList)23 UUID (java.util.UUID)22 DefaultSubscriptionBase (org.killbill.billing.subscription.api.user.DefaultSubscriptionBase)21 InternalCallContext (org.killbill.billing.callcontext.InternalCallContext)16 CatalogApiException (org.killbill.billing.catalog.api.CatalogApiException)14 Test (org.testng.annotations.Test)14 Plan (org.killbill.billing.catalog.api.Plan)13 PhaseEvent (org.killbill.billing.subscription.events.phase.PhaseEvent)13 ApiEvent (org.killbill.billing.subscription.events.user.ApiEvent)13 ApiEventCancel (org.killbill.billing.subscription.events.user.ApiEventCancel)12 ApiEventCreate (org.killbill.billing.subscription.events.user.ApiEventCreate)11 SubscriptionBase (org.killbill.billing.subscription.api.SubscriptionBase)10 SubscriptionBuilder (org.killbill.billing.subscription.api.user.SubscriptionBuilder)9 SubscriptionNotificationKey (org.killbill.billing.subscription.engine.core.SubscriptionNotificationKey)8 ApiEventChange (org.killbill.billing.subscription.events.user.ApiEventChange)8 ImmutableList (com.google.common.collect.ImmutableList)7