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