001// --------------------------------------------------------------------------------
002// Copyright 2002-2026 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.chain.server.logic;
018
019import com.echothree.control.user.chain.common.spec.ChainUniversalSpec;
020import com.echothree.model.control.chain.common.exception.CannotDeleteChainInUseException;
021import com.echothree.model.control.chain.common.exception.DuplicateChainNameException;
022import com.echothree.model.control.chain.common.exception.UnknownChainNameException;
023import com.echothree.model.control.chain.common.exception.UnknownDefaultChainException;
024import com.echothree.model.control.chain.common.exception.UnknownDefaultChainKindException;
025import com.echothree.model.control.chain.common.exception.UnknownDefaultChainTypeException;
026import com.echothree.model.control.chain.server.control.ChainControl;
027import com.echothree.model.control.core.common.ComponentVendors;
028import com.echothree.model.control.core.common.EntityTypes;
029import com.echothree.model.control.core.common.exception.InvalidParameterCountException;
030import com.echothree.model.control.core.server.logic.EntityInstanceLogic;
031import com.echothree.model.control.customer.server.control.CustomerControl;
032import com.echothree.model.control.offer.server.control.OfferControl;
033import com.echothree.model.control.sequence.common.SequenceTypes;
034import com.echothree.model.control.sequence.server.logic.SequenceLogic;
035import com.echothree.model.data.chain.server.entity.Chain;
036import com.echothree.model.data.chain.server.entity.ChainKind;
037import com.echothree.model.data.chain.server.entity.ChainType;
038import com.echothree.model.data.party.server.entity.Language;
039import com.echothree.model.data.party.server.entity.Party;
040import com.echothree.model.data.sequence.server.entity.Sequence;
041import com.echothree.util.common.message.ExecutionErrors;
042import com.echothree.util.common.persistence.BasePK;
043import com.echothree.util.server.control.BaseLogic;
044import com.echothree.util.server.message.ExecutionErrorAccumulator;
045import com.echothree.util.server.persistence.EntityPermission;
046import com.echothree.util.server.persistence.Session;
047import com.echothree.util.server.validation.ParameterUtils;
048import javax.enterprise.context.ApplicationScoped;
049import javax.inject.Inject;
050
051@ApplicationScoped
052public class ChainLogic
053        extends BaseLogic {
054
055    @Inject
056    ChainControl chainControl;
057
058    @Inject
059    OfferControl offerControl;
060
061    @Inject
062    CustomerControl customerControl;
063
064    @Inject
065    ChainTypeLogic chainTypeLogic;
066
067    @Inject
068    SequenceLogic sequenceLogic;
069
070    protected ChainLogic() {
071        super();
072    }
073
074    public Chain createChain(final ExecutionErrorAccumulator eea, final String chainKindName, final String chainTypeName,
075            final String chainName, final String initialChainAdjustmentName, final String chainItemSelectorName,
076            final Boolean isDefault, final Integer sortOrder, final Language language, final String description,
077            final BasePK createdBy) {
078        var chainType = chainTypeLogic.getChainTypeByName(eea, chainKindName, chainTypeName);
079        Chain chain = null;
080
081        if(eea == null || !eea.hasExecutionErrors()) {
082            var chainInstanceSequence = initialChainAdjustmentName == null ? null :
083                    sequenceLogic.getSequenceByName(eea, SequenceTypes.CHAIN_INSTANCE.name(), initialChainAdjustmentName);
084
085            if(eea == null || !eea.hasExecutionErrors()) {
086                chain = createChain(eea, chainType, chainName, chainInstanceSequence,
087                        isDefault, sortOrder, language, description, createdBy);
088            }
089        }
090
091        return chain;
092    }
093
094    public Chain createChain(final ExecutionErrorAccumulator eea, final ChainType chainType, final String chainName,
095            final Sequence chainInstanceSequence, final Boolean isDefault, final Integer sortOrder, final Language language,
096            final String description, final BasePK createdBy) {
097        var chainControl = Session.getModelController(ChainControl.class);
098        var chain = chainControl.getChainByName(chainType, chainName);
099
100        if(chain == null) {
101            chain = chainControl.createChain(chainType, chainName, chainInstanceSequence, isDefault, sortOrder, createdBy);
102
103            if(description != null) {
104                chainControl.createChainDescription(chain, language, description, createdBy);
105            }
106        } else {
107            handleExecutionError(DuplicateChainNameException.class, eea, ExecutionErrors.DuplicateChainName.name(), chainName);
108        }
109        return chain;
110    }
111
112    public Chain getChainByName(final ExecutionErrorAccumulator eea, final ChainType chainType, final String chainName,
113            final EntityPermission entityPermission) {
114        var chainControl = Session.getModelController(ChainControl.class);
115        var chain = chainControl.getChainByName(chainType, chainName, entityPermission);
116
117        if(chain == null) {
118            handleExecutionError(UnknownChainNameException.class, eea, ExecutionErrors.UnknownChainName.name(),
119                    chainType.getLastDetail().getChainKind().getLastDetail().getChainKindName(),
120                    chainType.getLastDetail().getChainTypeName(), chainName);
121        }
122
123        return chain;
124    }
125
126    public Chain getChainByName(final ExecutionErrorAccumulator eea, final ChainType chainType, final String chainName) {
127        return getChainByName(eea, chainType, chainName, EntityPermission.READ_ONLY);
128    }
129
130    public Chain getChainByNameForUpdate(final ExecutionErrorAccumulator eea, final ChainType chainType, final String chainName) {
131        return getChainByName(eea, chainType, chainName, EntityPermission.READ_WRITE);
132    }
133
134    public Chain getChainByName(final ExecutionErrorAccumulator eea, final String chainKindName, final String chainTypeName,
135            final String chainName, final EntityPermission entityPermission) {
136        var chainType = chainTypeLogic.getChainTypeByName(eea, chainKindName, chainTypeName);
137        Chain chain = null;
138
139        if(!eea.hasExecutionErrors()) {
140            chain = getChainByName(eea, chainType, chainName, entityPermission);
141        }
142
143        return chain;
144    }
145
146    public Chain getChainByName(final ExecutionErrorAccumulator eea, final String chainKindName, final String chainTypeName, final String chainName) {
147        return getChainByName(eea, chainKindName, chainTypeName, chainName, EntityPermission.READ_ONLY);
148    }
149
150    public Chain getChainByNameForUpdate(final ExecutionErrorAccumulator eea, final String chainKindName, final String chainTypeName, final String chainName) {
151        return getChainByName(eea, chainKindName, chainTypeName, chainName, EntityPermission.READ_WRITE);
152    }
153
154    public Chain getChainByUniversalSpec(final ExecutionErrorAccumulator eea, final ChainUniversalSpec universalSpec,
155            final boolean allowDefault, final EntityPermission entityPermission) {
156        var chainControl = Session.getModelController(ChainControl.class);
157        var chainKindName = universalSpec.getChainKindName();
158        var chainTypeName = universalSpec.getChainTypeName();
159        var chainName = universalSpec.getChainName();
160        var nameParameterCount= ParameterUtils.getInstance().countNonNullParameters(chainKindName, chainTypeName, chainName);
161        var possibleEntitySpecs= EntityInstanceLogic.getInstance().countPossibleEntitySpecs(universalSpec);
162        Chain chain = null;
163
164        if(nameParameterCount < 4 && possibleEntitySpecs == 0) {
165            ChainKind chainKind = null;
166            ChainType chainType = null;
167
168            if(chainKindName == null) {
169                if(allowDefault) {
170                    chainKind = chainControl.getDefaultChainKind();
171
172                    if(chainKind == null) {
173                        handleExecutionError(UnknownDefaultChainKindException.class, eea, ExecutionErrors.UnknownDefaultChainKind.name());
174                    }
175                } else {
176                    handleExecutionError(InvalidParameterCountException.class, eea, ExecutionErrors.InvalidParameterCount.name());
177                }
178            } else {
179                chainKind = ChainKindLogic.getInstance().getChainKindByName(eea, chainKindName);
180            }
181
182            if(chainTypeName == null && !eea.hasExecutionErrors()) {
183                if(allowDefault) {
184                    chainType = chainControl.getDefaultChainType(chainKind);
185
186                    if(chainType == null) {
187                        handleExecutionError(UnknownDefaultChainTypeException.class, eea, ExecutionErrors.UnknownDefaultChainKind.name(),
188                                chainKind.getLastDetail().getChainKindName());
189                    }
190                } else {
191                    handleExecutionError(InvalidParameterCountException.class, eea, ExecutionErrors.InvalidParameterCount.name());
192                }
193            } else {
194                chainType = chainTypeLogic.getChainTypeByName(eea, chainKind, chainTypeName);
195            }
196
197            if(!eea.hasExecutionErrors()) {
198                if(chainName == null) {
199                    if(allowDefault) {
200                        chain = chainControl.getDefaultChain(chainType, entityPermission);
201
202                        if(chain == null) {
203                            handleExecutionError(UnknownDefaultChainException.class, eea, ExecutionErrors.UnknownDefaultChain.name(),
204                                    chainKind.getLastDetail().getChainKindName(),
205                                    chainType.getLastDetail().getChainTypeName());
206                        }
207                    } else {
208                        handleExecutionError(InvalidParameterCountException.class, eea, ExecutionErrors.InvalidParameterCount.name());
209                    }
210                } else {
211                    chain = getChainByName(eea, chainType, chainName, entityPermission);
212                }
213            }
214        } else if(nameParameterCount == 0 && possibleEntitySpecs == 1) {
215            var entityInstance = EntityInstanceLogic.getInstance().getEntityInstance(eea, universalSpec,
216                    ComponentVendors.ECHO_THREE.name(), EntityTypes.Chain.name());
217
218            if(!eea.hasExecutionErrors()) {
219                chain = chainControl.getChainByEntityInstance(entityInstance, entityPermission);
220            }
221        } else {
222            handleExecutionError(InvalidParameterCountException.class, eea, ExecutionErrors.InvalidParameterCount.name());
223        }
224
225        return chain;
226    }
227
228    public Chain getChainByUniversalSpec(final ExecutionErrorAccumulator eea, final ChainUniversalSpec universalSpec,
229            boolean allowDefault) {
230        return getChainByUniversalSpec(eea, universalSpec, allowDefault, EntityPermission.READ_ONLY);
231    }
232
233    public Chain getChainByUniversalSpecForUpdate(final ExecutionErrorAccumulator eea, final ChainUniversalSpec universalSpec,
234            boolean allowDefault) {
235        return getChainByUniversalSpec(eea, universalSpec, allowDefault, EntityPermission.READ_WRITE);
236    }
237
238    public void deleteChain(final ExecutionErrorAccumulator eea, final Chain chain, final BasePK deletedBy) {
239        if(chainControl.countChainActionSetsByChain(chain) == 0) {
240            var chainControl = Session.getModelController(ChainControl.class);
241
242            chainControl.deleteChain(chain, deletedBy);
243        } else {
244            var chainDetail = chain.getLastDetail();
245
246            handleExecutionError(CannotDeleteChainInUseException.class, eea, ExecutionErrors.CannotDeleteChainInUse.name(),
247                    chainDetail.getChainType().getLastDetail().getChainKind().getLastDetail().getChainKindName(),
248                    chainDetail.getChainType().getLastDetail().getChainTypeName(),
249                    chainDetail.getChainName());
250        }
251    }
252    
253    public Chain getChain(final ExecutionErrorAccumulator eea, final ChainType chainType, final Party party) {
254        var customer = customerControl.getCustomer(party);
255        var initialOfferUse = customer == null ? null : customer.getInitialOfferUse();
256        var offerChainType = initialOfferUse == null ? null : offerControl.getOfferChainType(initialOfferUse.getLastDetail().getOffer(), chainType);
257
258        return offerChainType == null ? chainControl.getDefaultChain(chainType) : offerChainType.getChain();
259    }
260
261}