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