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.contact.server.command; 018 019import com.echothree.control.user.contact.common.edit.ContactEditFactory; 020import com.echothree.control.user.contact.common.edit.ContactPostalAddressEdit; 021import com.echothree.control.user.contact.common.result.ContactResultFactory; 022import com.echothree.control.user.contact.common.result.EditContactPostalAddressResult; 023import com.echothree.control.user.contact.common.spec.PartyContactMechanismSpec; 024import com.echothree.model.control.contact.common.ContactMechanismTypes; 025import com.echothree.model.control.contact.server.control.ContactControl; 026import com.echothree.model.control.geo.server.control.GeoControl; 027import com.echothree.model.control.party.common.PartyTypes; 028import com.echothree.model.control.party.server.control.PartyControl; 029import com.echothree.model.control.security.common.SecurityRoleGroups; 030import com.echothree.model.control.security.common.SecurityRoles; 031import com.echothree.model.data.contact.server.entity.ContactMechanism; 032import com.echothree.model.data.contact.server.entity.PartyContactMechanism; 033import com.echothree.model.data.geo.server.entity.GeoCode; 034import com.echothree.util.common.command.EditMode; 035import com.echothree.util.common.command.SecurityResult; 036import com.echothree.util.common.message.ExecutionErrors; 037import com.echothree.util.common.string.StringUtils; 038import com.echothree.util.common.validation.FieldDefinition; 039import com.echothree.util.common.validation.FieldType; 040import com.echothree.util.server.control.BaseAbstractEditCommand; 041import com.echothree.util.server.control.CommandSecurityDefinition; 042import com.echothree.util.server.control.PartyTypeDefinition; 043import com.echothree.util.server.control.SecurityRoleDefinition; 044import com.echothree.util.server.persistence.EntityPermission; 045import com.echothree.util.server.persistence.Session; 046import java.util.List; 047import java.util.Locale; 048import org.apache.commons.codec.language.Soundex; 049import javax.enterprise.context.Dependent; 050 051@Dependent 052public class EditContactPostalAddressCommand 053 extends BaseAbstractEditCommand<PartyContactMechanismSpec, ContactPostalAddressEdit, EditContactPostalAddressResult, PartyContactMechanism, ContactMechanism> { 054 055 private final static CommandSecurityDefinition COMMAND_SECURITY_DEFINITION; 056 private final static List<FieldDefinition> SPEC_FIELD_DEFINITIONS; 057 private final static List<FieldDefinition> editCustomerFieldDefinitions; 058 private final static List<FieldDefinition> editOtherFieldDefinitions; 059 060 static { 061 COMMAND_SECURITY_DEFINITION = new CommandSecurityDefinition(List.of( 062 new PartyTypeDefinition(PartyTypes.UTILITY.name(), null), 063 new PartyTypeDefinition(PartyTypes.CUSTOMER.name(), null), 064 new PartyTypeDefinition(PartyTypes.VENDOR.name(), null), 065 new PartyTypeDefinition(PartyTypes.EMPLOYEE.name(), List.of( 066 new SecurityRoleDefinition(SecurityRoleGroups.ContactMechanism.name(), SecurityRoles.Edit.name()) 067 )) 068 )); 069 070 SPEC_FIELD_DEFINITIONS = List.of( 071 new FieldDefinition("PartyName", FieldType.ENTITY_NAME, false, null, null), 072 new FieldDefinition("ContactMechanismName", FieldType.ENTITY_NAME, true, null, null) 073 ); 074 075 // customerFormFieldDefinitions differs from otherFormFieldDefinitions in that when the PartyType 076 // executing this command = CUSTOMER, FirstName and LastName are required fields. For all other 077 // PartyTypes, that requirement is relaxed. 078 editCustomerFieldDefinitions = List.of( 079 new FieldDefinition("PersonalTitleId", FieldType.ID, false, null, null), 080 new FieldDefinition("FirstName", FieldType.STRING, true, 1L, 20L), 081 new FieldDefinition("MiddleName", FieldType.STRING, false, 1L, 20L), 082 new FieldDefinition("LastName", FieldType.STRING, true, 1L, 20L), 083 new FieldDefinition("NameSuffixId", FieldType.ID, false, null, null), 084 new FieldDefinition("CompanyName", FieldType.STRING, false, 1L, 60L), 085 new FieldDefinition("Attention", FieldType.STRING, false, 1L, 60L), 086 new FieldDefinition("Address1", FieldType.STRING, true, 1L, 40L), 087 new FieldDefinition("Address2", FieldType.STRING, false, 1L, 40L), 088 new FieldDefinition("Address3", FieldType.STRING, false, 1L, 40L), 089 new FieldDefinition("City", FieldType.STRING, false, 1L, 30L), 090 new FieldDefinition("State", FieldType.STRING, false, 1L, 30L), 091 new FieldDefinition("PostalCode", FieldType.STRING, false, 1L, 15L), 092 new FieldDefinition("CountryName", FieldType.ENTITY_NAME, false, null, null), 093 new FieldDefinition("IsCommercial", FieldType.BOOLEAN, true, null, null), 094 new FieldDefinition("AllowSolicitation", FieldType.BOOLEAN, true, null, null), 095 new FieldDefinition("Description", FieldType.STRING, false, 1L, 132L) 096 ); 097 098 editOtherFieldDefinitions = List.of( 099 new FieldDefinition("PersonalTitleId", FieldType.ID, false, null, null), 100 new FieldDefinition("FirstName", FieldType.STRING, false, 1L, 20L), 101 new FieldDefinition("MiddleName", FieldType.STRING, false, 1L, 20L), 102 new FieldDefinition("LastName", FieldType.STRING, false, 1L, 20L), 103 new FieldDefinition("NameSuffixId", FieldType.ID, false, null, null), 104 new FieldDefinition("CompanyName", FieldType.STRING, false, 1L, 60L), 105 new FieldDefinition("Attention", FieldType.STRING, false, 1L, 60L), 106 new FieldDefinition("Address1", FieldType.STRING, true, 1L, 40L), 107 new FieldDefinition("Address2", FieldType.STRING, false, 1L, 40L), 108 new FieldDefinition("Address3", FieldType.STRING, false, 1L, 40L), 109 new FieldDefinition("City", FieldType.STRING, false, 1L, 30L), 110 new FieldDefinition("State", FieldType.STRING, false, 1L, 30L), 111 new FieldDefinition("PostalCode", FieldType.STRING, false, 1L, 15L), 112 new FieldDefinition("CountryName", FieldType.ENTITY_NAME, false, null, null), 113 new FieldDefinition("IsCommercial", FieldType.BOOLEAN, true, null, null), 114 new FieldDefinition("AllowSolicitation", FieldType.BOOLEAN, true, null, null), 115 new FieldDefinition("Description", FieldType.STRING, false, 1L, 132L) 116 ); 117 } 118 119 /** Creates a new instance of EditContactPostalAddressCommand */ 120 public EditContactPostalAddressCommand() { 121 super(COMMAND_SECURITY_DEFINITION, SPEC_FIELD_DEFINITIONS, null); 122 } 123 124 @Override 125 protected List<FieldDefinition> getEditFieldDefinitions() { 126 var partyTypeName = getPartyTypeName(); 127 128 return partyTypeName.equals(PartyTypes.CUSTOMER.name()) ? editCustomerFieldDefinitions : editOtherFieldDefinitions; 129 } 130 131 @Override 132 protected SecurityResult security() { 133 var securityResult = super.security(); 134 135 return securityResult != null ? securityResult : selfOnly(spec); 136 } 137 138 @Override 139 public EditContactPostalAddressResult getResult() { 140 return ContactResultFactory.getEditContactPostalAddressResult(); 141 } 142 143 @Override 144 public ContactPostalAddressEdit getEdit() { 145 return ContactEditFactory.getContactPostalAddressEdit(); 146 } 147 148 @Override 149 public PartyContactMechanism getEntity(EditContactPostalAddressResult result) { 150 var partyControl = Session.getModelController(PartyControl.class); 151 PartyContactMechanism partyContactMechanism = null; 152 var partyName = spec.getPartyName(); 153 var party = partyName == null ? getParty() : partyControl.getPartyByName(partyName); 154 155 if(party != null) { 156 var contactControl = Session.getModelController(ContactControl.class); 157 var contactMechanismName = spec.getContactMechanismName(); 158 var contactMechanism = contactControl.getContactMechanismByName(contactMechanismName); 159 160 if(contactMechanism != null) { 161 if(editMode.equals(EditMode.LOCK) || editMode.equals(EditMode.ABANDON)) { 162 partyContactMechanism = contactControl.getPartyContactMechanism(party, contactMechanism); 163 } else { // EditMode.UPDATE 164 partyContactMechanism = contactControl.getPartyContactMechanismForUpdate(party, contactMechanism); 165 } 166 167 if(partyContactMechanism != null) { 168 var lastContactMechanismDetail = contactMechanism.getLastDetail(); 169 var contactMechanismTypeName = lastContactMechanismDetail.getContactMechanismType().getContactMechanismTypeName(); 170 171 result.setContactMechanism(contactControl.getContactMechanismTransfer(getUserVisit(), contactMechanism)); 172 173 if(!ContactMechanismTypes.POSTAL_ADDRESS.name().equals(contactMechanismTypeName)) { 174 addExecutionError(ExecutionErrors.InvalidContactMechanismType.name(), contactMechanismTypeName); 175 } 176 } else { 177 addExecutionError(ExecutionErrors.UnknownPartyContactMechanism.name(), partyName, contactMechanismName); 178 } 179 } else { 180 addExecutionError(ExecutionErrors.UnknownContactMechanismName.name(), contactMechanismName); 181 } 182 } else { 183 addExecutionError(ExecutionErrors.UnknownPartyName.name(), partyName); 184 } 185 186 return partyContactMechanism; 187 } 188 189 @Override 190 public ContactMechanism getLockEntity(PartyContactMechanism partyContactMechanism) { 191 return partyContactMechanism.getLastDetail().getContactMechanism(); 192 } 193 194 @Override 195 public void fillInResult(EditContactPostalAddressResult result, PartyContactMechanism partyContactMechanism) { 196 var contactControl = Session.getModelController(ContactControl.class); 197 198 result.setContactMechanism(contactControl.getContactMechanismTransfer(getUserVisit(), 199 partyContactMechanism.getLastDetail().getContactMechanism())); 200 } 201 202 @Override 203 public void doLock(ContactPostalAddressEdit edit, PartyContactMechanism partyContactMechanism) { 204 var contactControl = Session.getModelController(ContactControl.class); 205 var geoControl = Session.getModelController(GeoControl.class); 206 var contactMechanism = partyContactMechanism.getLastDetail().getContactMechanism(); 207 var contactMechanismDetail = contactMechanism.getLastDetail(); 208 var contactPostalAddress = contactControl.getContactPostalAddress(contactMechanism); 209 var partyContactMechanismDetail = partyContactMechanism.getLastDetail(); 210 211 var personalTitle = contactPostalAddress.getPersonalTitle(); 212 var nameSuffix = contactPostalAddress.getNameSuffix(); 213 214 var city = contactPostalAddress.getCity(); 215 var state = contactPostalAddress.getState(); 216 var postalCode = contactPostalAddress.getPostalCode(); 217 218 var cityGeoCode = city == null ? contactPostalAddress.getCityGeoCode() : null; 219 var stateGeoCode = state == null ? contactPostalAddress.getStateGeoCode() : null; 220 var postalCodeGeoCode = postalCode == null ? contactPostalAddress.getPostalCodeGeoCode() : null; 221 222 var cityGeoCodeDescription = cityGeoCode == null ? null : geoControl.getBestGeoCodeDescription(cityGeoCode, getPreferredLanguage()); 223 var stateGeoCodeDescription = stateGeoCode == null ? null : geoControl.getBestGeoCodeDescription(stateGeoCode, getPreferredLanguage()); 224 var postalCodeGeoCodeDescription = postalCodeGeoCode == null ? null : geoControl.getBestGeoCodeDescription(postalCodeGeoCode, getPreferredLanguage()); 225 226 edit.setAllowSolicitation(contactMechanismDetail.getAllowSolicitation().toString()); 227 edit.setPersonalTitleId(personalTitle == null ? null : personalTitle.getPrimaryKey().getEntityId().toString()); 228 edit.setFirstName(contactPostalAddress.getFirstName()); 229 edit.setMiddleName(contactPostalAddress.getMiddleName()); 230 edit.setLastName(contactPostalAddress.getLastName()); 231 edit.setNameSuffixId(nameSuffix == null ? null : nameSuffix.getPrimaryKey().getEntityId().toString()); 232 edit.setCompanyName(contactPostalAddress.getCompanyName()); 233 edit.setAttention(contactPostalAddress.getAttention()); 234 edit.setAddress1(contactPostalAddress.getAddress1()); 235 edit.setAddress2(contactPostalAddress.getAddress2()); 236 edit.setAddress3(contactPostalAddress.getAddress3()); 237 edit.setCity(city == null ? (cityGeoCode == null ? null : (cityGeoCodeDescription == null ? geoControl.getAliasForCity(cityGeoCode) : cityGeoCodeDescription)) : city); 238 edit.setState(state == null ? (stateGeoCode == null ? null : (stateGeoCodeDescription == null ? geoControl.getAliasForState(stateGeoCode) : stateGeoCodeDescription)) : state); 239 edit.setPostalCode(postalCode == null ? (postalCodeGeoCode == null ? null : (postalCodeGeoCodeDescription == null ? geoControl.getAliasForPostalCode(postalCodeGeoCode) : postalCodeGeoCodeDescription)) : postalCode); 240 edit.setCountryName(geoControl.getAliasForCountry(contactPostalAddress.getCountryGeoCode())); 241 edit.setIsCommercial(contactPostalAddress.getIsCommercial().toString()); 242 edit.setDescription(partyContactMechanismDetail.getDescription()); 243 } 244 245 GeoCode countryGeoCode; 246 String state; 247 GeoCode stateGeoCode; 248 String city; 249 GeoCode cityGeoCode; 250 String postalCode; 251 GeoCode postalCodeGeoCode; 252 GeoCode countyGeoCode; 253 254 @Override 255 public void canUpdate(PartyContactMechanism partyContactMechanism) { 256 var geoControl = Session.getModelController(GeoControl.class); 257 var countryName = edit.getCountryName(); 258 var countryAlias = StringUtils.getInstance().cleanStringToName(countryName).toUpperCase(Locale.getDefault()); 259 260 countryGeoCode = geoControl.getCountryByAlias(countryAlias); 261 262 if(countryGeoCode != null) { 263 var geoCodeCountry = geoControl.getGeoCodeCountry(countryGeoCode); 264 265 postalCode = edit.getPostalCode(); 266 267 if(postalCode != null) { 268 postalCode = postalCode.toUpperCase(Locale.getDefault()); 269 } 270 271 if(!geoCodeCountry.getPostalCodeRequired() || postalCode != null) { 272 var postalCodePattern = geoCodeCountry.getPostalCodePattern(); 273 var postalCodeLength = geoCodeCountry.getPostalCodeLength(); 274 275 if(postalCodeLength == null) { 276 postalCodeLength = Integer.MAX_VALUE; 277 } 278 279 if(postalCode == null || ((postalCodePattern == null || postalCode.matches(postalCodePattern)) && (postalCode.length() <= postalCodeLength))) { 280 var postalCodeAlias = postalCode == null ? null : StringUtils.getInstance().cleanStringToLettersOrDigits(StringUtils.getInstance().cleanStringToName(postalCode)); 281 282 if(postalCodeAlias != null) { 283 var postalCodeAliasLength = postalCodeAlias.length(); 284 var postalCodeGeoCodeLength = geoCodeCountry.getPostalCodeGeoCodeLength(); 285 286 if(postalCodeGeoCodeLength == null || postalCodeAliasLength >= postalCodeGeoCodeLength) { 287 if(postalCodeGeoCodeLength != null && postalCodeAliasLength > postalCodeGeoCodeLength) { 288 postalCodeAlias = postalCodeAlias.substring(0, postalCodeGeoCodeLength); 289 } 290 291 postalCodeGeoCode = geoControl.getPostalCodeByAlias(countryGeoCode, postalCodeAlias); 292 } 293 } 294 295 if(!geoCodeCountry.getPostalCodeGeoCodeRequired() || postalCodeGeoCode != null) { 296 state = edit.getState(); 297 298 if(!geoCodeCountry.getStateRequired() || state != null) { 299 var stateAlias = state == null ? null : StringUtils.getInstance().cleanStringToName(state).toUpperCase(Locale.getDefault()); 300 301 if(stateAlias != null) { 302 stateGeoCode = geoControl.getStateByAlias(countryGeoCode, stateAlias); 303 } 304 305 if(!geoCodeCountry.getStateGeoCodeRequired() || stateGeoCode != null) { 306 city = edit.getCity(); 307 308 if(!geoCodeCountry.getCityRequired() || city != null) { 309 var cityAlias = city == null ? null : StringUtils.getInstance().cleanStringToName(city).toUpperCase(Locale.getDefault()); 310 311 if(stateGeoCode != null && cityAlias != null) { 312 cityGeoCode = geoControl.getCityByAlias(stateGeoCode, cityAlias); 313 } 314 315 if(!geoCodeCountry.getCityGeoCodeRequired() || cityGeoCode != null) { 316 // TODO: countyGeoCode 317 } else { 318 addExecutionError(ExecutionErrors.UnknownCity.name(), city, cityAlias); 319 } 320 } else { 321 addExecutionError(ExecutionErrors.MissingCity.name()); 322 } 323 } else { 324 addExecutionError(ExecutionErrors.UnknownState.name(), state, stateAlias); 325 } 326 } else { 327 addExecutionError(ExecutionErrors.MissingState.name()); 328 } 329 } else { 330 addExecutionError(ExecutionErrors.UnknownPostalCode.name(), postalCode); 331 } 332 } else { 333 addExecutionError(ExecutionErrors.InvalidPostalCode.name(), postalCode); 334 } 335 } else { 336 addExecutionError(ExecutionErrors.MissingPostalCode.name()); 337 } 338 } else { 339 addExecutionError(ExecutionErrors.UnknownCountryName.name(), countryName, countryAlias); 340 } 341 } 342 343 @Override 344 public void doUpdate(PartyContactMechanism partyContactMechanism) { 345 var contactControl = Session.getModelController(ContactControl.class); 346 var partyControl = Session.getModelController(PartyControl.class); 347 var soundex = new Soundex(); 348 var updatedBy = getPartyPK(); 349 var contactMechanism = partyContactMechanism.getLastDetail().getContactMechanism(); 350 var contactMechanismDetailValue = contactControl.getContactMechanismDetailValue(contactMechanism.getLastDetail()); 351 var contactPostalAddressValue = contactControl.getContactPostalAddressValueForUpdate(contactMechanism); 352 var partyContactMechanismDetailValue = contactControl.getPartyContactMechanismDetailValueForUpdate(partyContactMechanism); 353 354 var personalTitleId = edit.getPersonalTitleId(); 355 var personalTitle = personalTitleId == null ? null : partyControl.convertPersonalTitleIdToEntity(personalTitleId, EntityPermission.READ_ONLY); 356 var firstName = edit.getFirstName(); 357 var middleName = edit.getMiddleName(); 358 var lastName = edit.getLastName(); 359 var nameSuffixId = edit.getNameSuffixId(); 360 var nameSuffix = nameSuffixId == null ? null : partyControl.convertNameSuffixIdToEntity(nameSuffixId, EntityPermission.READ_ONLY); 361 362 String firstNameSdx; 363 try { 364 firstNameSdx = firstName == null ? null : soundex.encode(firstName); 365 } catch(IllegalArgumentException iae) { 366 firstNameSdx = null; 367 } 368 369 String middleNameSdx; 370 try { 371 middleNameSdx = middleName == null ? null : soundex.encode(middleName); 372 } catch(IllegalArgumentException iae) { 373 middleNameSdx = null; 374 } 375 376 String lastNameSdx; 377 try { 378 lastNameSdx = lastName == null ? null : soundex.encode(lastName); 379 } catch(IllegalArgumentException iae) { 380 lastNameSdx = null; 381 } 382 383 contactMechanismDetailValue.setAllowSolicitation(Boolean.valueOf(edit.getAllowSolicitation())); 384 contactPostalAddressValue.setCountryGeoCodePK(countryGeoCode.getPrimaryKey()); 385 contactPostalAddressValue.setPersonalTitlePK(personalTitle == null ? null : personalTitle.getPrimaryKey()); 386 contactPostalAddressValue.setFirstName(firstName); 387 contactPostalAddressValue.setFirstNameSdx(firstNameSdx); 388 contactPostalAddressValue.setMiddleName(middleName); 389 contactPostalAddressValue.setMiddleNameSdx(middleNameSdx); 390 contactPostalAddressValue.setLastName(lastName); 391 contactPostalAddressValue.setLastNameSdx(lastNameSdx); 392 contactPostalAddressValue.setNameSuffixPK(nameSuffix == null ? null : nameSuffix.getPrimaryKey()); 393 contactPostalAddressValue.setCompanyName(edit.getCompanyName()); 394 contactPostalAddressValue.setAttention(edit.getAttention()); 395 contactPostalAddressValue.setAddress1(edit.getAddress1()); 396 contactPostalAddressValue.setAddress2(edit.getAddress2()); 397 contactPostalAddressValue.setAddress3(edit.getAddress3()); 398 contactPostalAddressValue.setCity(city); 399 contactPostalAddressValue.setCityGeoCodePK(cityGeoCode == null ? null : cityGeoCode.getPrimaryKey()); 400 contactPostalAddressValue.setCountyGeoCodePK(countyGeoCode == null ? null : countyGeoCode.getPrimaryKey()); 401 contactPostalAddressValue.setState(state); 402 contactPostalAddressValue.setStateGeoCodePK(stateGeoCode == null ? null : stateGeoCode.getPrimaryKey()); 403 contactPostalAddressValue.setPostalCode(postalCode); 404 contactPostalAddressValue.setPostalCodeGeoCodePK(postalCodeGeoCode == null ? null : postalCodeGeoCode.getPrimaryKey()); 405 contactPostalAddressValue.setCountryGeoCodePK(countryGeoCode == null ? null : countryGeoCode.getPrimaryKey()); 406 contactPostalAddressValue.setIsCommercial(Boolean.valueOf(edit.getIsCommercial())); 407 partyContactMechanismDetailValue.setDescription(edit.getDescription()); 408 409 contactControl.updateContactMechanismFromValue(contactMechanismDetailValue, updatedBy); 410 contactControl.updateContactPostalAddressFromValue(contactPostalAddressValue, updatedBy); 411 contactControl.updatePartyContactMechanismFromValue(partyContactMechanismDetailValue, updatedBy); 412 } 413 414} 415