Search in sources :

Example 1 with PhaseEvent

use of org.killbill.billing.subscription.events.phase.PhaseEvent in project killbill by killbill.

the class DefaultSubscriptionBaseTimeline method toExistingEvents.

private List<ExistingEvent> toExistingEvents(final Catalog catalog, final ProductCategory category, final List<SubscriptionBaseEvent> events) throws CatalogApiException {
    final List<ExistingEvent> result = new LinkedList<SubscriptionBaseTimeline.ExistingEvent>();
    String prevPlanName = null;
    String prevProductName = null;
    BillingPeriod prevBillingPeriod = null;
    String prevPriceListName = null;
    PhaseType prevPhaseType = null;
    DateTime startDate = null;
    for (final SubscriptionBaseEvent cur : events) {
        if (!cur.isActive()) {
            continue;
        }
        startDate = (startDate == null) ? cur.getEffectiveDate() : startDate;
        String productName = null;
        BillingPeriod billingPeriod = null;
        String priceListName = null;
        PhaseType phaseType = null;
        String planName = null;
        String planPhaseName = null;
        Integer billCycleDayLocal = null;
        ApiEventType apiType = null;
        switch(cur.getType()) {
            case PHASE:
                final PhaseEvent phaseEV = (PhaseEvent) cur;
                planPhaseName = phaseEV.getPhase();
                phaseType = catalog.findPhase(phaseEV.getPhase(), cur.getEffectiveDate(), startDate).getPhaseType();
                // A PHASE event always occurs within the same plan (and is never the first event)
                planName = prevPlanName;
                productName = prevProductName;
                billingPeriod = getBillingPeriod(catalog, phaseEV.getPhase(), cur.getEffectiveDate(), startDate);
                priceListName = prevPriceListName;
                break;
            case BCD_UPDATE:
                final BCDEvent bcdEvent = (BCDEvent) cur;
                billCycleDayLocal = bcdEvent.getBillCycleDayLocal();
                break;
            case API_USER:
                final ApiEvent userEV = (ApiEvent) cur;
                apiType = userEV.getApiEventType();
                planName = userEV.getEventPlan();
                planPhaseName = userEV.getEventPlanPhase();
                final Plan plan = (userEV.getEventPlan() != null) ? catalog.findPlan(userEV.getEventPlan(), cur.getEffectiveDate(), startDate) : null;
                phaseType = (userEV.getEventPlanPhase() != null) ? catalog.findPhase(userEV.getEventPlanPhase(), cur.getEffectiveDate(), startDate).getPhaseType() : prevPhaseType;
                productName = (plan != null) ? plan.getProduct().getName() : prevProductName;
                billingPeriod = (userEV.getEventPlanPhase() != null) ? getBillingPeriod(catalog, userEV.getEventPlanPhase(), cur.getEffectiveDate(), startDate) : prevBillingPeriod;
                priceListName = (userEV.getPriceList() != null) ? userEV.getPriceList() : prevPriceListName;
                break;
        }
        final SubscriptionBaseTransitionType transitionType = SubscriptionBaseTransitionData.toSubscriptionTransitionType(cur.getType(), apiType);
        final String planNameWithClosure = planName;
        final String planPhaseNameWithClosure = planPhaseName;
        final Integer billCycleDayLocalWithClosure = billCycleDayLocal;
        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(planName, phaseType);
        result.add(new ExistingEvent() {

            @Override
            public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
                return transitionType;
            }

            @Override
            public ProductCategory getProductCategory() {
                return category;
            }

            @Override
            public PlanPhaseSpecifier getPlanPhaseSpecifier() {
                return spec;
            }

            @Override
            public UUID getEventId() {
                return cur.getId();
            }

            @Override
            public DateTime getEffectiveDate() {
                return cur.getEffectiveDate();
            }

            @Override
            public String getPlanName() {
                return planNameWithClosure;
            }

            @Override
            public String getPlanPhaseName() {
                return planPhaseNameWithClosure;
            }

            @Override
            public Integer getBillCycleDayLocal() {
                return billCycleDayLocalWithClosure;
            }
        });
        prevPlanName = planName;
        prevProductName = productName;
        prevBillingPeriod = billingPeriod;
        prevPriceListName = priceListName;
        prevPhaseType = phaseType;
    }
    sortExistingEvent(result);
    return result;
}
Also used : ApiEventType(org.killbill.billing.subscription.events.user.ApiEventType) PlanPhaseSpecifier(org.killbill.billing.catalog.api.PlanPhaseSpecifier) PhaseEvent(org.killbill.billing.subscription.events.phase.PhaseEvent) BillingPeriod(org.killbill.billing.catalog.api.BillingPeriod) SubscriptionBaseTransitionType(org.killbill.billing.subscription.api.SubscriptionBaseTransitionType) BCDEvent(org.killbill.billing.subscription.events.bcd.BCDEvent) Plan(org.killbill.billing.catalog.api.Plan) LinkedList(java.util.LinkedList) DateTime(org.joda.time.DateTime) ApiEvent(org.killbill.billing.subscription.events.user.ApiEvent) PhaseType(org.killbill.billing.catalog.api.PhaseType) ProductCategory(org.killbill.billing.catalog.api.ProductCategory) UUID(java.util.UUID) SubscriptionBaseEvent(org.killbill.billing.subscription.events.SubscriptionBaseEvent)

Example 2 with PhaseEvent

use of org.killbill.billing.subscription.events.phase.PhaseEvent in project killbill by killbill.

the class TestUserApiCreate method testSimpleCreateSubscription.

@Test(groups = "slow")
public void testSimpleCreateSubscription() throws SubscriptionBaseApiException {
    final DateTime init = clock.getUTCNow();
    final String productName = "Shotgun";
    final BillingPeriod term = BillingPeriod.MONTHLY;
    final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
    final DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName);
    assertNotNull(subscription);
    assertEquals(subscription.getBundleExternalKey(), bundle.getExternalKey());
    testUtil.assertDateWithin(subscription.getStartDate(), init, clock.getUTCNow());
    testUtil.assertDateWithin(subscription.getBundleStartDate(), init, clock.getUTCNow());
    final Plan currentPlan = subscription.getCurrentPlan();
    assertNotNull(currentPlan);
    assertEquals(currentPlan.getProduct().getName(), productName);
    assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
    assertEquals(currentPlan.getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
    final PlanPhase currentPhase = subscription.getCurrentPhase();
    assertNotNull(currentPhase);
    assertEquals(currentPhase.getPhaseType(), PhaseType.TRIAL);
    assertListenerStatus();
    final List<SubscriptionBaseEvent> events = dao.getPendingEventsForSubscription(subscription.getId(), internalCallContext);
    assertNotNull(events);
    testUtil.printEvents(events);
    assertTrue(events.size() == 1);
    assertTrue(events.get(0) instanceof PhaseEvent);
    final DateTime nextPhaseChange = ((PhaseEvent) events.get(0)).getEffectiveDate();
    final DateTime nextExpectedPhaseChange = TestSubscriptionHelper.addDuration(subscription.getStartDate(), currentPhase.getDuration());
    assertEquals(nextPhaseChange, nextExpectedPhaseChange);
    testListener.pushExpectedEvent(NextEvent.PHASE);
    final Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(31));
    clock.addDeltaFromReality(it.toDurationMillis());
    final DateTime futureNow = clock.getUTCNow();
    assertTrue(futureNow.isAfter(nextPhaseChange));
    assertListenerStatus();
}
Also used : PhaseEvent(org.killbill.billing.subscription.events.phase.PhaseEvent) BillingPeriod(org.killbill.billing.catalog.api.BillingPeriod) PlanPhase(org.killbill.billing.catalog.api.PlanPhase) Plan(org.killbill.billing.catalog.api.Plan) SubscriptionBaseEvent(org.killbill.billing.subscription.events.SubscriptionBaseEvent) DateTime(org.joda.time.DateTime) Interval(org.joda.time.Interval) Test(org.testng.annotations.Test)

Example 3 with PhaseEvent

use of org.killbill.billing.subscription.events.phase.PhaseEvent in project killbill by killbill.

the class DefaultSubscriptionDao method mergeDryRunEvents.

private void mergeDryRunEvents(final UUID subscriptionId, final List<SubscriptionBaseEvent> events, @Nullable final Collection<SubscriptionBaseEvent> dryRunEvents) {
    if (dryRunEvents == null || dryRunEvents.isEmpty()) {
        return;
    }
    for (final SubscriptionBaseEvent curDryRun : dryRunEvents) {
        boolean swapChangeEventWithCreate = false;
        if (curDryRun.getSubscriptionId() != null && curDryRun.getSubscriptionId().equals(subscriptionId)) {
            final boolean isApiChange = curDryRun.getType() == EventType.API_USER && ((ApiEvent) curDryRun).getApiEventType() == ApiEventType.CHANGE;
            final Iterator<SubscriptionBaseEvent> it = events.iterator();
            while (it.hasNext()) {
                final SubscriptionBaseEvent event = it.next();
                if (event.getEffectiveDate().isAfter(curDryRun.getEffectiveDate())) {
                    it.remove();
                } else if (event.getEffectiveDate().compareTo(curDryRun.getEffectiveDate()) == 0 && isApiChange && (event.getType() == EventType.API_USER && (((ApiEvent) event).getApiEventType() == ApiEventType.CREATE) || ((ApiEvent) event).getApiEventType() == ApiEventType.TRANSFER)) {
                    it.remove();
                    swapChangeEventWithCreate = true;
                }
            }
            // Set total ordering value of the fake dryRun event to make sure billing events are correctly ordered
            // and also transform CHANGE event into CREATE in case of perfect effectiveDate match
            final EventBaseBuilder eventBuilder;
            switch(curDryRun.getType()) {
                case PHASE:
                    eventBuilder = new PhaseEventBuilder((PhaseEvent) curDryRun);
                    break;
                case BCD_UPDATE:
                    eventBuilder = new BCDEventBuilder((BCDEvent) curDryRun);
                    break;
                case API_USER:
                default:
                    eventBuilder = new ApiEventBuilder((ApiEvent) curDryRun);
                    if (swapChangeEventWithCreate) {
                        ((ApiEventBuilder) eventBuilder).setApiEventType(ApiEventType.CREATE);
                    }
                    break;
            }
            if (!events.isEmpty()) {
                eventBuilder.setTotalOrdering(events.get(events.size() - 1).getTotalOrdering() + 1);
            }
            events.add(eventBuilder.build());
        }
    }
}
Also used : PhaseEvent(org.killbill.billing.subscription.events.phase.PhaseEvent) ApiEventBuilder(org.killbill.billing.subscription.events.user.ApiEventBuilder) ApiEvent(org.killbill.billing.subscription.events.user.ApiEvent) BCDEventBuilder(org.killbill.billing.subscription.events.bcd.BCDEventBuilder) EventBaseBuilder(org.killbill.billing.subscription.events.EventBaseBuilder) BCDEvent(org.killbill.billing.subscription.events.bcd.BCDEvent) PhaseEventBuilder(org.killbill.billing.subscription.events.phase.PhaseEventBuilder) SubscriptionBaseEvent(org.killbill.billing.subscription.events.SubscriptionBaseEvent)

Example 4 with PhaseEvent

use of org.killbill.billing.subscription.events.phase.PhaseEvent 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 5 with PhaseEvent

use of org.killbill.billing.subscription.events.phase.PhaseEvent in project killbill by killbill.

the class DefaultSubscriptionBase method rebuildTransitions.

public void rebuildTransitions(final List<SubscriptionBaseEvent> inputEvents, final Catalog catalog) throws CatalogApiException {
    if (inputEvents == null) {
        return;
    }
    this.events = inputEvents;
    filterOutDuplicateCancelEvents(events);
    UUID nextUserToken = null;
    UUID nextEventId = null;
    DateTime nextCreatedDate = null;
    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>();
    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();
                        break;
                    case CHANGE:
                        nextPlanName = userEV.getEventPlan();
                        nextPhaseName = userEV.getEventPlanPhase();
                        break;
                    case CANCEL:
                        nextState = EntitlementState.CANCELLED;
                        nextPlanName = null;
                        nextPhaseName = null;
                        break;
                    case UNCANCEL:
                    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()));
        }
        Plan nextPlan = null;
        PlanPhase nextPhase = null;
        PriceList nextPriceList = null;
        nextPlan = (nextPlanName != null) ? catalog.findPlan(nextPlanName, cur.getEffectiveDate(), getAlignStartDate()) : null;
        nextPhase = (nextPhaseName != null) ? catalog.findPhase(nextPhaseName, cur.getEffectiveDate(), getAlignStartDate()) : null;
        nextPriceList = (nextPlan != null) ? catalog.findPriceListForPlan(nextPlanName, cur.getEffectiveDate(), getAlignStartDate()) : 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) PriceList(org.killbill.billing.catalog.api.PriceList) SubscriptionBaseEvent(org.killbill.billing.subscription.events.SubscriptionBaseEvent)

Aggregations

PhaseEvent (org.killbill.billing.subscription.events.phase.PhaseEvent)15 SubscriptionBaseEvent (org.killbill.billing.subscription.events.SubscriptionBaseEvent)13 TimedPhase (org.killbill.billing.subscription.alignment.TimedPhase)8 DateTime (org.joda.time.DateTime)7 ApiEventBuilder (org.killbill.billing.subscription.events.user.ApiEventBuilder)7 ArrayList (java.util.ArrayList)6 ApiEvent (org.killbill.billing.subscription.events.user.ApiEvent)6 Plan (org.killbill.billing.catalog.api.Plan)5 BCDEvent (org.killbill.billing.subscription.events.bcd.BCDEvent)5 UUID (java.util.UUID)4 ApiEventType (org.killbill.billing.subscription.events.user.ApiEventType)4 SubscriptionBaseError (org.killbill.billing.subscription.exceptions.SubscriptionBaseError)4 PlanPhase (org.killbill.billing.catalog.api.PlanPhase)3 BCDEventBuilder (org.killbill.billing.subscription.events.bcd.BCDEventBuilder)3 LinkedList (java.util.LinkedList)2 InternalCallContext (org.killbill.billing.callcontext.InternalCallContext)2 BillingPeriod (org.killbill.billing.catalog.api.BillingPeriod)2 CatalogApiException (org.killbill.billing.catalog.api.CatalogApiException)2 PhaseType (org.killbill.billing.catalog.api.PhaseType)2 PlanPhaseSpecifier (org.killbill.billing.catalog.api.PlanPhaseSpecifier)2