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.ItemAccountingCategoryDescriptionSpec;
020import com.echothree.control.user.accounting.common.spec.ItemAccountingCategoryUniversalSpec;
021import com.echothree.model.control.accounting.common.exception.DuplicateItemAccountingCategoryNameException;
022import com.echothree.model.control.accounting.common.exception.UnknownDefaultItemAccountingCategoryException;
023import com.echothree.model.control.accounting.common.exception.UnknownItemAccountingCategoryDescriptionException;
024import com.echothree.model.control.accounting.common.exception.UnknownItemAccountingCategoryNameException;
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.item.server.control.ItemControl;
031import com.echothree.model.control.party.server.logic.LanguageLogic;
032import com.echothree.model.data.accounting.server.entity.GlAccount;
033import com.echothree.model.data.accounting.server.entity.ItemAccountingCategory;
034import com.echothree.model.data.accounting.server.entity.ItemAccountingCategoryDescription;
035import com.echothree.model.data.accounting.server.value.ItemAccountingCategoryDetailValue;
036import com.echothree.model.data.party.server.entity.Language;
037import com.echothree.util.common.message.ExecutionErrors;
038import com.echothree.util.common.persistence.BasePK;
039import com.echothree.util.server.control.BaseLogic;
040import com.echothree.util.server.message.ExecutionErrorAccumulator;
041import com.echothree.util.server.persistence.EntityPermission;
042import javax.enterprise.context.ApplicationScoped;
043import javax.inject.Inject;
044
045@ApplicationScoped
046public class ItemAccountingCategoryLogic
047        extends BaseLogic {
048
049    @Inject
050    AccountingControl accountingControl;
051
052    @Inject
053    ItemControl itemControl;
054
055    @Inject
056    EntityInstanceLogic entityInstanceLogic;
057
058    @Inject
059    LanguageLogic languageLogic;
060
061    protected ItemAccountingCategoryLogic() {
062        super();
063    }
064
065    public ItemAccountingCategory createItemAccountingCategory(final ExecutionErrorAccumulator eea, final String itemAccountingCategoryName,
066            final ItemAccountingCategory parentItemAccountingCategory, final GlAccount inventoryGlAccount, final GlAccount salesGlAccount,
067            final GlAccount returnsGlAccount, final GlAccount cogsGlAccount, final GlAccount returnsCogsGlAccount, final Boolean isDefault,
068            final Integer sortOrder, final Language language, final String description, final BasePK createdBy) {
069        var itemAccountingCategory = accountingControl.getItemAccountingCategoryByName(itemAccountingCategoryName);
070
071        if(itemAccountingCategory == null) {
072            itemAccountingCategory = accountingControl.createItemAccountingCategory(itemAccountingCategoryName,
073                    parentItemAccountingCategory, inventoryGlAccount, salesGlAccount, returnsGlAccount, cogsGlAccount,
074                    returnsCogsGlAccount, isDefault, sortOrder, createdBy);
075
076            if(description != null) {
077                accountingControl.createItemAccountingCategoryDescription(itemAccountingCategory, language, description, createdBy);
078            }
079        } else {
080            handleExecutionError(DuplicateItemAccountingCategoryNameException.class, eea, ExecutionErrors.DuplicateItemAccountingCategoryName.name(),
081                    itemAccountingCategoryName);
082        }
083
084        return itemAccountingCategory;
085    }
086
087    public ItemAccountingCategory getItemAccountingCategoryByName(final ExecutionErrorAccumulator eea, final String itemAccountingCategoryName,
088            final EntityPermission entityPermission) {
089        var itemAccountingCategory = accountingControl.getItemAccountingCategoryByName(itemAccountingCategoryName, entityPermission);
090
091        if(itemAccountingCategory == null) {
092            handleExecutionError(UnknownItemAccountingCategoryNameException.class, eea, ExecutionErrors.UnknownItemAccountingCategoryName.name(), itemAccountingCategoryName);
093        }
094
095        return itemAccountingCategory;
096    }
097
098    public ItemAccountingCategory getItemAccountingCategoryByName(final ExecutionErrorAccumulator eea, final String itemAccountingCategoryName) {
099        return getItemAccountingCategoryByName(eea, itemAccountingCategoryName, EntityPermission.READ_ONLY);
100    }
101
102    public ItemAccountingCategory getItemAccountingCategoryByNameForUpdate(final ExecutionErrorAccumulator eea, final String itemAccountingCategoryName) {
103        return getItemAccountingCategoryByName(eea, itemAccountingCategoryName, EntityPermission.READ_WRITE);
104    }
105
106    public ItemAccountingCategory getItemAccountingCategoryByUniversalSpec(final ExecutionErrorAccumulator eea,
107            final ItemAccountingCategoryUniversalSpec universalSpec, boolean allowDefault, final EntityPermission entityPermission) {
108        ItemAccountingCategory itemAccountingCategory = null;
109        var itemAccountingCategoryName = universalSpec.getItemAccountingCategoryName();
110        var parameterCount = (itemAccountingCategoryName == null ? 0 : 1) + entityInstanceLogic.countPossibleEntitySpecs(universalSpec);
111
112        switch(parameterCount) {
113            case 0 -> {
114                if(allowDefault) {
115                    itemAccountingCategory = accountingControl.getDefaultItemAccountingCategory(entityPermission);
116
117                    if(itemAccountingCategory == null) {
118                        handleExecutionError(UnknownDefaultItemAccountingCategoryException.class, eea, ExecutionErrors.UnknownDefaultItemAccountingCategory.name());
119                    }
120                } else {
121                    handleExecutionError(InvalidParameterCountException.class, eea, ExecutionErrors.InvalidParameterCount.name());
122                }
123            }
124            case 1 -> {
125                if(itemAccountingCategoryName == null) {
126                    var entityInstance = entityInstanceLogic.getEntityInstance(eea, universalSpec,
127                            ComponentVendors.ECHO_THREE.name(), EntityTypes.ItemAccountingCategory.name());
128
129                    if(eea == null || !eea.hasExecutionErrors()) {
130                        itemAccountingCategory = accountingControl.getItemAccountingCategoryByEntityInstance(entityInstance, entityPermission);
131                    }
132                } else {
133                    itemAccountingCategory = getItemAccountingCategoryByName(eea, itemAccountingCategoryName, entityPermission);
134                }
135            }
136            default ->
137                    handleExecutionError(InvalidParameterCountException.class, eea, ExecutionErrors.InvalidParameterCount.name());
138        }
139
140        return itemAccountingCategory;
141    }
142
143    public ItemAccountingCategory getItemAccountingCategoryByUniversalSpec(final ExecutionErrorAccumulator eea,
144            final ItemAccountingCategoryUniversalSpec universalSpec, boolean allowDefault) {
145        return getItemAccountingCategoryByUniversalSpec(eea, universalSpec, allowDefault, EntityPermission.READ_ONLY);
146    }
147
148    public ItemAccountingCategory getItemAccountingCategoryByUniversalSpecForUpdate(final ExecutionErrorAccumulator eea,
149            final ItemAccountingCategoryUniversalSpec universalSpec, boolean allowDefault) {
150        return getItemAccountingCategoryByUniversalSpec(eea, universalSpec, allowDefault, EntityPermission.READ_WRITE);
151    }
152
153    public ItemAccountingCategoryDescription getItemAccountingCategoryDescriptionBySpec(final ExecutionErrorAccumulator eea,
154            final ItemAccountingCategoryDescriptionSpec spec, final EntityPermission entityPermission) {
155        ItemAccountingCategoryDescription itemAccountingCategoryDescription = null;
156        var itemAccountingCategoryName = spec.getItemAccountingCategoryName();
157        var itemAccountingCategory = getItemAccountingCategoryByName(eea, itemAccountingCategoryName);
158
159        if(!hasExecutionErrors(eea)) {
160            var languageIsoName = spec.getLanguageIsoName();
161            var language = languageLogic.getLanguageByName(eea, languageIsoName);
162
163            if(!hasExecutionErrors(eea)) {
164                itemAccountingCategoryDescription = accountingControl.getItemAccountingCategoryDescription(itemAccountingCategory, language,
165                        entityPermission);
166
167                if(itemAccountingCategoryDescription == null) {
168                    handleExecutionError(UnknownItemAccountingCategoryDescriptionException.class, eea, ExecutionErrors.UnknownItemAccountingCategoryDescription.name(), itemAccountingCategoryName, languageIsoName);
169                }
170            }
171        }
172
173        return itemAccountingCategoryDescription;
174    }
175
176    public void updateItemAccountingCategoryFromValue(ItemAccountingCategoryDetailValue itemAccountingCategoryDetailValue, BasePK updatedBy) {
177        accountingControl.updateItemAccountingCategoryFromValue(itemAccountingCategoryDetailValue, updatedBy);
178    }
179
180    private long countItemsByItemAccountingCategoryChildren(final AccountingControl accountingControl, final ItemControl itemControl,
181            final ItemAccountingCategory parentItemAccountingCategory) {
182        var itemAccountingCategoryChildren = accountingControl.getItemAccountingCategoriesByParentItemAccountingCategory(parentItemAccountingCategory);
183        var total = itemControl.countItemsByItemAccountingCategory(parentItemAccountingCategory);
184
185        total = itemAccountingCategoryChildren.stream().map((childItemAccountingCategory) ->
186                countItemsByItemAccountingCategoryChildren(accountingControl, itemControl, childItemAccountingCategory)).reduce(total, Long::sum);
187
188        return total;
189    }
190
191    private void checkDeleteItemAccountingCategory(final ExecutionErrorAccumulator eea, final ItemAccountingCategory itemAccountingCategory) {
192        if(countItemsByItemAccountingCategoryChildren(accountingControl, itemControl, itemAccountingCategory) != 0) {
193            eea.addExecutionError(ExecutionErrors.CannotDeleteItemAccountingCategoryInUse.name(),
194                    itemAccountingCategory.getLastDetail().getItemAccountingCategoryName());
195        }
196    }
197
198    public void deleteItemAccountingCategory(final ExecutionErrorAccumulator eea, final ItemAccountingCategory itemAccountingCategory, final BasePK deletedBy) {
199        checkDeleteItemAccountingCategory(eea, itemAccountingCategory);
200
201        if(eea == null || !eea.hasExecutionErrors()) {
202            accountingControl.deleteItemAccountingCategory(itemAccountingCategory, deletedBy);
203        }
204    }
205
206}