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.party.server.command; 018 019import com.echothree.control.user.party.common.form.CreateCustomerForm; 020import com.echothree.control.user.party.common.result.CreateCustomerResult; 021import com.echothree.control.user.party.common.result.PartyResultFactory; 022import com.echothree.model.control.accounting.common.AccountingConstants; 023import com.echothree.model.control.accounting.server.control.AccountingControl; 024import com.echothree.model.control.cancellationpolicy.common.CancellationKinds; 025import com.echothree.model.control.cancellationpolicy.server.control.CancellationPolicyControl; 026import com.echothree.model.control.contact.common.ContactMechanismPurposes; 027import com.echothree.model.control.contact.server.logic.ContactEmailAddressLogic; 028import com.echothree.model.control.contactlist.server.logic.ContactListLogic; 029import com.echothree.model.control.customer.common.workflow.CustomerCreditStatusConstants; 030import com.echothree.model.control.customer.common.workflow.CustomerStatusConstants; 031import com.echothree.model.control.customer.server.control.CustomerControl; 032import com.echothree.model.control.offer.server.control.OfferControl; 033import com.echothree.model.control.offer.server.control.OfferUseControl; 034import com.echothree.model.control.offer.server.control.SourceControl; 035import com.echothree.model.control.offer.server.control.UseControl; 036import com.echothree.model.control.party.common.PartyTypes; 037import com.echothree.model.control.party.server.control.PartyControl; 038import com.echothree.model.control.party.server.logic.PartyChainLogic; 039import com.echothree.model.control.returnpolicy.common.ReturnKinds; 040import com.echothree.model.control.returnpolicy.server.control.ReturnPolicyControl; 041import com.echothree.model.control.security.common.SecurityRoleGroups; 042import com.echothree.model.control.security.common.SecurityRoles; 043import com.echothree.model.control.shipment.server.control.FreeOnBoardControl; 044import com.echothree.model.control.shipment.server.control.PartyFreeOnBoardControl; 045import com.echothree.model.control.term.server.control.TermControl; 046import com.echothree.model.control.workflow.server.control.WorkflowControl; 047import com.echothree.model.data.accounting.server.entity.Currency; 048import com.echothree.model.data.accounting.server.entity.GlAccount; 049import com.echothree.model.data.cancellationpolicy.server.entity.CancellationKind; 050import com.echothree.model.data.cancellationpolicy.server.entity.CancellationPolicy; 051import com.echothree.model.data.core.server.entity.EntityInstance; 052import com.echothree.model.data.customer.server.entity.Customer; 053import com.echothree.model.data.customer.server.entity.CustomerType; 054import com.echothree.model.data.customer.server.entity.CustomerTypeDetail; 055import com.echothree.model.data.offer.server.entity.Offer; 056import com.echothree.model.data.offer.server.entity.OfferUse; 057import com.echothree.model.data.offer.server.entity.Source; 058import com.echothree.model.data.offer.server.entity.Use; 059import com.echothree.model.data.party.server.entity.DateTimeFormat; 060import com.echothree.model.data.party.server.entity.Language; 061import com.echothree.model.data.party.server.entity.NameSuffix; 062import com.echothree.model.data.party.server.entity.Party; 063import com.echothree.model.data.party.server.entity.PartyType; 064import com.echothree.model.data.party.server.entity.PersonalTitle; 065import com.echothree.model.data.party.server.entity.TimeZone; 066import com.echothree.model.data.returnpolicy.server.entity.ReturnKind; 067import com.echothree.model.data.returnpolicy.server.entity.ReturnPolicy; 068import com.echothree.model.data.term.server.entity.CustomerTypeCreditLimit; 069import com.echothree.model.data.term.server.entity.Term; 070import com.echothree.model.data.user.common.pk.UserVisitPK; 071import com.echothree.model.data.workflow.server.entity.Workflow; 072import com.echothree.model.data.workflow.server.entity.WorkflowEntrance; 073import com.echothree.util.common.command.BaseResult; 074import com.echothree.util.common.message.ExecutionErrors; 075import com.echothree.util.common.persistence.BasePK; 076import com.echothree.util.common.validation.FieldDefinition; 077import com.echothree.util.common.validation.FieldType; 078import com.echothree.util.server.control.BaseSimpleCommand; 079import com.echothree.util.server.control.CommandSecurityDefinition; 080import com.echothree.util.server.control.PartyTypeDefinition; 081import com.echothree.util.server.control.SecurityRoleDefinition; 082import com.echothree.util.server.persistence.EntityPermission; 083import com.echothree.util.server.persistence.Session; 084import java.util.Arrays; 085import java.util.Collections; 086import java.util.List; 087import org.apache.commons.codec.language.Soundex; 088 089public class CreateCustomerCommand 090 extends BaseSimpleCommand<CreateCustomerForm> { 091 092 private final static CommandSecurityDefinition COMMAND_SECURITY_DEFINITION; 093 private final static List<FieldDefinition> FORM_FIELD_DEFINITIONS; 094 095 static { 096 COMMAND_SECURITY_DEFINITION = new CommandSecurityDefinition(Collections.unmodifiableList(Arrays.asList( 097 new PartyTypeDefinition(PartyTypes.UTILITY.name(), null), 098 new PartyTypeDefinition(PartyTypes.EMPLOYEE.name(), Collections.unmodifiableList(Arrays.asList( 099 new SecurityRoleDefinition(SecurityRoleGroups.Customer.name(), SecurityRoles.Create.name()) 100 ))) 101 ))); 102 103 FORM_FIELD_DEFINITIONS = Collections.unmodifiableList(Arrays.asList( 104 new FieldDefinition("CustomerTypeName", FieldType.ENTITY_NAME, false, null, null), 105 new FieldDefinition("CancellationPolicyName", FieldType.ENTITY_NAME, false, null, null), 106 new FieldDefinition("ReturnPolicyName", FieldType.ENTITY_NAME, false, null, null), 107 new FieldDefinition("ArGlAccountName", FieldType.ENTITY_NAME, false, null, null), 108 new FieldDefinition("InitialOfferName", FieldType.ENTITY_NAME, false, null, null), 109 new FieldDefinition("InitialUseName", FieldType.ENTITY_NAME, false, null, null), 110 new FieldDefinition("InitialSourceName", FieldType.ENTITY_NAME, false, null, null), 111 new FieldDefinition("PersonalTitleId", FieldType.ID, false, null, null), 112 new FieldDefinition("FirstName", FieldType.STRING, false, 1L, 20L), 113 new FieldDefinition("MiddleName", FieldType.STRING, false, 1L, 20L), 114 new FieldDefinition("LastName", FieldType.STRING, false, 1L, 20L), 115 new FieldDefinition("NameSuffixId", FieldType.ID, false, null, null), 116 new FieldDefinition("Name", FieldType.STRING, false, 1L, 60L), 117 new FieldDefinition("PreferredLanguageIsoName", FieldType.ENTITY_NAME, false, null, null), 118 new FieldDefinition("PreferredCurrencyIsoName", FieldType.ENTITY_NAME, false, null, null), 119 new FieldDefinition("PreferredJavaTimeZoneName", FieldType.TIME_ZONE_NAME, false, null, null), 120 new FieldDefinition("PreferredDateTimeFormatName", FieldType.ENTITY_NAME, false, null, null), 121 new FieldDefinition("EmailAddress", FieldType.EMAIL_ADDRESS, false, null, null), 122 new FieldDefinition("AllowSolicitation", FieldType.BOOLEAN, true, null, null), 123 new FieldDefinition("CustomerStatusChoice", FieldType.ENTITY_NAME, false, null, null), 124 new FieldDefinition("CustomerCreditStatusChoice", FieldType.ENTITY_NAME, false, null, null) 125 )); 126 } 127 128 /** Creates a new instance of CreateCustomerCommand */ 129 public CreateCustomerCommand(UserVisitPK userVisitPK, CreateCustomerForm form) { 130 super(userVisitPK, form, COMMAND_SECURITY_DEFINITION, FORM_FIELD_DEFINITIONS, false); 131 } 132 133 @Override 134 protected BaseResult execute() { 135 CreateCustomerResult result = PartyResultFactory.getCreateCustomerResult(); 136 var customerControl = Session.getModelController(CustomerControl.class); 137 Customer customer = null; 138 String customerTypeName = form.getCustomerTypeName(); 139 CustomerType customerType = customerTypeName == null ? customerControl.getDefaultCustomerType() : customerControl.getCustomerTypeByName(customerTypeName); 140 141 if(customerType != null) { 142 String cancellationPolicyName = form.getCancellationPolicyName(); 143 CancellationPolicy cancellationPolicy = null; 144 145 if(cancellationPolicyName != null) { 146 var cancellationPolicyControl = Session.getModelController(CancellationPolicyControl.class); 147 CancellationKind returnKind = cancellationPolicyControl.getCancellationKindByName(CancellationKinds.CUSTOMER_CANCELLATION.name()); 148 149 cancellationPolicy = cancellationPolicyControl.getCancellationPolicyByName(returnKind, cancellationPolicyName); 150 } 151 152 if(cancellationPolicyName == null || cancellationPolicy != null) { 153 String returnPolicyName = form.getReturnPolicyName(); 154 ReturnPolicy returnPolicy = null; 155 156 if(returnPolicyName != null) { 157 var returnPolicyControl = Session.getModelController(ReturnPolicyControl.class); 158 ReturnKind returnKind = returnPolicyControl.getReturnKindByName(ReturnKinds.CUSTOMER_RETURN.name()); 159 160 returnPolicy = returnPolicyControl.getReturnPolicyByName(returnKind, returnPolicyName); 161 } 162 163 if(returnPolicyName == null || returnPolicy != null) { 164 var accountingControl = Session.getModelController(AccountingControl.class); 165 String arGlAccountName = form.getArGlAccountName(); 166 GlAccount arGlAccount = arGlAccountName == null ? null : accountingControl.getGlAccountByName(arGlAccountName); 167 168 if(arGlAccountName == null || arGlAccount != null) { 169 String glAccountCategoryName = arGlAccount == null ? null 170 : arGlAccount.getLastDetail().getGlAccountCategory().getLastDetail().getGlAccountCategoryName(); 171 172 if(glAccountCategoryName == null || glAccountCategoryName.equals(AccountingConstants.GlAccountCategory_ACCOUNTS_RECEIVABLE)) { 173 var termControl = Session.getModelController(TermControl.class); 174 CustomerTypeDetail customerTypeDetail = customerType.getLastDetail(); 175 Term term = customerTypeDetail.getDefaultTerm(); 176 177 if(term == null) { 178 term = termControl.getDefaultTerm(); 179 } 180 181 if(term != null) { 182 String initialOfferName = form.getInitialOfferName(); 183 String initialUseName = form.getInitialUseName(); 184 String initialSourceName = form.getInitialSourceName(); 185 OfferUse initialOfferUse = null; 186 boolean invalidInitialOfferOrSourceSpecification = false; 187 188 if(initialOfferName != null && initialUseName != null && initialSourceName == null) { 189 var offerControl = Session.getModelController(OfferControl.class); 190 Offer initialOffer = offerControl.getOfferByName(initialOfferName); 191 192 if(initialOffer != null) { 193 var useControl = Session.getModelController(UseControl.class); 194 Use initialUse = useControl.getUseByName(initialUseName); 195 196 if(initialUse != null) { 197 var offerUseControl = Session.getModelController(OfferUseControl.class); 198 initialOfferUse = offerUseControl.getOfferUse(initialOffer, initialUse); 199 200 if(initialOfferUse == null) { 201 addExecutionError(ExecutionErrors.UnknownInitialOfferUse.name()); 202 } 203 } else { 204 addExecutionError(ExecutionErrors.UnknownInitialUseName.name(), initialUseName); 205 } 206 } else { 207 addExecutionError(ExecutionErrors.UnknownInitialOfferName.name(), initialOfferName); 208 } 209 } else { 210 var sourceControl = Session.getModelController(SourceControl.class); 211 212 if(initialOfferName == null && initialUseName == null && initialSourceName != null) { 213 Source source = sourceControl.getSourceByName(initialSourceName); 214 215 if(source != null) { 216 initialOfferUse = source.getLastDetail().getOfferUse(); 217 } else { 218 addExecutionError(ExecutionErrors.UnknownInitialSourceName.name(), initialSourceName); 219 } 220 } else { 221 initialOfferUse = getUserVisit().getOfferUse(); 222 223 if(initialOfferUse == null) { 224 // If all three parameters are null, then try to get the default Source and use its OfferUse. 225 Source source = sourceControl.getDefaultSource(); 226 227 if(source != null) { 228 initialOfferUse = source.getLastDetail().getOfferUse(); 229 } else { 230 addExecutionError(ExecutionErrors.InvalidInitialOfferOrSourceSpecification.name()); 231 invalidInitialOfferOrSourceSpecification = true; 232 } 233 } 234 } 235 } 236 237 if(initialOfferUse != null) { 238 var partyControl = Session.getModelController(PartyControl.class); 239 String preferredLanguageIsoName = form.getPreferredLanguageIsoName(); 240 Language preferredLanguage = preferredLanguageIsoName == null ? null : partyControl.getLanguageByIsoName(preferredLanguageIsoName); 241 242 if(preferredLanguageIsoName == null || (preferredLanguage != null)) { 243 String preferredJavaTimeZoneName = form.getPreferredJavaTimeZoneName(); 244 TimeZone preferredTimeZone = preferredJavaTimeZoneName == null ? null : partyControl.getTimeZoneByJavaName(preferredJavaTimeZoneName); 245 246 if(preferredJavaTimeZoneName == null || (preferredTimeZone != null)) { 247 String preferredDateTimeFormatName = form.getPreferredDateTimeFormatName(); 248 DateTimeFormat preferredDateTimeFormat = preferredDateTimeFormatName == null ? null : partyControl.getDateTimeFormatByName(preferredDateTimeFormatName); 249 250 if(preferredDateTimeFormatName == null || (preferredDateTimeFormat != null)) { 251 String preferredCurrencyIsoName = form.getPreferredCurrencyIsoName(); 252 Currency preferredCurrency; 253 254 if(preferredCurrencyIsoName == null) { 255 preferredCurrency = null; 256 } else { 257 preferredCurrency = accountingControl.getCurrencyByIsoName(preferredCurrencyIsoName); 258 } 259 260 if(preferredCurrencyIsoName == null || (preferredCurrency != null)) { 261 var freeOnBoardControl = Session.getModelController(FreeOnBoardControl.class); 262 var partyFreeOnBoardControl = Session.getModelController(PartyFreeOnBoardControl.class); 263 var workflowControl = Session.getModelController(WorkflowControl.class); 264 Soundex soundex = new Soundex(); 265 PartyType partyType = partyControl.getPartyTypeByName(PartyTypes.CUSTOMER.name()); 266 BasePK createdBy = getPartyPK(); 267 String personalTitleId = form.getPersonalTitleId(); 268 PersonalTitle personalTitle = personalTitleId == null ? null : partyControl.convertPersonalTitleIdToEntity(personalTitleId, 269 EntityPermission.READ_ONLY); 270 String firstName = form.getFirstName(); 271 String middleName = form.getMiddleName(); 272 String lastName = form.getLastName(); 273 String nameSuffixId = form.getNameSuffixId(); 274 NameSuffix nameSuffix = nameSuffixId == null ? null : partyControl.convertNameSuffixIdToEntity(nameSuffixId, 275 EntityPermission.READ_ONLY); 276 String name = form.getName(); 277 String emailAddress = form.getEmailAddress(); 278 Boolean allowSolicitation = Boolean.valueOf(form.getAllowSolicitation()); 279 280 String firstNameSdx; 281 try { 282 firstNameSdx = firstName == null ? null : soundex.encode(firstName); 283 } catch(IllegalArgumentException iae) { 284 firstNameSdx = null; 285 } 286 287 String middleNameSdx; 288 try { 289 middleNameSdx = middleName == null ? null : soundex.encode(middleName); 290 } catch(IllegalArgumentException iae) { 291 middleNameSdx = null; 292 } 293 294 String lastNameSdx; 295 try { 296 lastNameSdx = lastName == null ? null : soundex.encode(lastName); 297 } catch(IllegalArgumentException iae) { 298 lastNameSdx = null; 299 } 300 301 Party party = partyControl.createParty(null, partyType, preferredLanguage, preferredCurrency, preferredTimeZone, preferredDateTimeFormat, createdBy); 302 303 if(createdBy == null) { 304 createdBy = party.getPrimaryKey(); 305 } 306 if(personalTitle != null || firstName != null || middleName != null || lastName != null || nameSuffix != null) { 307 partyControl.createPerson(party, personalTitle, firstName, firstNameSdx, middleName, middleNameSdx, 308 lastName, lastNameSdx, nameSuffix, createdBy); 309 } 310 311 if(name != null) { 312 partyControl.createPartyGroup(party, name, createdBy); 313 } 314 315 customer = customerControl.createCustomer(party, customerType, initialOfferUse, cancellationPolicy, 316 returnPolicy, arGlAccount, customerTypeDetail.getDefaultHoldUntilComplete(), 317 customerTypeDetail.getDefaultAllowBackorders(), customerTypeDetail.getDefaultAllowSubstitutions(), 318 customerTypeDetail.getDefaultAllowCombiningShipments(), customerTypeDetail.getDefaultRequireReference(), 319 customerTypeDetail.getDefaultAllowReferenceDuplicates(), customerTypeDetail.getDefaultReferenceValidationPattern(), 320 createdBy); 321 322 if(emailAddress != null) { 323 ContactEmailAddressLogic.getInstance().createContactEmailAddress(party, 324 emailAddress, allowSolicitation, null, 325 ContactMechanismPurposes.PRIMARY_EMAIL.name(), createdBy); 326 } 327 328 termControl.createPartyTerm(party, term, customerTypeDetail.getDefaultTaxable(), createdBy); 329 330 partyFreeOnBoardControl.createPartyFreeOnBoard(party, freeOnBoardControl.getDefaultFreeOnBoard(), createdBy); 331 332 for(CustomerTypeCreditLimit customerTypeCreditLimit : termControl.getCustomerTypeCreditLimitsByCustomerType(customerType)) { 333 Currency currency = customerTypeCreditLimit.getCurrency(); 334 Long creditLimit = customerTypeCreditLimit.getCreditLimit(); 335 Long potentialCreditLimit = customerTypeCreditLimit.getPotentialCreditLimit(); 336 337 termControl.createPartyCreditLimit(party, currency, creditLimit, potentialCreditLimit, createdBy); 338 } 339 340 // TODO: error checking for unknown customerStatusChoice 341 String customerStatusChoice = form.getCustomerStatusChoice(); 342 Workflow customerStatusWorkflow = workflowControl.getWorkflowByName(CustomerStatusConstants.Workflow_CUSTOMER_STATUS); 343 WorkflowEntrance customerStatusWorkflowEntrance = customerStatusChoice == null ? customerTypeDetail.getDefaultCustomerStatus() 344 : workflowControl.getWorkflowEntranceByName(customerStatusWorkflow, 345 customerStatusChoice); 346 347 if(customerStatusWorkflowEntrance == null) { 348 customerStatusWorkflowEntrance = workflowControl.getDefaultWorkflowEntrance(customerStatusWorkflow); 349 } 350 351 // TODO: error checking for unknown customerCreditStatusChoice 352 String customerCreditStatusChoice = form.getCustomerCreditStatusChoice(); 353 Workflow customerCreditStatusWorkflow = workflowControl.getWorkflowByName(CustomerCreditStatusConstants.Workflow_CUSTOMER_CREDIT_STATUS); 354 WorkflowEntrance customerCreditStatusWorkflowEntrance = customerCreditStatusChoice == null ? customerTypeDetail.getDefaultCustomerCreditStatus() 355 : workflowControl.getWorkflowEntranceByName(customerCreditStatusWorkflow, customerCreditStatusChoice); 356 357 if(customerCreditStatusWorkflowEntrance == null) { 358 customerCreditStatusWorkflowEntrance = workflowControl.getDefaultWorkflowEntrance(customerCreditStatusWorkflow); 359 } 360 361 EntityInstance entityInstance = getCoreControl().getEntityInstanceByBasePK(party.getPrimaryKey()); 362 workflowControl.addEntityToWorkflow(customerStatusWorkflowEntrance, entityInstance, null, null, createdBy); 363 workflowControl.addEntityToWorkflow(customerCreditStatusWorkflowEntrance, entityInstance, null, null, createdBy); 364 365 ContactListLogic.getInstance().setupInitialContactLists(this, party, createdBy); 366 367 // ExecutionErrorAccumulator is passed in as null so that an Exception will be thrown if there is an error. 368 PartyChainLogic.getInstance().createPartyWelcomeChainInstance(null, party, createdBy); 369 } else { 370 addExecutionError(ExecutionErrors.UnknownCurrencyIsoName.name(), preferredCurrencyIsoName); 371 } 372 } else { 373 addExecutionError(ExecutionErrors.UnknownDateTimeFormatName.name(), preferredDateTimeFormatName); 374 } 375 } else { 376 addExecutionError(ExecutionErrors.UnknownJavaTimeZoneName.name(), preferredJavaTimeZoneName); 377 } 378 } else { 379 addExecutionError(ExecutionErrors.UnknownLanguageIsoName.name(), preferredLanguageIsoName); 380 } 381 } 382 } else { 383 addExecutionError(ExecutionErrors.UnknownDefaultTerm.name()); 384 } 385 } else { 386 addExecutionError(ExecutionErrors.InvalidGlAccountCategory.name(), glAccountCategoryName); 387 } 388 } else { 389 addExecutionError(ExecutionErrors.UnknownArGlAccountName.name(), arGlAccountName); 390 } 391 } else { 392 addExecutionError(ExecutionErrors.UnknownReturnPolicyName.name(), returnPolicyName); 393 } 394 } else { 395 addExecutionError(ExecutionErrors.UnknownCancellationPolicyName.name(), cancellationPolicyName); 396 } 397 } else { 398 if(customerTypeName != null) { 399 addExecutionError(ExecutionErrors.UnknownCustomerTypeName.name(), customerTypeName); 400 } else { 401 addExecutionError(ExecutionErrors.UnknownDefaultCustomerType.name()); 402 } 403 } 404 405 if(customer != null) { 406 Party party = customer.getParty(); 407 408 result.setEntityRef(party.getPrimaryKey().getEntityRef()); 409 result.setCustomerName(customer.getCustomerName()); 410 result.setPartyName(party.getLastDetail().getPartyName()); 411 } 412 413 return result; 414 } 415 416}