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}