EntitlementAlignment.java

149 lines | 4.831 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.LinkedList;
import java.util.List;
import java.util.UUID;

import org.joda.time.DateTime;

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.PlanAlignment;
import com.ning.billing.entitlement.events.phase.IPhaseEvent;
import com.ning.billing.entitlement.events.phase.PhaseEvent;
import com.ning.billing.entitlement.exceptions.EntitlementError;
import com.ning.billing.util.clock.Clock;

public class EntitlementAlignment {

    private final UUID subscriptionId;
    private final DateTime bundleStart;
    private final IPlan plan;
    private final DateTime effectiveDate;
    private final long activeVersion;
    private final DateTime now;
    private final List<TimedPhase> timedPhases;

    public final static class TimedPhase {

        private final IPlanPhase phase;
        private final DateTime startPhase;

        public TimedPhase(IPlanPhase phase, DateTime startPhase) {
            super();
            this.phase = phase;
            this.startPhase = startPhase;
        }

        public IPlanPhase getPhase() {
            return phase;
        }

        public DateTime getStartPhase() {
            return startPhase;
        }
    }

    public EntitlementAlignment(DateTime bundleStart, IPlan plan, DateTime effectiveDate) {
        this(null, null, bundleStart, plan, effectiveDate, 0);
    }

    public EntitlementAlignment(UUID subscriptionId, DateTime now, DateTime bundleStart, IPlan plan,
            DateTime effectiveDate, long activeVersion) {
        super();
        this.subscriptionId = subscriptionId;
        this.now = now;
        this.bundleStart = bundleStart;
        this.plan = plan;
        this.effectiveDate = effectiveDate;
        this.activeVersion = activeVersion;
        this.timedPhases = buildPhaseAlignment();
    }

    public List<TimedPhase> getTimedPhases() {
        return timedPhases;
    }

    public TimedPhase getCurrentTimedPhase() {
        return getCurOrNextTimedPhase(true);
    }

    public TimedPhase getNextTimedPhase() {
        return getCurOrNextTimedPhase(false);
    }

    public IPhaseEvent getNextPhaseEvent() {

        if (subscriptionId == null || now == null || activeVersion == 0) {
            throw new EntitlementError("Missing required arguments for creating next phase event");
        }
        TimedPhase nextPhase = getNextTimedPhase();
        IPhaseEvent nextPhaseEvent = (nextPhase == null) ?
                null :
                    new PhaseEvent(subscriptionId, nextPhase.getPhase(), now, nextPhase.getStartPhase(),
                            now,  activeVersion);
        return nextPhaseEvent;
    }

    private TimedPhase getCurOrNextTimedPhase(boolean returnCurrent) {
        TimedPhase cur = null;
        TimedPhase next = null;
        for (TimedPhase phase : timedPhases) {
            if (phase.getStartPhase().isAfter(effectiveDate)) {
                next = phase;
                break;
            }
            cur = phase;
        }
        return (returnCurrent) ? cur : next;
    }


    private List<TimedPhase> buildPhaseAlignment() {

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

        DateTime curPhaseStart = (plan.getPlanAlignment() == PlanAlignment.START_OF_SUBSCRIPTION) ?
                effectiveDate :  bundleStart;
        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;
    }


}