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.control.user.search.server.command; 018 019import com.echothree.control.user.search.common.form.IdentifyForm; 020import com.echothree.control.user.search.common.result.SearchResultFactory; 021import com.echothree.model.control.core.common.CoreOptions; 022import com.echothree.model.control.core.common.CoreProperties; 023import com.echothree.model.control.core.common.transfer.ComponentVendorTransfer; 024import com.echothree.model.control.core.common.transfer.EntityInstanceTransfer; 025import com.echothree.model.control.core.common.transfer.EntityTypeTransfer; 026import com.echothree.model.control.core.server.control.CoreControl; 027import com.echothree.model.control.customer.server.search.CustomerSearchEvaluator; 028import com.echothree.model.control.item.server.control.ItemControl; 029import com.echothree.model.control.item.server.search.ItemSearchEvaluator; 030import com.echothree.model.control.party.server.control.PartyControl; 031import com.echothree.model.control.search.common.SearchKinds; 032import com.echothree.model.control.search.common.SearchTypes; 033import com.echothree.model.control.search.server.control.SearchControl; 034import com.echothree.model.control.search.server.logic.SearchLogic; 035import com.echothree.model.control.security.common.SecurityRoleGroups; 036import com.echothree.model.control.security.common.SecurityRoles; 037import com.echothree.model.control.security.server.logic.SecurityRoleLogic; 038import com.echothree.model.control.vendor.server.control.VendorControl; 039import com.echothree.model.control.vendor.server.search.VendorSearchEvaluator; 040import com.echothree.model.control.warehouse.server.control.WarehouseControl; 041import com.echothree.model.control.warehouse.server.search.WarehouseSearchEvaluator; 042import com.echothree.model.data.party.server.entity.Party; 043import com.echothree.model.data.search.server.entity.SearchKind; 044import com.echothree.model.data.search.server.entity.SearchType; 045import com.echothree.model.data.user.common.pk.UserVisitPK; 046import com.echothree.model.data.user.server.entity.UserVisit; 047import com.echothree.util.common.command.BaseResult; 048import com.echothree.util.common.string.NameResult; 049import com.echothree.util.common.validation.FieldDefinition; 050import com.echothree.util.common.validation.FieldType; 051import com.echothree.util.server.control.BaseSimpleCommand; 052import com.echothree.util.server.message.DummyExecutionErrorAccumulator; 053import com.echothree.util.server.persistence.EntityNamesUtils; 054import com.echothree.util.server.persistence.Session; 055import com.echothree.util.server.persistence.translator.EntityInstanceAndNames; 056import com.echothree.util.server.string.NameCleaner; 057import java.util.Arrays; 058import java.util.Collections; 059import java.util.HashSet; 060import java.util.List; 061import java.util.Set; 062 063public class IdentifyCommand 064 extends BaseSimpleCommand<IdentifyForm> { 065 066 private final static List<FieldDefinition> FORM_FIELD_DEFINITIONS; 067 068 static { 069 FORM_FIELD_DEFINITIONS = Collections.unmodifiableList(Arrays.asList( 070 new FieldDefinition("Target", FieldType.STRING, true, null, null) 071 )); 072 } 073 074 /** Creates a new instance of IdentifyCommand */ 075 public IdentifyCommand(UserVisitPK userVisitPK, IdentifyForm form) { 076 super(userVisitPK, form, null, FORM_FIELD_DEFINITIONS, true); 077 } 078 079 @Override 080 protected void setupSession() { 081 super.setupSession(); 082 083 // Names are always included in the JumpResult, assembly of them is a little weird, so always disallow this option. 084 removeOption(CoreOptions.EntityInstanceIncludeNames); 085 086 // Ensure we're able to compare instances of and generate a HashCode for the EntityInstanceTransfers 087 removeFilteredTransferProperty(EntityInstanceTransfer.class, CoreProperties.ENTITY_TYPE); 088 removeFilteredTransferProperty(EntityTypeTransfer.class, CoreProperties.COMPONENT_VENDOR); 089 removeFilteredTransferProperty(EntityTypeTransfer.class, CoreProperties.ENTITY_TYPE_NAME); 090 removeFilteredTransferProperty(ComponentVendorTransfer.class, CoreProperties.COMPONENT_VENDOR_NAME); 091 } 092 093 private EntityInstanceTransfer fillInEntityInstance(EntityInstanceAndNames entityInstanceAndNames) { 094 var entityInstance = getCoreControl().getEntityInstanceTransfer(getUserVisit(), entityInstanceAndNames.getEntityInstance(), false, false, false, false, false, false); 095 096 entityInstance.setEntityNames(entityInstanceAndNames.getEntityNames()); 097 098 return entityInstance; 099 } 100 101 private void checkSequenceTypes(final Party party, final Set<EntityInstanceTransfer> entityInstances, final String target) { 102 var entityInstanceAndNames = EntityNamesUtils.getInstance().getEntityNames(party, target, true); 103 104 if(entityInstanceAndNames != null) { 105 entityInstances.add(fillInEntityInstance(entityInstanceAndNames)); 106 } 107 } 108 109 private void checkItems(final Party party, final Set<EntityInstanceTransfer> entityInstances, final String target) { 110 if(SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(null, party, 111 SecurityRoleGroups.Item.name(), SecurityRoles.Search.name())) { 112 var itemControl = Session.getModelController(ItemControl.class); 113 var item = itemControl.getItemByNameThenAlias(target); 114 115 if(item != null) { 116 var entityInstance = getCoreControl().getEntityInstanceByBasePK(item.getPrimaryKey()); 117 var entityInstanceAndNames = EntityNamesUtils.getInstance().getEntityNames(entityInstance); 118 119 entityInstances.add(fillInEntityInstance(entityInstanceAndNames)); 120 } 121 } 122 } 123 124 private void checkCompanies(final Party party, final Set<EntityInstanceTransfer> entityInstances, final String target) { 125 if(SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(null, party, 126 SecurityRoleGroups.Company.name(), SecurityRoles.Search.name())) { 127 var partyControl = Session.getModelController(PartyControl.class); 128 var partyCompany = partyControl.getPartyCompanyByName(target); 129 130 if(partyCompany != null) { 131 var entityInstance = getCoreControl().getEntityInstanceByBasePK(partyCompany.getParty().getPrimaryKey()); 132 var entityInstanceAndNames = EntityNamesUtils.getInstance().getEntityNames(entityInstance); 133 134 entityInstances.add(fillInEntityInstance(entityInstanceAndNames)); 135 } 136 } 137 } 138 139 private void checkDivisions(final Party party, final Set<EntityInstanceTransfer> entityInstances, final String target) { 140 if(SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(null, party, 141 SecurityRoleGroups.Division.name(), SecurityRoles.Search.name())) { 142 var partyControl = Session.getModelController(PartyControl.class); 143 var partyDivisions = partyControl.getDivisionsByName(target); 144 145 partyDivisions.stream().map((partyDivision) -> getCoreControl().getEntityInstanceByBasePK(partyDivision.getParty().getPrimaryKey())).map((entityInstance) -> EntityNamesUtils.getInstance().getEntityNames(entityInstance)).forEach((entityInstanceAndNames) -> { 146 entityInstances.add(fillInEntityInstance(entityInstanceAndNames)); 147 }); 148 } 149 } 150 151 private void checkDepartments(final Party party, final Set<EntityInstanceTransfer> entityInstances, final String target) { 152 if(SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(null, party, 153 SecurityRoleGroups.Department.name(), SecurityRoles.Search.name())) { 154 var partyControl = Session.getModelController(PartyControl.class); 155 var partyDepartments = partyControl.getDepartmentsByName(target); 156 157 partyDepartments.stream().map((partyDepartment) -> getCoreControl().getEntityInstanceByBasePK(partyDepartment.getParty().getPrimaryKey())).map((entityInstance) -> EntityNamesUtils.getInstance().getEntityNames(entityInstance)).forEach((entityInstanceAndNames) -> { 158 entityInstances.add(fillInEntityInstance(entityInstanceAndNames)); 159 }); 160 } 161 } 162 163 private void checkWarehouses(final Party party, final Set<EntityInstanceTransfer> entityInstances, final String target) { 164 if(SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(null, party, 165 SecurityRoleGroups.Warehouse.name(), SecurityRoles.Search.name())) { 166 var warehouseControl = Session.getModelController(WarehouseControl.class); 167 var warehouse = warehouseControl.getWarehouseByName(target); 168 169 if(warehouse != null) { 170 var entityInstance = getCoreControl().getEntityInstanceByBasePK(warehouse.getParty().getPrimaryKey()); 171 var entityInstanceAndNames = EntityNamesUtils.getInstance().getEntityNames(entityInstance); 172 173 entityInstances.add(fillInEntityInstance(entityInstanceAndNames)); 174 } 175 } 176 } 177 178 private void checkLocations(final Party party, final Set<EntityInstanceTransfer> entityInstances, final String target) { 179 if(SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(null, party, 180 SecurityRoleGroups.Location.name(), SecurityRoles.Search.name())) { 181 var warehouseControl = Session.getModelController(WarehouseControl.class); 182 var locations = warehouseControl.getLocationsByName(target); 183 184 locations.stream().map((location) -> getCoreControl().getEntityInstanceByBasePK(location.getPrimaryKey())).map((entityInstance) -> EntityNamesUtils.getInstance().getEntityNames(entityInstance)).forEach((entityInstanceAndNames) -> { 185 entityInstances.add(fillInEntityInstance(entityInstanceAndNames)); 186 }); 187 } 188 } 189 190 private void checkVendors(final Party party, final Set<EntityInstanceTransfer> entityInstances, final String target) { 191 if(SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(null, party, 192 SecurityRoleGroups.Vendor.name(), SecurityRoles.Search.name())) { 193 var vendorControl = Session.getModelController(VendorControl.class); 194 var vendor = vendorControl.getVendorByName(target); 195 196 if(vendor != null) { 197 var entityInstance = getCoreControl().getEntityInstanceByBasePK(vendor.getParty().getPrimaryKey()); 198 var entityInstanceAndNames = EntityNamesUtils.getInstance().getEntityNames(entityInstance); 199 200 entityInstances.add(fillInEntityInstance(entityInstanceAndNames)); 201 } 202 } 203 } 204 205 private void checkVendorItems(final Party party, final Set<EntityInstanceTransfer> entityInstances, final String target) { 206 if(SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(null, party, 207 SecurityRoleGroups.VendorItem.name(), SecurityRoles.Search.name())) { 208 var vendorControl = Session.getModelController(VendorControl.class); 209 var vendorItems = vendorControl.getVendorItemsByVendorItemName(target); 210 211 vendorItems.stream().map((vendorItem) -> getCoreControl().getEntityInstanceByBasePK(vendorItem.getPrimaryKey())).map((entityInstance) -> EntityNamesUtils.getInstance().getEntityNames(entityInstance)).forEach((entityInstanceAndNames) -> { 212 entityInstances.add(fillInEntityInstance(entityInstanceAndNames)); 213 }); 214 } 215 } 216 217 private void checkComponentVendors(final Party party, final Set<EntityInstanceTransfer> entityInstances, final String target) { 218 if(SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(null, party, 219 SecurityRoleGroups.ComponentVendor.name(), SecurityRoles.Search.name())) { 220 var componentVendor = getCoreControl().getComponentVendorByName(target); 221 222 if(componentVendor != null) { 223 var entityInstance = getCoreControl().getEntityInstanceByBasePK(componentVendor.getPrimaryKey()); 224 var entityInstanceAndNames = EntityNamesUtils.getInstance().getEntityNames(entityInstance); 225 226 entityInstances.add(fillInEntityInstance(entityInstanceAndNames)); 227 } 228 } 229 } 230 231 private void checkEntityTypes(final Party party, final Set<EntityInstanceTransfer> entityInstances, final String target) { 232 if(SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(null, party, 233 SecurityRoleGroups.EntityType.name(), SecurityRoles.Search.name())) { 234 var coreControl = Session.getModelController(CoreControl.class); 235 var entityTypes = coreControl.getEntityTypesByName(target); 236 237 entityTypes.stream().map((entityType) -> getCoreControl().getEntityInstanceByBasePK(entityType.getPrimaryKey())).map((entityInstance) -> EntityNamesUtils.getInstance().getEntityNames(entityInstance)).forEach((entityInstanceAndNames) -> { 238 entityInstances.add(fillInEntityInstance(entityInstanceAndNames)); 239 }); 240 } 241 } 242 243 private void executeCustomerSearch(final UserVisit userVisit, final Set<EntityInstanceTransfer> entityInstances, 244 final SearchLogic searchLogic, final SearchKind searchKind, final SearchType searchType, 245 final String firstName, final String middleName, final String lastName, final String q) { 246 var customerSearchEvaluator = new CustomerSearchEvaluator(userVisit, searchType, 247 searchLogic.getDefaultSearchDefaultOperator(null), 248 searchLogic.getDefaultSearchSortOrder(null, searchKind), 249 searchLogic.getDefaultSearchSortDirection(null)); 250 251 customerSearchEvaluator.setFirstName(firstName); 252 customerSearchEvaluator.setFirstNameSoundex(false); 253 customerSearchEvaluator.setMiddleName(middleName); 254 customerSearchEvaluator.setMiddleNameSoundex(false); 255 customerSearchEvaluator.setLastName(lastName); 256 customerSearchEvaluator.setLastNameSoundex(false); 257 customerSearchEvaluator.setQ(null, q); 258 259 // Avoid using the real ExecutionErrorAccumulator in order to avoid either throwing an Exception or 260 // accumulating errors for this UC. 261 var dummyExecutionErrorAccumulator = new DummyExecutionErrorAccumulator(); 262 customerSearchEvaluator.execute(dummyExecutionErrorAccumulator); 263 264 if(!dummyExecutionErrorAccumulator.hasExecutionErrors()) { 265 addSearchResults(userVisit, searchType, entityInstances); 266 } 267 } 268 269 private void searchCustomers(final Party party, final Set<EntityInstanceTransfer> entityInstances, final String target, 270 final NameResult nameResult) { 271 if(SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(null, party, 272 SecurityRoleGroups.Customer.name(), SecurityRoles.Search.name())) { 273 var userVisit = getUserVisit(); 274 var searchLogic = SearchLogic.getInstance(); 275 var searchKind = searchLogic.getSearchKindByName(null, SearchKinds.CUSTOMER.name()); 276 var searchType = searchLogic.getSearchTypeByName(null, searchKind, SearchTypes.IDENTIFY.name()); 277 278 // First attempt using a first and/or last name isolated from target. 279 executeCustomerSearch(userVisit, entityInstances, searchLogic, searchKind, searchType, 280 nameResult.getFirstName(), nameResult.getMiddleName(), nameResult.getLastName(), null); 281 // Then attempt searching for target using it as a query string. 282 executeCustomerSearch(userVisit, entityInstances, searchLogic, searchKind, searchType, 283 null, null, null, target); 284 } 285 } 286 287 private void executeVendorSearch(final UserVisit userVisit, final Set<EntityInstanceTransfer> entityInstances, 288 final SearchLogic searchLogic, final SearchKind searchKind, final SearchType searchType, 289 final String firstName, final String middleName, final String lastName, final String q) { 290 var vendorSearchEvaluator = new VendorSearchEvaluator(userVisit, searchType, 291 searchLogic.getDefaultSearchDefaultOperator(null), 292 searchLogic.getDefaultSearchSortOrder(null, searchKind), 293 searchLogic.getDefaultSearchSortDirection(null)); 294 295 vendorSearchEvaluator.setFirstName(firstName); 296 vendorSearchEvaluator.setFirstNameSoundex(false); 297 vendorSearchEvaluator.setMiddleName(middleName); 298 vendorSearchEvaluator.setMiddleNameSoundex(false); 299 vendorSearchEvaluator.setLastName(lastName); 300 vendorSearchEvaluator.setLastNameSoundex(false); 301 vendorSearchEvaluator.setQ(null, q); 302 303 // Avoid using the real ExecutionErrorAccumulator in order to avoid either throwing an Exception or 304 // accumulating errors for this UC. 305 var dummyExecutionErrorAccumulator = new DummyExecutionErrorAccumulator(); 306 vendorSearchEvaluator.execute(dummyExecutionErrorAccumulator); 307 308 if(!dummyExecutionErrorAccumulator.hasExecutionErrors()) { 309 addSearchResults(userVisit, searchType, entityInstances); 310 } 311 } 312 313 private void searchVendors(final Party party, final Set<EntityInstanceTransfer> entityInstances, final String target, 314 final NameResult nameResult) { 315 if(SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(null, party, 316 SecurityRoleGroups.Vendor.name(), SecurityRoles.Search.name())) { 317 var userVisit = getUserVisit(); 318 var searchLogic = SearchLogic.getInstance(); 319 var searchKind = searchLogic.getSearchKindByName(null, SearchKinds.VENDOR.name()); 320 var searchType = searchLogic.getSearchTypeByName(null, searchKind, SearchTypes.IDENTIFY.name()); 321 322 // First attempt using a first and/or last name isolated from target. 323 executeVendorSearch(userVisit, entityInstances, searchLogic, searchKind, searchType, 324 nameResult.getFirstName(), nameResult.getMiddleName(), nameResult.getLastName(), null); 325 // Then attempt searching for target using it as a query string. 326 executeVendorSearch(userVisit, entityInstances, searchLogic, searchKind, searchType, 327 null, null, null, target); 328 } 329 } 330 331 private void executeWarehouseSearch(final UserVisit userVisit, final Set<EntityInstanceTransfer> entityInstances, 332 final SearchLogic searchLogic, final SearchKind searchKind, final SearchType searchType, final String q) { 333 var warehouseSearchEvaluator = new WarehouseSearchEvaluator(userVisit, searchType, 334 searchLogic.getDefaultSearchDefaultOperator(null), 335 searchLogic.getDefaultSearchSortOrder(null, searchKind), 336 searchLogic.getDefaultSearchSortDirection(null)); 337 338 warehouseSearchEvaluator.setQ(null, q); 339 340 // Avoid using the real ExecutionErrorAccumulator in order to avoid either throwing an Exception or 341 // accumulating errors for this UC. 342 var dummyExecutionErrorAccumulator = new DummyExecutionErrorAccumulator(); 343 warehouseSearchEvaluator.execute(dummyExecutionErrorAccumulator); 344 345 if(!dummyExecutionErrorAccumulator.hasExecutionErrors()) { 346 addSearchResults(userVisit, searchType, entityInstances); 347 } 348 } 349 350 private void searchWarehouses(final Party party, final Set<EntityInstanceTransfer> entityInstances, final String target) { 351 if(SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(null, party, 352 SecurityRoleGroups.Warehouse.name(), SecurityRoles.Search.name())) { 353 var userVisit = getUserVisit(); 354 var searchLogic = SearchLogic.getInstance(); 355 var searchKind = searchLogic.getSearchKindByName(null, SearchKinds.WAREHOUSE.name()); 356 var searchType = searchLogic.getSearchTypeByName(null, searchKind, SearchTypes.IDENTIFY.name()); 357 358 // Attempt searching for target using it as a query string. 359 executeWarehouseSearch(userVisit, entityInstances, searchLogic, searchKind, searchType, 360 target); 361 } 362 } 363 364 private void executeItemSearch(final UserVisit userVisit, final Set<EntityInstanceTransfer> entityInstances, 365 final SearchLogic searchLogic, final SearchKind searchKind, final SearchType searchType, 366 final String q) { 367 var itemSearchEvaluator = new ItemSearchEvaluator(userVisit, null, searchType, 368 searchLogic.getDefaultSearchDefaultOperator(null), 369 searchLogic.getDefaultSearchSortOrder(null, searchKind), 370 searchLogic.getDefaultSearchSortDirection(null), 371 null); 372 373 itemSearchEvaluator.setQ(null, q); 374 375 // Avoid using the real ExecutionErrorAccumulator in order to avoid either throwing an Exception or 376 // accumulating errors for this UC. 377 var dummyExecutionErrorAccumulator = new DummyExecutionErrorAccumulator(); 378 itemSearchEvaluator.execute(dummyExecutionErrorAccumulator); 379 380 if(!dummyExecutionErrorAccumulator.hasExecutionErrors()) { 381 addSearchResults(userVisit, searchType, entityInstances); 382 } 383 } 384 385 private void searchItems(final Party party, final Set<EntityInstanceTransfer> entityInstances, final String target) { 386 if(SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(null, party, 387 SecurityRoleGroups.Item.name(), SecurityRoles.Search.name())) { 388 var userVisit = getUserVisit(); 389 var searchLogic = SearchLogic.getInstance(); 390 var searchKind = searchLogic.getSearchKindByName(null, SearchKinds.ITEM.name()); 391 var searchType = searchLogic.getSearchTypeByName(null, searchKind, SearchTypes.IDENTIFY.name()); 392 393 executeItemSearch(userVisit, entityInstances, searchLogic, searchKind, searchType, target); 394 } 395 } 396 397 // Add results from any of the BaseSearchEvaluators to the entityInstances. 398 private void addSearchResults(final UserVisit userVisit, final SearchType searchType, 399 final Set<EntityInstanceTransfer> entityInstances) { 400 var searchControl = Session.getModelController(SearchControl.class); 401 var userVisitSearch = SearchLogic.getInstance().getUserVisitSearch(null, userVisit, searchType); 402 var resultEntityInstances = searchControl.getUserVisitSearchEntityInstances(userVisitSearch); 403 404 for(var resultEntityInstance : resultEntityInstances) { 405 var entityInstanceAndNames = EntityNamesUtils.getInstance().getEntityNames(resultEntityInstance); 406 407 entityInstances.add(fillInEntityInstance(entityInstanceAndNames)); 408 } 409 } 410 411 @Override 412 protected BaseResult execute() { 413 var result = SearchResultFactory.getIdentifyResult(); 414 var entityInstances = new HashSet<EntityInstanceTransfer>(); 415 var target = form.getTarget(); 416 var party = getParty(); 417 418 // Compile a list of all possible EntityInstances that the target may refer to. 419 checkSequenceTypes(party, entityInstances, target); 420 checkItems(party, entityInstances, target); 421 checkCompanies(party, entityInstances, target); 422 checkDivisions(party, entityInstances, target); 423 checkDepartments(party, entityInstances, target); 424 checkWarehouses(party, entityInstances, target); 425 checkLocations(party, entityInstances, target); 426 checkVendors(party, entityInstances, target); 427 checkVendorItems(party, entityInstances, target); 428 checkComponentVendors(party, entityInstances, target); 429 checkEntityTypes(party, entityInstances, target); 430 431 var nameResult = new NameCleaner().getCleansedName(target); 432 searchCustomers(party, entityInstances, target, nameResult); 433 searchVendors(party, entityInstances, target, nameResult); 434 searchWarehouses(party, entityInstances, target); 435 searchItems(party, entityInstances, target); 436 437 result.setEntityInstances(entityInstances); 438 439 return result; 440 } 441 442}