PlanAligner.java

224 lines | 8.33 kB Blame History Raw Download
/*
 * Copyright 2010-2011 Ning, Inc.
 *
 * Ning licenses this file to you under the Apache License, version 2.0
 * (the "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at:
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package com.ning.billing.entitlement.alignment;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import org.joda.time.DateTime;

import com.google.inject.Inject;
import com.ning.billing.catalog.api.CatalogApiException;
import com.ning.billing.catalog.api.ICatalog;
import com.ning.billing.catalog.api.ICatalogService;
import com.ning.billing.catalog.api.IDuration;
import com.ning.billing.catalog.api.IPlan;
import com.ning.billing.catalog.api.IPlanPhase;
import com.ning.billing.catalog.api.PlanAlignmentChange;
import com.ning.billing.catalog.api.PlanAlignmentCreate;
import com.ning.billing.catalog.api.PlanPhaseSpecifier;
import com.ning.billing.catalog.api.PlanSpecifier;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.exceptions.EntitlementError;
import com.ning.billing.util.clock.Clock;

public class PlanAligner implements IPlanAligner {

    private final ICatalogService catalogService;

    @Inject
    public PlanAligner(ICatalogService catalogService) {
        this.catalogService = catalogService;
    }


    private enum WhichPhase {
        CURRENT,
        NEXT
    }

    @Override
    public TimedPhase getCurrentTimedPhaseOnCreate(Subscription subscription,
            IPlan plan, String priceList, DateTime effectiveDate) {
        return getTimedPhaseOnCreate(subscription, plan, priceList, effectiveDate, WhichPhase.CURRENT);
    }

    @Override
    public TimedPhase getNextTimedPhaseOnCreate(Subscription subscription,
            IPlan plan, String priceList, DateTime effectiveDate) {
            return getTimedPhaseOnCreate(subscription, plan, priceList, effectiveDate, WhichPhase.NEXT);
    }

    @Override
    public TimedPhase getCurrentTimedPhaseOnChange(Subscription subscription,
            IPlan plan, String priceList, DateTime effectiveDate) {
        return getTimedPhaseOnChange(subscription, plan, priceList, effectiveDate, WhichPhase.CURRENT);
    }

    @Override
    public TimedPhase getNextTimedPhaseOnChange(Subscription subscription,
            IPlan plan, String priceList, DateTime effectiveDate) {
        return getTimedPhaseOnChange(subscription, plan, priceList, effectiveDate, WhichPhase.NEXT);
    }



    @Override
    public TimedPhase getNextTimedPhase(Subscription subscription,
            IPlan plan, DateTime effectiveDate, DateTime planStartDate) {
        List<TimedPhase> timedPhases = getPhaseAlignments(subscription, plan, effectiveDate, planStartDate);
        return getTimedPhase(timedPhases, effectiveDate, WhichPhase.NEXT);
    }

    private TimedPhase getTimedPhaseOnCreate(Subscription subscription,
            IPlan plan, String priceList, DateTime effectiveDate, WhichPhase which) {

        ICatalog catalog = catalogService.getCatalog();

            PlanSpecifier planSpecifier = new PlanSpecifier(plan.getProduct().getName(),
                    plan.getProduct().getCategory(),
                    plan.getBillingPeriod(),
                    priceList);

            DateTime planStartDate = null;
            
            //TODO fix exception handling
            PlanAlignmentCreate alignement = null;
			try {
				alignement = catalog.planCreateAlignment(planSpecifier);
			} catch (CatalogApiException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
            switch(alignement) {
            case START_OF_SUBSCRIPTION:
                planStartDate = subscription.getStartDate();
                break;
            case START_OF_BUNDLE:
                planStartDate = subscription.getBundleStartDate();
                break;
            default:
                throw new EntitlementError(String.format("Unknwon PlanAlignmentCreate %s", alignement));
            }
            List<TimedPhase> timedPhases = getPhaseAlignments(subscription, plan, effectiveDate, planStartDate);
            return getTimedPhase(timedPhases, effectiveDate, which);
    }

    private TimedPhase getTimedPhaseOnChange(Subscription subscription,
            IPlan plan, String priceList, DateTime effectiveDate, WhichPhase which) {

        ICatalog catalog = catalogService.getCatalog();

        IPlanPhase currentPhase = subscription.getCurrentPhase();
        IPlan currentPlan = subscription.getCurrentPlan();
        String currentPriceList = subscription.getCurrentPriceList();

        PlanPhaseSpecifier fromPlanPhaseSpecifier = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
                currentPlan.getProduct().getCategory(),
                currentPlan.getBillingPeriod(),
                currentPriceList,
                currentPhase.getPhaseType());

        PlanSpecifier toPlanSpecifier = new PlanSpecifier(plan.getProduct().getName(),
                plan.getProduct().getCategory(),
                plan.getBillingPeriod(),
                priceList);

        DateTime planStartDate = null;
        
        //TODO Correctly handle exception
        PlanAlignmentChange alignment = null;
		try {
			alignment = catalog.planChangeAlignment(fromPlanPhaseSpecifier, toPlanSpecifier);
		} catch (CatalogApiException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        switch(alignment) {
        case START_OF_SUBSCRIPTION:
            planStartDate = subscription.getStartDate();
            break;
        case START_OF_BUNDLE:
            planStartDate = subscription.getBundleStartDate();
            break;
        case CHANGE_OF_PLAN:
            throw new EntitlementError(String.format("Not implemented yet %s", alignment));
        case CHANGE_OF_PRICELIST:
            throw new EntitlementError(String.format("Not implemented yet %s", alignment));
        default:
            throw new EntitlementError(String.format("Unknwon PlanAlignmentChange %s", alignment));
        }
        List<TimedPhase> timedPhases = getPhaseAlignments(subscription, plan, effectiveDate, planStartDate);
        return getTimedPhase(timedPhases, effectiveDate, which);
    }

    private List<TimedPhase> getPhaseAlignments(Subscription subscription, IPlan plan,
            DateTime effectiveDate, DateTime planStartDate) {

        // The plan can be null with the nasty endpoint from test API.
        if (plan == null) {
            return Collections.emptyList();
        }

        List<TimedPhase> result = new LinkedList<IPlanAligner.TimedPhase>();

        DateTime curPhaseStart = planStartDate;
        if (plan.getInitialPhases() == null) {
            result.add(new TimedPhase(plan.getFinalPhase(), curPhaseStart));
            return result;
        }

        DateTime nextPhaseStart = null;
        for (IPlanPhase cur : plan.getInitialPhases()) {

            result.add(new TimedPhase(cur, curPhaseStart));

            IDuration curPhaseDuration = cur.getDuration();
            nextPhaseStart = Clock.addDuration(curPhaseStart, curPhaseDuration);
            if (nextPhaseStart == null) {
                throw new EntitlementError(String.format("Unexpected non ending UNLIMITED phase for plan %s",
                        plan.getName()));
            }
            curPhaseStart = nextPhaseStart;
        }
        result.add(new TimedPhase(plan.getFinalPhase(), nextPhaseStart));
        return result;
    }

    private TimedPhase getTimedPhase(List<TimedPhase> timedPhases, DateTime effectiveDate, WhichPhase which) {
        TimedPhase cur = null;
        TimedPhase next = null;
        for (TimedPhase phase : timedPhases) {
            if (phase.getStartPhase().isAfter(effectiveDate)) {
                next = phase;
                break;
            }
            cur = phase;
        }
        switch(which) {
        case CURRENT:
            return cur;
        case NEXT:
            return next;
        default:
            throw new EntitlementError(String.format("Unepected %s TimedPhase", which));
        }
    }
}