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