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