Search in sources :

Example 1 with ApiEvent

use of org.killbill.billing.subscription.events.user.ApiEvent 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) {
        if (curDryRun.getSubscriptionId() != null && curDryRun.getSubscriptionId().equals(subscriptionId)) {
            //boolean inserted = false;
            final Iterator<SubscriptionBaseEvent> it = events.iterator();
            while (it.hasNext()) {
                final SubscriptionBaseEvent event = it.next();
                if (event.getEffectiveDate().isAfter(curDryRun.getEffectiveDate())) {
                    it.remove();
                }
            }
            // Set total ordering value of the fake dryRun event to make sure billing events are correctly ordered
            final SubscriptionBaseEvent curAdjustedDryRun;
            if (!events.isEmpty()) {
                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);
                        break;
                }
                eventBuilder.setTotalOrdering(events.get(events.size() - 1).getTotalOrdering() + 1);
                curAdjustedDryRun = eventBuilder.build();
            } else {
                curAdjustedDryRun = curDryRun;
            }
            events.add(curAdjustedDryRun);
        }
    }
}
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 2 with ApiEvent

use of org.killbill.billing.subscription.events.user.ApiEvent in project killbill by killbill.

the class TestUserApiChangePlan method testChangePlanBundleAlignEOTWithChargeThroughDate.

private void testChangePlanBundleAlignEOTWithChargeThroughDate(final String fromProd, final BillingPeriod fromTerm, final String fromPlanSet, final String toProd, final BillingPeriod toTerm, final String toPlanSet) throws SubscriptionBillingApiException, SubscriptionBaseApiException {
    // CREATE
    DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, fromProd, fromTerm, fromPlanSet);
    final PlanPhase trialPhase = subscription.getCurrentPhase();
    final DateTime expectedPhaseTrialChange = TestSubscriptionHelper.addDuration(subscription.getStartDate(), trialPhase.getDuration());
    assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
    // MOVE TO NEXT PHASE
    testListener.pushExpectedEvent(NextEvent.PHASE);
    Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(31));
    clock.addDeltaFromReality(it.toDurationMillis());
    assertListenerStatus();
    PlanPhase currentPhase = subscription.getCurrentPhase();
    assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
    // SET CTD
    final Duration ctd = testUtil.getDurationMonth(1);
    final DateTime newChargedThroughDate = TestSubscriptionHelper.addDuration(expectedPhaseTrialChange, ctd);
    subscriptionInternalApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, internalCallContext);
    // RE READ SUBSCRIPTION + CHANGE PLAN
    subscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
    subscription.changePlan(new PlanSpecifier(toProd, toTerm, toPlanSet), null, callContext);
    assertListenerStatus();
    // CHECK CHANGE PLAN
    currentPhase = subscription.getCurrentPhase();
    checkChangePlan(subscription, fromProd, ProductCategory.BASE, fromTerm, PhaseType.DISCOUNT);
    // NEXT PHASE
    final DateTime nextExpectedPhaseChange = TestSubscriptionHelper.addDuration(expectedPhaseTrialChange, currentPhase.getDuration());
    testUtil.checkNextPhaseChange(subscription, 2, nextExpectedPhaseChange);
    // ALSO VERIFY PENDING CHANGE EVENT
    final List<SubscriptionBaseEvent> events = dao.getPendingEventsForSubscription(subscription.getId(), internalCallContext);
    assertTrue(events.get(0) instanceof ApiEvent);
    // MOVE TO EOT
    testListener.pushExpectedEvent(NextEvent.CHANGE);
    it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(1));
    clock.addDeltaFromReality(it.toDurationMillis());
    assertListenerStatus();
    subscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
    currentPhase = subscription.getCurrentPhase();
    checkChangePlan(subscription, toProd, ProductCategory.BASE, toTerm, PhaseType.DISCOUNT);
    assertListenerStatus();
}
Also used : ApiEvent(org.killbill.billing.subscription.events.user.ApiEvent) PlanPhase(org.killbill.billing.catalog.api.PlanPhase) Duration(org.killbill.billing.catalog.api.Duration) SubscriptionBaseEvent(org.killbill.billing.subscription.events.SubscriptionBaseEvent) DateTime(org.joda.time.DateTime) Interval(org.joda.time.Interval) PlanSpecifier(org.killbill.billing.catalog.api.PlanSpecifier)

Example 3 with ApiEvent

use of org.killbill.billing.subscription.events.user.ApiEvent 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 4 with ApiEvent

use of org.killbill.billing.subscription.events.user.ApiEvent in project killbill by killbill.

the class DefaultSubscriptionBase method filterOutDuplicateCancelEvents.

//
// Hardening against data integrity issues where we have multiple active CANCEL (See #619):
// We skip any cancel events after the first one (subscription cannot be cancelled multiple times).
// The code should prevent such cases from happening but because of #619, some invalid data could be there so to be safe we added this code
//
// Also we remove !onDisk cancel events when there is an onDisk cancel event (can happen during the path where we process the base plan cancel notification, and are
// in the process of adding the new cancel events for the AO)
//
private void filterOutDuplicateCancelEvents(final List<SubscriptionBaseEvent> inputEvents) {
    Collections.sort(inputEvents, new Comparator<SubscriptionBaseEvent>() {

        @Override
        public int compare(final SubscriptionBaseEvent o1, final SubscriptionBaseEvent o2) {
            int res = o1.getEffectiveDate().compareTo(o2.getEffectiveDate());
            if (res == 0) {
                res = o1.getTotalOrdering() < (o2.getTotalOrdering()) ? -1 : 1;
            }
            return res;
        }
    });
    final boolean isCancelled = Iterables.any(inputEvents, new Predicate<SubscriptionBaseEvent>() {

        @Override
        public boolean apply(final SubscriptionBaseEvent input) {
            if (input.isActive() && input.getType() == EventType.API_USER) {
                final ApiEvent userEV = (ApiEvent) input;
                if (userEV.getApiEventType() == ApiEventType.CANCEL && userEV.isFromDisk()) {
                    return true;
                }
            }
            return false;
        }
    });
    if (!isCancelled) {
        return;
    }
    boolean foundFirstOnDiskCancel = false;
    final Iterator<SubscriptionBaseEvent> it = inputEvents.iterator();
    while (it.hasNext()) {
        final SubscriptionBaseEvent input = it.next();
        if (!input.isActive()) {
            continue;
        }
        if (input.getType() == EventType.API_USER) {
            final ApiEvent userEV = (ApiEvent) input;
            if (userEV.getApiEventType() == ApiEventType.CANCEL) {
                if (userEV.isFromDisk()) {
                    if (!foundFirstOnDiskCancel) {
                        foundFirstOnDiskCancel = true;
                    } else {
                        it.remove();
                    }
                } else {
                    it.remove();
                }
            }
        }
    }
}
Also used : ApiEvent(org.killbill.billing.subscription.events.user.ApiEvent) SubscriptionBaseEvent(org.killbill.billing.subscription.events.SubscriptionBaseEvent) PlanPhasePriceOverride(org.killbill.billing.catalog.api.PlanPhasePriceOverride)

Example 5 with ApiEvent

use of org.killbill.billing.subscription.events.user.ApiEvent in project killbill by killbill.

the class MockSubscriptionDaoMemory method uncancelSubscription.

@Override
public void uncancelSubscription(final DefaultSubscriptionBase subscription, final List<SubscriptionBaseEvent> uncancelEvents, final InternalCallContext context) {
    synchronized (events) {
        boolean foundCancel = false;
        final Iterator<SubscriptionBaseEvent> it = events.descendingIterator();
        while (it.hasNext()) {
            final SubscriptionBaseEvent cur = it.next();
            if (cur.getSubscriptionId() != subscription.getId()) {
                continue;
            }
            if (cur.getType() == EventType.API_USER && ((ApiEvent) cur).getApiEventType() == ApiEventType.CANCEL) {
                it.remove();
                foundCancel = true;
                break;
            }
        }
        if (foundCancel) {
            for (final SubscriptionBaseEvent cur : uncancelEvents) {
                insertEvent(cur, context);
            }
        }
    }
}
Also used : ApiEvent(org.killbill.billing.subscription.events.user.ApiEvent) SubscriptionBaseEvent(org.killbill.billing.subscription.events.SubscriptionBaseEvent)

Aggregations

SubscriptionBaseEvent (org.killbill.billing.subscription.events.SubscriptionBaseEvent)8 ApiEvent (org.killbill.billing.subscription.events.user.ApiEvent)8 PhaseEvent (org.killbill.billing.subscription.events.phase.PhaseEvent)4 DateTime (org.joda.time.DateTime)3 BCDEvent (org.killbill.billing.subscription.events.bcd.BCDEvent)3 LinkedList (java.util.LinkedList)2 UUID (java.util.UUID)2 Plan (org.killbill.billing.catalog.api.Plan)2 PlanPhase (org.killbill.billing.catalog.api.PlanPhase)2 PlanPhasePriceOverride (org.killbill.billing.catalog.api.PlanPhasePriceOverride)2 ApiEventType (org.killbill.billing.subscription.events.user.ApiEventType)2 Interval (org.joda.time.Interval)1 InternalCallContext (org.killbill.billing.callcontext.InternalCallContext)1 BillingPeriod (org.killbill.billing.catalog.api.BillingPeriod)1 Duration (org.killbill.billing.catalog.api.Duration)1 PhaseType (org.killbill.billing.catalog.api.PhaseType)1 PlanPhaseSpecifier (org.killbill.billing.catalog.api.PlanPhaseSpecifier)1 PlanSpecifier (org.killbill.billing.catalog.api.PlanSpecifier)1 PriceList (org.killbill.billing.catalog.api.PriceList)1 Product (org.killbill.billing.catalog.api.Product)1