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