001// --------------------------------------------------------------------------------
002// Copyright 2002-2024 Echo Three, LLC
003//
004// Licensed under the Apache License, Version 2.0 (the "License");
005// you may not use this file except in compliance with the License.
006// You may obtain a copy of the License at
007//
008//     http://www.apache.org/licenses/LICENSE-2.0
009//
010// Unless required by applicable law or agreed to in writing, software
011// distributed under the License is distributed on an "AS IS" BASIS,
012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013// See the License for the specific language governing permissions and
014// limitations under the License.
015// --------------------------------------------------------------------------------
016
017package com.echothree.model.control.subscription.server.logic;
018
019import com.echothree.model.control.chain.common.ChainConstants;
020import com.echothree.model.control.chain.server.control.ChainControl;
021import com.echothree.model.control.chain.server.logic.BaseChainLogic;
022import com.echothree.model.control.subscription.server.control.SubscriptionControl;
023import com.echothree.model.data.chain.server.entity.Chain;
024import com.echothree.model.data.chain.server.entity.ChainInstance;
025import com.echothree.model.data.chain.server.entity.ChainType;
026import com.echothree.model.data.party.server.entity.Party;
027import com.echothree.model.data.subscription.server.entity.Subscription;
028import com.echothree.model.data.subscription.server.entity.SubscriptionType;
029import com.echothree.model.data.subscription.server.entity.SubscriptionTypeChain;
030import com.echothree.util.common.persistence.BasePK;
031import com.echothree.util.server.message.ExecutionErrorAccumulator;
032import com.echothree.util.server.persistence.Session;
033import java.util.List;
034
035public class SubscriptionChainLogic
036        extends BaseChainLogic {
037    
038    private SubscriptionChainLogic() {
039        super();
040    }
041    
042    private static class SubscriptionChainLogicHolder {
043        static SubscriptionChainLogic instance = new SubscriptionChainLogic();
044    }
045    
046    public static SubscriptionChainLogic getInstance() {
047        return SubscriptionChainLogicHolder.instance;
048    }
049    
050    /** createChainInstance is different than the version in BaseChainLogic, it requires a Subscription parameter. Subscriptions
051     * are allowed to override the Chain being used in each SubscriptionType, so this takes that into account when choosing a
052     * Chain. If there is no SubscriptionTypeChain, then we fall back to the default for the ChainType, following the normal
053     * rules that take OfferChainTypes into account as well. If the chainTypeName == RENEWAL, then we immediately fall back to the
054     * default for the ChainType.
055     * 
056     * TODO: RENEWAL should really be based on the amount of time remaining in the Subscription, and not just fall back to the
057     * default. getSubscriptionTypeChainsBySubscriptionTypeAndChainType(...) could order by the remaining time, and then search
058     * for the one that's the best fit.
059     */
060    protected ChainInstance createChainInstance(final ExecutionErrorAccumulator eea, final String chainKindName, final String chainTypeName,
061            final Subscription subscription, final BasePK createdBy) {
062        var subscriptionControl = Session.getModelController(SubscriptionControl.class);
063        SubscriptionType subscriptionType = subscription.getLastDetail().getSubscriptionType();
064        ChainType chainType = getChainTypeByName(eea, chainKindName, chainTypeName);
065        Party party = subscription.getLastDetail().getParty();
066        List<SubscriptionTypeChain> subscriptionTypeChains = chainTypeName.equals(ChainConstants.ChainType_RENEWAL) ? null
067                : subscriptionControl.getSubscriptionTypeChainsBySubscriptionTypeAndChainType(subscriptionType, chainType);
068        Chain chain = null;
069        ChainInstance chainInstance = null;
070        
071        if(subscriptionTypeChains == null || subscriptionTypeChains.isEmpty()) {
072            chain = getChain(eea, chainType, party);
073        } else {
074            chain = subscriptionTypeChains.iterator().next().getChain();
075        }
076        
077        if(chain != null) {
078            chainInstance = createChainInstance(eea, chainType, party, createdBy);
079        }
080        
081        return chainInstance;
082    }
083    
084    protected ChainInstance createSubscriptionChainInstance(final ExecutionErrorAccumulator eea, final String chainTypeName, final Subscription subscription,
085            final BasePK createdBy) {
086        ChainInstance chainInstance = createChainInstance(eea, ChainConstants.ChainKind_SUBSCRIPTION, chainTypeName, subscription, createdBy);
087        
088        if(chainInstance != null) {
089            var chainControl = Session.getModelController(ChainControl.class);
090            ChainType chainType = chainInstance.getLastDetail().getChain().getLastDetail().getChainType();
091        
092            chainControl.createChainInstanceEntityRole(chainInstance, chainControl.getChainEntityRoleTypeByName(chainType,
093                    ChainConstants.ChainEntityRoleType_SUBSCRIPTION), subscription.getPrimaryKey(), createdBy);
094        }
095        
096        return chainInstance;
097    }
098    
099    public ChainInstance createSubscriptionInitialChainInstance(final ExecutionErrorAccumulator eea, final Subscription subscription,
100            final BasePK createdBy) {
101        return createSubscriptionChainInstance(eea, ChainConstants.ChainType_INITIAL, subscription, createdBy);
102    }
103    
104    public ChainInstance createSubscriptionExpirationWarningChainInstance(final ExecutionErrorAccumulator eea, final Subscription subscription,
105            final BasePK createdBy) {
106        return createSubscriptionChainInstance(eea, ChainConstants.ChainType_EXPIRATION_WARNING, subscription, createdBy);
107    }
108    
109    /** TODO: Consider the RemainingTime in the SubscriptionDetail when choosing the Chain to use.
110     */
111    public ChainInstance createSubscriptionRenewalChainInstance(final ExecutionErrorAccumulator eea, final Subscription subscription,
112            final BasePK createdBy) {
113        return createSubscriptionChainInstance(eea, ChainConstants.ChainType_RENEWAL, subscription, createdBy);
114    }
115    
116    public ChainInstance createSubscriptionExpirationChainInstance(final ExecutionErrorAccumulator eea, final Subscription subscription,
117            final BasePK createdBy) {
118        return createSubscriptionChainInstance(eea, ChainConstants.ChainType_EXPIRATION, subscription, createdBy);
119    }
120    
121}