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