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.accounting.server.logic;
018
019import com.echothree.control.user.accounting.common.spec.GlAccountDescriptionSpec;
020import com.echothree.control.user.accounting.common.spec.GlAccountUniversalSpec;
021import com.echothree.model.control.accounting.common.exception.DuplicateGlAccountNameException;
022import com.echothree.model.control.accounting.common.exception.UnknownDefaultGlAccountException;
023import com.echothree.model.control.accounting.common.exception.UnknownGlAccountDescriptionException;
024import com.echothree.model.control.accounting.common.exception.UnknownGlAccountNameException;
025import com.echothree.model.control.accounting.server.control.AccountingControl;
026import com.echothree.model.control.core.common.ComponentVendors;
027import com.echothree.model.control.core.common.EntityTypes;
028import com.echothree.model.control.core.common.exception.InvalidParameterCountException;
029import com.echothree.model.control.core.server.logic.EntityInstanceLogic;
030import com.echothree.model.control.party.server.logic.LanguageLogic;
031import com.echothree.model.data.accounting.server.entity.Currency;
032import com.echothree.model.data.accounting.server.entity.GlAccount;
033import com.echothree.model.data.accounting.server.entity.GlAccountCategory;
034import com.echothree.model.data.accounting.server.entity.GlAccountClass;
035import com.echothree.model.data.accounting.server.entity.GlAccountDescription;
036import com.echothree.model.data.accounting.server.entity.GlAccountType;
037import com.echothree.model.data.accounting.server.entity.GlResourceType;
038import com.echothree.model.data.accounting.server.value.GlAccountDetailValue;
039import com.echothree.model.data.party.server.entity.Language;
040import com.echothree.util.common.message.ExecutionErrors;
041import com.echothree.util.common.persistence.BasePK;
042import com.echothree.util.server.control.BaseLogic;
043import com.echothree.util.server.message.ExecutionErrorAccumulator;
044import com.echothree.util.server.persistence.EntityPermission;
045import javax.enterprise.context.ApplicationScoped;
046import javax.inject.Inject;
047
048@ApplicationScoped
049public class GlAccountLogic
050        extends BaseLogic {
051
052    @Inject
053    AccountingControl accountingControl;
054
055    @Inject
056    EntityInstanceLogic entityInstanceLogic;
057
058    @Inject
059    GlAccountCategoryLogic glAccountCategoryLogic;
060
061    @Inject
062    LanguageLogic languageLogic;
063
064    protected GlAccountLogic() {
065        super();
066    }
067
068    public GlAccount createGlAccount(final ExecutionErrorAccumulator eea, final String glAccountName,
069            final GlAccount parentGlAccount, final GlAccountType glAccountType, final GlAccountClass glAccountClass,
070            final GlAccountCategory glAccountCategory, final GlResourceType glResourceType, final Currency currency,
071            final Boolean isDefault, final Language language, final String description, final BasePK createdBy) {
072        var glAccount = accountingControl.getGlAccountByName(glAccountName);
073
074        if(glAccount == null) {
075            glAccount = accountingControl.createGlAccount(glAccountName, parentGlAccount, glAccountType,
076                    glAccountClass, glAccountCategory, glResourceType, currency, isDefault, createdBy);
077
078            if(description != null) {
079                accountingControl.createGlAccountDescription(glAccount, language, description, createdBy);
080            }
081        } else {
082            handleExecutionError(DuplicateGlAccountNameException.class, eea, ExecutionErrors.DuplicateGlAccountName.name(),
083                    glAccountName);
084        }
085
086        return glAccount;
087    }
088
089    public GlAccount getGlAccountByName(final ExecutionErrorAccumulator eea, final String glAccountName,
090            final EntityPermission entityPermission) {
091        var glAccount = accountingControl.getGlAccountByName(glAccountName, entityPermission);
092
093        if(glAccount == null) {
094            handleExecutionError(UnknownGlAccountNameException.class, eea, ExecutionErrors.UnknownGlAccountName.name(), glAccountName);
095        }
096
097        return glAccount;
098    }
099
100    public GlAccount getGlAccountByName(final ExecutionErrorAccumulator eea, final String glAccountName) {
101        return getGlAccountByName(eea, glAccountName, EntityPermission.READ_ONLY);
102    }
103
104    public GlAccount getGlAccountByNameForUpdate(final ExecutionErrorAccumulator eea, final String glAccountName) {
105        return getGlAccountByName(eea, glAccountName, EntityPermission.READ_WRITE);
106    }
107
108    public GlAccount getGlAccountByUniversalSpec(final ExecutionErrorAccumulator eea, final GlAccountUniversalSpec universalSpec,
109            final boolean allowDefault, final GlAccountCategory glAccountCategory, final EntityPermission entityPermission) {
110        GlAccount glAccount = null;
111        var glAccountName = universalSpec.getGlAccountName();
112        var parameterCount = (glAccountName == null ? 0 : 1) + entityInstanceLogic.countPossibleEntitySpecs(universalSpec);
113
114        switch(parameterCount) {
115            case 0 -> {
116                if(allowDefault && glAccountCategory != null) {
117                    glAccount = accountingControl.getDefaultGlAccount(glAccountCategory, entityPermission);
118
119                    if(glAccount == null) {
120                        handleExecutionError(UnknownDefaultGlAccountException.class, eea, ExecutionErrors.UnknownDefaultGlAccount.name());
121                    }
122                } else {
123                    handleExecutionError(InvalidParameterCountException.class, eea, ExecutionErrors.InvalidParameterCount.name());
124                }
125            }
126            case 1 -> {
127                if(glAccountName == null) {
128                    var entityInstance = entityInstanceLogic.getEntityInstance(eea, universalSpec,
129                            ComponentVendors.ECHO_THREE.name(), EntityTypes.GlAccount.name());
130
131                    if(eea == null || !eea.hasExecutionErrors()) {
132                        glAccount = accountingControl.getGlAccountByEntityInstance(entityInstance, entityPermission);
133                    }
134                } else {
135                    glAccount = getGlAccountByName(eea, glAccountName, entityPermission);
136                }
137            }
138            default ->
139                    handleExecutionError(InvalidParameterCountException.class, eea, ExecutionErrors.InvalidParameterCount.name());
140        }
141
142        return glAccount;
143    }
144
145    public GlAccount getGlAccountByUniversalSpec(final ExecutionErrorAccumulator eea, final GlAccountUniversalSpec universalSpec,
146            final boolean allowDefault, final GlAccountCategory glAccountCategory) {
147        return getGlAccountByUniversalSpec(eea, universalSpec, allowDefault, glAccountCategory, EntityPermission.READ_ONLY);
148    }
149
150    public GlAccount getGlAccountByUniversalSpecForUpdate(final ExecutionErrorAccumulator eea, final GlAccountUniversalSpec universalSpec,
151            final boolean allowDefault, final GlAccountCategory glAccountCategory) {
152        return getGlAccountByUniversalSpec(eea, universalSpec, allowDefault, glAccountCategory, EntityPermission.READ_WRITE);
153    }
154
155    /**
156     * Obtain a GL Account fitting within a given GL Account Category.
157     *
158     * @param eea The ExecutionErrorAccumulator that any errors that are encountered should be added to.
159     * @param glAccounts A list of GlAccounts that will be drawn from to find a result.
160     * @param glAccountCategoryName If no glAccount is found in glAccounts, this will be used to find a default.
161     * @param unknownDefaultGlAccountError If no default is found, this error will be added to the ExecutionErrorAccumulator.
162     * @return The glAccount to use, unless null.
163     */
164    public GlAccount getDefaultGlAccountByCategory(final ExecutionErrorAccumulator eea, final GlAccount[] glAccounts,
165            final String glAccountCategoryName, final String unknownDefaultGlAccountError) {
166        GlAccount glAccount = null;
167
168        for(var i = 0; glAccount == null && i < glAccounts.length ; i++) {
169            glAccount = glAccounts[i];
170        }
171
172        if(glAccount == null) {
173            var glAccountCategory = glAccountCategoryLogic.getGlAccountCategoryByName(eea, glAccountCategoryName);
174
175            if(eea == null || !eea.hasExecutionErrors()) {
176                glAccount = accountingControl.getDefaultGlAccount(glAccountCategory);
177
178                if(glAccount == null) {
179                    eea.addExecutionError(unknownDefaultGlAccountError);
180                }
181            }
182        }
183
184        return glAccount;
185    }
186
187    public GlAccountDescription getGlAccountDescriptionBySpec(final ExecutionErrorAccumulator eea,
188            final GlAccountDescriptionSpec spec, final EntityPermission entityPermission) {
189        GlAccountDescription glAccountDescription = null;
190        var glAccountName = spec.getGlAccountName();
191        var glAccount = getGlAccountByName(eea, glAccountName);
192
193        if(!hasExecutionErrors(eea)) {
194            var languageIsoName = spec.getLanguageIsoName();
195            var language = languageLogic.getLanguageByName(eea, languageIsoName);
196
197            if(!hasExecutionErrors(eea)) {
198                glAccountDescription = accountingControl.getGlAccountDescription(glAccount, language, entityPermission);
199
200                if(glAccountDescription == null) {
201                    handleExecutionError(UnknownGlAccountDescriptionException.class, eea, ExecutionErrors.UnknownGlAccountDescription.name(), glAccountName, languageIsoName);
202                }
203            }
204        }
205
206        return glAccountDescription;
207    }
208
209    public void updateGlAccountFromValue(GlAccountDetailValue glAccountDetailValue, BasePK updatedBy) {
210        accountingControl.updateGlAccountFromValue(glAccountDetailValue, updatedBy);
211    }
212
213    public void deleteGlAccount(final ExecutionErrorAccumulator eea, final GlAccount glAccount, final BasePK deletedBy) {
214        accountingControl.deleteGlAccount(glAccount, deletedBy);
215    }
216
217}