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.contact.server.command; 018 019import com.echothree.control.user.contact.common.form.CreateContactPostalAddressForm; 020import com.echothree.control.user.contact.common.result.ContactResultFactory; 021import com.echothree.model.control.contact.common.ContactMechanismTypes; 022import com.echothree.model.control.contact.common.workflow.PostalAddressStatusConstants; 023import com.echothree.model.control.contact.server.control.ContactControl; 024import com.echothree.model.control.core.server.control.EntityInstanceControl; 025import com.echothree.model.control.geo.server.control.GeoControl; 026import com.echothree.model.control.party.common.PartyTypes; 027import com.echothree.model.control.party.server.control.PartyControl; 028import com.echothree.model.control.security.common.SecurityRoleGroups; 029import com.echothree.model.control.security.common.SecurityRoles; 030import com.echothree.model.control.sequence.common.SequenceTypes; 031import com.echothree.model.control.sequence.server.logic.SequenceGeneratorLogic; 032import com.echothree.model.control.workflow.server.control.WorkflowControl; 033import com.echothree.model.data.geo.server.entity.GeoCode; 034import com.echothree.model.data.user.common.pk.UserVisitPK; 035import com.echothree.util.common.command.BaseResult; 036import com.echothree.util.common.command.SecurityResult; 037import com.echothree.util.common.form.ValidationResult; 038import com.echothree.util.common.message.ExecutionErrors; 039import com.echothree.util.common.persistence.BasePK; 040import com.echothree.util.common.string.StringUtils; 041import com.echothree.util.common.validation.FieldDefinition; 042import com.echothree.util.common.validation.FieldType; 043import com.echothree.util.server.control.BaseSimpleCommand; 044import com.echothree.util.server.control.CommandSecurityDefinition; 045import com.echothree.util.server.control.PartyTypeDefinition; 046import com.echothree.util.server.control.SecurityRoleDefinition; 047import com.echothree.util.server.persistence.EntityPermission; 048import com.echothree.util.server.persistence.Session; 049import com.echothree.util.server.validation.Validator; 050import java.util.ArrayList; 051import java.util.Arrays; 052import java.util.Collections; 053import java.util.List; 054import java.util.Locale; 055import org.apache.commons.codec.language.Soundex; 056import javax.enterprise.context.RequestScoped; 057 058@RequestScoped 059public class CreateContactPostalAddressCommand 060 extends BaseSimpleCommand<CreateContactPostalAddressForm> { 061 062 private final static CommandSecurityDefinition COMMAND_SECURITY_DEFINITION; 063 private final static List<FieldDefinition> customerFormFieldDefinitions; 064 private final static List<FieldDefinition> otherFormFieldDefinitions; 065 066 static { 067 COMMAND_SECURITY_DEFINITION = new CommandSecurityDefinition(Collections.unmodifiableList(Arrays.asList( 068 new PartyTypeDefinition(PartyTypes.UTILITY.name(), null), 069 new PartyTypeDefinition(PartyTypes.CUSTOMER.name(), null), 070 new PartyTypeDefinition(PartyTypes.VENDOR.name(), null), 071 new PartyTypeDefinition(PartyTypes.EMPLOYEE.name(), Collections.unmodifiableList(Arrays.asList( 072 new SecurityRoleDefinition(SecurityRoleGroups.ContactMechanism.name(), SecurityRoles.Create.name()) 073 ))) 074 ))); 075 076 // customerFormFieldDefinitions differs from otherFormFieldDefinitions in that when the PartyType 077 // executing this command = CUSTOMER, FirstName and LastName are required fields. For all other 078 // PartyTypes, that requirement is relaxed. 079 List<FieldDefinition> temp = new ArrayList<>(17); 080 temp.add(new FieldDefinition("PartyName", FieldType.ENTITY_NAME, false, null, null)); 081 temp.add(new FieldDefinition("PersonalTitleId", FieldType.ID, false, null, null)); 082 temp.add(new FieldDefinition("FirstName", FieldType.STRING, true, 1L, 20L)); 083 temp.add(new FieldDefinition("MiddleName", FieldType.STRING, false, 1L, 20L)); 084 temp.add(new FieldDefinition("LastName", FieldType.STRING, true, 1L, 20L)); 085 temp.add(new FieldDefinition("NameSuffixId", FieldType.ID, false, null, null)); 086 temp.add(new FieldDefinition("CompanyName", FieldType.STRING, false, 1L, 60L)); 087 temp.add(new FieldDefinition("Attention", FieldType.STRING, false, 1L, 60L)); 088 temp.add(new FieldDefinition("Address1", FieldType.STRING, true, 1L, 40L)); 089 temp.add(new FieldDefinition("Address2", FieldType.STRING, false, 1L, 40L)); 090 temp.add(new FieldDefinition("Address3", FieldType.STRING, false, 1L, 40L)); 091 temp.add(new FieldDefinition("City", FieldType.STRING, false, 1L, 30L)); 092 temp.add(new FieldDefinition("State", FieldType.STRING, false, 1L, 30L)); 093 temp.add(new FieldDefinition("PostalCode", FieldType.STRING, false, 1L, 15L)); 094 temp.add(new FieldDefinition("CountryName", FieldType.ENTITY_NAME, true, null, null)); 095 temp.add(new FieldDefinition("IsCommercial", FieldType.BOOLEAN, true, null, null)); 096 temp.add(new FieldDefinition("AllowSolicitation", FieldType.BOOLEAN, true, null, null)); 097 temp.add(new FieldDefinition("Description", FieldType.STRING, false, 1L, 132L)); 098 customerFormFieldDefinitions = Collections.unmodifiableList(temp); 099 100 temp = new ArrayList<>(17); 101 temp.add(new FieldDefinition("PartyName", FieldType.ENTITY_NAME, false, null, null)); 102 temp.add(new FieldDefinition("PersonalTitleId", FieldType.ID, false, null, null)); 103 temp.add(new FieldDefinition("FirstName", FieldType.STRING, false, 1L, 20L)); 104 temp.add(new FieldDefinition("MiddleName", FieldType.STRING, false, 1L, 20L)); 105 temp.add(new FieldDefinition("LastName", FieldType.STRING, false, 1L, 20L)); 106 temp.add(new FieldDefinition("NameSuffixId", FieldType.ID, false, null, null)); 107 temp.add(new FieldDefinition("CompanyName", FieldType.STRING, false, 1L, 60L)); 108 temp.add(new FieldDefinition("Attention", FieldType.STRING, false, 1L, 60L)); 109 temp.add(new FieldDefinition("Address1", FieldType.STRING, true, 1L, 40L)); 110 temp.add(new FieldDefinition("Address2", FieldType.STRING, false, 1L, 40L)); 111 temp.add(new FieldDefinition("Address3", FieldType.STRING, false, 1L, 40L)); 112 temp.add(new FieldDefinition("City", FieldType.STRING, false, 1L, 30L)); 113 temp.add(new FieldDefinition("State", FieldType.STRING, false, 1L, 30L)); 114 temp.add(new FieldDefinition("PostalCode", FieldType.STRING, false, 1L, 15L)); 115 temp.add(new FieldDefinition("CountryName", FieldType.ENTITY_NAME, true, null, null)); 116 temp.add(new FieldDefinition("IsCommercial", FieldType.BOOLEAN, true, null, null)); 117 temp.add(new FieldDefinition("AllowSolicitation", FieldType.BOOLEAN, true, null, null)); 118 temp.add(new FieldDefinition("Description", FieldType.STRING, false, 1L, 132L)); 119 otherFormFieldDefinitions = Collections.unmodifiableList(temp); 120 } 121 122 /** Creates a new instance of CreateContactPostalAddressCommand */ 123 public CreateContactPostalAddressCommand() { 124 super(COMMAND_SECURITY_DEFINITION, null, false); 125 } 126 127 @Override 128 protected SecurityResult security() { 129 var securityResult = super.security(); 130 131 return securityResult != null ? securityResult : selfOnly(form); 132 } 133 134 @Override 135 protected ValidationResult validate() { 136 var partyTypeName = getPartyTypeName(); 137 var FORM_FIELD_DEFINITIONS = partyTypeName.equals(PartyTypes.CUSTOMER.name())? customerFormFieldDefinitions: otherFormFieldDefinitions; 138 var validator = new Validator(this); 139 var validationResult = validator.validate(form, FORM_FIELD_DEFINITIONS); 140 141 return validationResult; 142 } 143 144 @Override 145 protected BaseResult execute() { 146 var result = ContactResultFactory.getCreateContactPostalAddressResult(); 147 var partyControl = Session.getModelController(PartyControl.class); 148 var partyName = form.getPartyName(); 149 var party = partyName == null ? getParty() : partyControl.getPartyByName(partyName); 150 151 if(party != null) { 152 var geoControl = Session.getModelController(GeoControl.class); 153 var countryName = form.getCountryName(); 154 var countryAlias = StringUtils.getInstance().cleanStringToName(countryName).toUpperCase(Locale.getDefault()); 155 var countryGeoCode = geoControl.getCountryByAlias(countryAlias); 156 157 if(countryGeoCode != null) { 158 var geoCodeCountry = geoControl.getGeoCodeCountry(countryGeoCode); 159 var postalCode = form.getPostalCode(); 160 161 if(postalCode != null) { 162 postalCode = postalCode.toUpperCase(Locale.getDefault()); 163 } 164 165 if(!geoCodeCountry.getPostalCodeRequired() || postalCode != null) { 166 var postalCodePattern = geoCodeCountry.getPostalCodePattern(); 167 var postalCodeLength = geoCodeCountry.getPostalCodeLength(); 168 169 if(postalCodeLength == null) { 170 postalCodeLength = Integer.MAX_VALUE; 171 } 172 173 if(postalCode == null || ((postalCodePattern == null || postalCode.matches(postalCodePattern)) && (postalCode.length() <= postalCodeLength))) { 174 GeoCode postalCodeGeoCode = null; 175 var postalCodeAlias = postalCode == null? null: StringUtils.getInstance().cleanStringToLettersOrDigits(StringUtils.getInstance().cleanStringToName(postalCode)); 176 177 if(postalCodeAlias != null) { 178 var postalCodeAliasLength = postalCodeAlias.length(); 179 var postalCodeGeoCodeLength = geoCodeCountry.getPostalCodeGeoCodeLength(); 180 181 if(postalCodeGeoCodeLength == null || postalCodeAliasLength >= postalCodeGeoCodeLength) { 182 if(postalCodeGeoCodeLength != null && postalCodeAliasLength > postalCodeGeoCodeLength) { 183 postalCodeAlias = postalCodeAlias.substring(0, postalCodeGeoCodeLength); 184 } 185 186 postalCodeGeoCode = geoControl.getPostalCodeByAlias(countryGeoCode, postalCodeAlias); 187 } 188 } 189 190 if(!geoCodeCountry.getPostalCodeGeoCodeRequired() || postalCodeGeoCode != null) { 191 var state = form.getState(); 192 193 if(!geoCodeCountry.getStateRequired() || state != null) { 194 GeoCode stateGeoCode = null; 195 var stateAlias = state == null? null: StringUtils.getInstance().cleanStringToName(state).toUpperCase(Locale.getDefault()); 196 197 if(stateAlias != null) { 198 stateGeoCode = geoControl.getStateByAlias(countryGeoCode, stateAlias); 199 } 200 201 if(!geoCodeCountry.getStateGeoCodeRequired() || stateGeoCode != null) { 202 var city = form.getCity(); 203 204 if(!geoCodeCountry.getCityRequired() || city != null) { 205 GeoCode cityGeoCode = null; 206 var cityAlias = city == null? null: StringUtils.getInstance().cleanStringToName(city).toUpperCase(Locale.getDefault()); 207 208 if(stateGeoCode != null && cityAlias != null) { 209 cityGeoCode = geoControl.getCityByAlias(stateGeoCode, cityAlias); 210 } 211 212 if(!geoCodeCountry.getCityGeoCodeRequired() || cityGeoCode != null) { 213 GeoCode countyGeoCode = null; 214 215 var contactControl = Session.getModelController(ContactControl.class); 216 var entityInstanceControl = Session.getModelController(EntityInstanceControl.class); 217 var workflowControl = Session.getModelController(WorkflowControl.class); 218 var soundex = new Soundex(); 219 BasePK createdBy = getPartyPK(); 220 var personalTitleId = form.getPersonalTitleId(); 221 var personalTitle = personalTitleId == null? null: partyControl.convertPersonalTitleIdToEntity(personalTitleId, EntityPermission.READ_ONLY); 222 var firstName = form.getFirstName(); 223 var middleName = form.getMiddleName(); 224 var lastName = form.getLastName(); 225 var nameSuffixId = form.getNameSuffixId(); 226 var nameSuffix = nameSuffixId == null? null: partyControl.convertNameSuffixIdToEntity(nameSuffixId, EntityPermission.READ_ONLY); 227 var companyName = form.getCompanyName(); 228 var attention = form.getAttention(); 229 var address1 = form.getAddress1(); 230 var address2 = form.getAddress2(); 231 var address3 = form.getAddress3(); 232 var allowSolicitation = Boolean.valueOf(form.getAllowSolicitation()); 233 var isCommercial = Boolean.valueOf(form.getIsCommercial()); 234 var description = form.getDescription(); 235 var contactMechanismName = SequenceGeneratorLogic.getInstance().getNextSequenceValue(null, SequenceTypes.CONTACT_MECHANISM.name()); 236 237 String firstNameSdx; 238 try { 239 firstNameSdx = firstName == null? null: soundex.encode(firstName); 240 } catch (IllegalArgumentException iae) { 241 firstNameSdx = null; 242 } 243 244 String middleNameSdx; 245 try { 246 middleNameSdx = middleName == null? null: soundex.encode(middleName); 247 } catch (IllegalArgumentException iae) { 248 middleNameSdx = null; 249 } 250 251 String lastNameSdx; 252 try { 253 lastNameSdx = lastName == null? null: soundex.encode(lastName); 254 } catch (IllegalArgumentException iae) { 255 lastNameSdx = null; 256 } 257 258 var contactMechanismType = contactControl.getContactMechanismTypeByName(ContactMechanismTypes.POSTAL_ADDRESS.name()); 259 var contactMechanism = contactControl.createContactMechanism(contactMechanismName, contactMechanismType, 260 allowSolicitation, createdBy); 261 262 contactControl.createContactPostalAddress(contactMechanism, personalTitle, firstName, firstNameSdx, middleName, 263 middleNameSdx, lastName, lastNameSdx, nameSuffix, companyName, attention, address1, address2, address3, city, 264 cityGeoCode, countyGeoCode, state, stateGeoCode, postalCode, postalCodeGeoCode, countryGeoCode, isCommercial, 265 createdBy); 266 267 contactControl.createPartyContactMechanism(party, contactMechanism, description, 268 false, 1, createdBy); 269 270 var entityInstance = entityInstanceControl.getEntityInstanceByBasePK(contactMechanism.getPrimaryKey()); 271 workflowControl.addEntityToWorkflowUsingNames(null, PostalAddressStatusConstants.Workflow_POSTAL_ADDRESS_STATUS, 272 PostalAddressStatusConstants.WorkflowEntrance_NEW_POSTAL_ADDRESS, entityInstance, null, null, createdBy); 273 274 result.setContactMechanismName(contactMechanism.getLastDetail().getContactMechanismName()); 275 result.setEntityRef(contactMechanism.getPrimaryKey().getEntityRef()); 276 } else { 277 addExecutionError(ExecutionErrors.UnknownCity.name(), city, cityAlias); 278 } 279 } else { 280 addExecutionError(ExecutionErrors.MissingCity.name()); 281 } 282 } else { 283 addExecutionError(ExecutionErrors.UnknownState.name(), state, stateAlias); 284 } 285 } else { 286 addExecutionError(ExecutionErrors.MissingState.name()); 287 } 288 } else { 289 addExecutionError(ExecutionErrors.UnknownPostalCode.name(), postalCode); 290 } 291 } else { 292 addExecutionError(ExecutionErrors.InvalidPostalCode.name(), postalCode); 293 } 294 } else { 295 addExecutionError(ExecutionErrors.MissingPostalCode.name()); 296 } 297 } else { 298 addExecutionError(ExecutionErrors.UnknownCountryName.name(), countryName, countryAlias); 299 } 300 } else { 301 addExecutionError(ExecutionErrors.UnknownPartyName.name(), partyName); 302 } 303 304 return result; 305 } 306 307}