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