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.control.user.search.server.command;
018
019import com.echothree.control.user.search.common.form.SearchCustomersForm;
020import com.echothree.control.user.search.common.result.SearchResultFactory;
021import com.echothree.model.control.customer.server.control.CustomerControl;
022import com.echothree.model.control.geo.server.control.GeoControl;
023import com.echothree.model.control.party.common.PartyTypes;
024import com.echothree.model.control.party.server.control.PartyControl;
025import com.echothree.model.control.search.common.SearchKinds;
026import com.echothree.model.control.search.server.control.SearchControl;
027import com.echothree.model.control.customer.server.search.CustomerSearchEvaluator;
028import com.echothree.model.control.search.server.logic.SearchLogic;
029import com.echothree.model.control.security.common.SecurityRoleGroups;
030import com.echothree.model.control.security.common.SecurityRoles;
031import com.echothree.model.data.party.server.entity.PartyAliasType;
032import com.echothree.model.data.user.common.pk.UserVisitPK;
033import com.echothree.util.common.message.ExecutionErrors;
034import com.echothree.util.common.string.StringUtils;
035import com.echothree.util.common.validation.FieldDefinition;
036import com.echothree.util.common.validation.FieldType;
037import com.echothree.util.common.command.BaseResult;
038import com.echothree.util.common.form.ValidationResult;
039import com.echothree.util.server.control.BaseSimpleCommand;
040import com.echothree.util.server.control.CommandSecurityDefinition;
041import com.echothree.util.server.control.PartyTypeDefinition;
042import com.echothree.util.server.control.SecurityRoleDefinition;
043import com.echothree.util.server.persistence.Session;
044import com.echothree.util.server.validation.Validator;
045import com.google.common.base.Splitter;
046import java.util.List;
047import java.util.Locale;
048import java.util.regex.Pattern;
049import javax.enterprise.context.Dependent;
050
051@Dependent
052public class SearchCustomersCommand
053        extends BaseSimpleCommand<SearchCustomersForm> {
054    
055    private final static CommandSecurityDefinition COMMAND_SECURITY_DEFINITION;
056    private final static List<FieldDefinition> FORM_FIELD_DEFINITIONS;
057    private final static List<FieldDefinition> formAliasFieldDefinitions;
058
059    static {
060        COMMAND_SECURITY_DEFINITION = new CommandSecurityDefinition(List.of(
061                new PartyTypeDefinition(PartyTypes.UTILITY.name(), null),
062                new PartyTypeDefinition(PartyTypes.EMPLOYEE.name(), List.of(
063                        new SecurityRoleDefinition(SecurityRoleGroups.Customer.name(), SecurityRoles.Search.name())
064                        ))
065                ));
066        
067        FORM_FIELD_DEFINITIONS = List.of(
068                new FieldDefinition("SearchTypeName", FieldType.ENTITY_NAME, true, null, null),
069                new FieldDefinition("CustomerTypeName", FieldType.ENTITY_NAME, false, null, null),
070                new FieldDefinition("FirstName", FieldType.STRING, false, 1L, 20L),
071                new FieldDefinition("FirstNameSoundex", FieldType.BOOLEAN, false, null, null),
072                new FieldDefinition("MiddleName", FieldType.STRING, false, 1L, 20L),
073                new FieldDefinition("MiddleNameSoundex", FieldType.BOOLEAN, false, null, null),
074                new FieldDefinition("LastName", FieldType.STRING, false, 1L, 20L),
075                new FieldDefinition("LastNameSoundex", FieldType.BOOLEAN, false, null, null),
076                new FieldDefinition("Name", FieldType.STRING, false, null, null),
077                new FieldDefinition("CountryName", FieldType.ENTITY_NAME, false, null, null),
078                new FieldDefinition("AreaCode", FieldType.STRING, false, 1L, 5L),
079                new FieldDefinition("TelephoneNumber", FieldType.STRING, false, 1L, 25L),
080                new FieldDefinition("TelephoneExtension", FieldType.NUMBERS, false, 1L, 10L),
081                new FieldDefinition("EmailAddress", FieldType.EMAIL_ADDRESS, false, null, null),
082                new FieldDefinition("CustomerName", FieldType.ENTITY_NAME, false, null, null),
083                new FieldDefinition("PartyName", FieldType.ENTITY_NAME, false, null, null),
084                new FieldDefinition("PartyAliasTypeName", FieldType.ENTITY_NAME, false, null, null),
085                new FieldDefinition("Alias", FieldType.ENTITY_NAME, false, null, null),
086                new FieldDefinition("CreatedSince", FieldType.DATE_TIME, false, null, null),
087                new FieldDefinition("ModifiedSince", FieldType.DATE_TIME, false, null, null),
088                new FieldDefinition("Fields", FieldType.STRING, false, null, null)
089                );
090
091        formAliasFieldDefinitions = List.of(
092                new FieldDefinition("Alias", FieldType.ENTITY_NAME, true, null, null)
093                );
094    }
095
096    /** Creates a new instance of SearchCustomersCommand */
097    public SearchCustomersCommand() {
098        super(COMMAND_SECURITY_DEFINITION, FORM_FIELD_DEFINITIONS, false);
099    }
100    
101    @Override
102    protected ValidationResult validate() {
103        var validator = new Validator(this);
104        var validationResult = validator.validate(form, FORM_FIELD_DEFINITIONS);
105        
106        if(!validationResult.getHasErrors()) {
107            var partyAliasTypeName = form.getPartyAliasTypeName();
108            
109            if(partyAliasTypeName != null) {
110                validationResult = validator.validate(form, formAliasFieldDefinitions);
111            }
112        }
113        
114        return validationResult;
115    }
116    
117    @Override
118    protected BaseResult execute() {
119        var searchControl = Session.getModelController(SearchControl.class);
120        var result = SearchResultFactory.getSearchCustomersResult();
121        var searchKind = searchControl.getSearchKindByName(SearchKinds.CUSTOMER.name());
122        
123        if(searchKind != null) {
124            var searchTypeName = form.getSearchTypeName();
125            var searchType = searchControl.getSearchTypeByName(searchKind, searchTypeName);
126            
127            if(searchType != null) {
128                var customerControl = Session.getModelController(CustomerControl.class);
129                var customerTypeName = form.getCustomerTypeName();
130                var customerType = customerTypeName == null? null: customerControl.getCustomerTypeByName(customerTypeName);
131                
132                if(customerTypeName == null || customerType != null) {
133                    var partyAliasTypeName = form.getPartyAliasTypeName();
134                    var alias = form.getAlias();
135                    PartyAliasType partyAliasType = null;
136                    
137                    if(partyAliasTypeName != null) {
138                        var partyControl = Session.getModelController(PartyControl.class);
139                        var partyType = partyControl.getPartyTypeByName(PartyTypes.CUSTOMER.name());
140                        
141                        if(partyType != null) {
142                            partyAliasType = partyControl.getPartyAliasTypeByName(partyType, partyAliasTypeName);
143
144                            if(partyAliasType == null) {
145                                addExecutionError(ExecutionErrors.UnknownPartyAliasTypeName.name(), PartyTypes.CUSTOMER.name(), partyAliasTypeName);
146                            }
147                        } else {
148                            addExecutionError(ExecutionErrors.UnknownPartyTypeName.name(), PartyTypes.CUSTOMER.name());
149                        }
150                    }
151                    
152                    if(!hasExecutionErrors()) {
153                        var geoControl = Session.getModelController(GeoControl.class);
154                        var countryName = form.getCountryName();
155                        var countryAlias = countryName == null ? null : StringUtils.getInstance().cleanStringToName(countryName).toUpperCase(Locale.getDefault());
156                        var countryGeoCode = countryAlias == null ? null : geoControl.getCountryByAlias(countryAlias);
157
158                        if(countryName == null || countryGeoCode != null) {
159                            var geoCodeCountry = countryGeoCode == null ? null : geoControl.getGeoCodeCountry(countryGeoCode);
160                            var areaCode = form.getAreaCode();
161                            var areaCodePattern = geoCodeCountry == null ? null : geoCodeCountry.getAreaCodePattern();
162                            var pattern = areaCodePattern == null ? null : Pattern.compile(areaCodePattern);
163
164                            if(areaCode == null || (pattern == null || pattern.matcher(areaCode).matches())) {
165                                var telephoneNumberPattern = countryGeoCode == null ? null : geoCodeCountry.getTelephoneNumberPattern();
166                                var telephoneNumber = form.getTelephoneNumber();
167
168                                pattern = telephoneNumberPattern == null ? null : Pattern.compile(telephoneNumberPattern);
169
170                                if(telephoneNumber == null || (pattern == null || pattern.matcher(telephoneNumber).matches())) {
171                                    var searchLogic = SearchLogic.getInstance();
172                                    var userVisit = getUserVisit();
173                                    var customerSearchEvaluator = new CustomerSearchEvaluator(userVisit, searchType,
174                                            searchLogic.getDefaultSearchDefaultOperator(null), searchLogic.getDefaultSearchSortOrder(null, searchKind),
175                                            searchLogic.getDefaultSearchSortDirection(null));
176                                    var createdSince = form.getCreatedSince();
177                                    var modifiedSince = form.getModifiedSince();
178                                    var fields = form.getFields();
179
180                                    customerSearchEvaluator.setFirstName(form.getFirstName());
181                                    customerSearchEvaluator.setFirstNameSoundex(Boolean.parseBoolean(form.getFirstNameSoundex()));
182                                    customerSearchEvaluator.setMiddleName(form.getMiddleName());
183                                    customerSearchEvaluator.setMiddleNameSoundex(Boolean.parseBoolean(form.getMiddleNameSoundex()));
184                                    customerSearchEvaluator.setLastName(form.getLastName());
185                                    customerSearchEvaluator.setLastNameSoundex(Boolean.parseBoolean(form.getLastNameSoundex()));
186                                    customerSearchEvaluator.setQ(this, form.getName());
187                                    customerSearchEvaluator.setCountryGeoCode(countryGeoCode);
188                                    customerSearchEvaluator.setAreaCode(areaCode);
189                                    customerSearchEvaluator.setTelephoneNumber(telephoneNumber);
190                                    customerSearchEvaluator.setTelephoneExtension(form.getTelephoneExtension());
191                                    customerSearchEvaluator.setEmailAddress(form.getEmailAddress());
192                                    customerSearchEvaluator.setCustomerType(customerType);
193                                    customerSearchEvaluator.setPartyAliasType(partyAliasType);
194                                    customerSearchEvaluator.setAlias(alias);
195                                    customerSearchEvaluator.setCustomerName(form.getCustomerName());
196                                    customerSearchEvaluator.setPartyName(form.getPartyName());
197                                    customerSearchEvaluator.setCreatedSince(createdSince == null ? null : Long.valueOf(createdSince));
198                                    customerSearchEvaluator.setModifiedSince(modifiedSince == null ? null : Long.valueOf(modifiedSince));
199                                    customerSearchEvaluator.setFields(fields == null ? null : Splitter.on(':').trimResults().omitEmptyStrings().splitToList(fields).toArray(new String[0]));
200
201                                    if(!hasExecutionErrors()) {
202                                        result.setCount(customerSearchEvaluator.execute(this));
203                                    }
204                                } else {
205                                    addExecutionError(ExecutionErrors.InvalidTelephoneNumber.name(), telephoneNumber);
206                                }
207                            } else {
208                                addExecutionError(ExecutionErrors.InvalidAreaCode.name(), areaCode);
209                            }
210                        } else {
211                            addExecutionError(ExecutionErrors.UnknownCountryName.name(), countryName);
212                        }
213                    }
214                } else {
215                    addExecutionError(ExecutionErrors.UnknownCustomerTypeName.name(), customerTypeName);
216                }
217            } else {
218                addExecutionError(ExecutionErrors.UnknownSearchTypeName.name(), SearchKinds.CUSTOMER.name(), searchTypeName);
219            }
220        } else {
221            addExecutionError(ExecutionErrors.UnknownSearchKindName.name(), SearchKinds.CUSTOMER.name());
222        }
223        
224        return result;
225    }
226}