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 com.echothree.util.server.persistence.Session; 064import java.util.List; 065import org.apache.commons.codec.language.Soundex; 066import javax.enterprise.context.Dependent; 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 /** Creates a new instance of CreateCustomerCommand */ 109 public CreateCustomerCommand() { 110 super(COMMAND_SECURITY_DEFINITION, FORM_FIELD_DEFINITIONS, false); 111 } 112 113 @Override 114 protected BaseResult execute() { 115 var result = PartyResultFactory.getCreateCustomerResult(); 116 var customerControl = Session.getModelController(CustomerControl.class); 117 Customer customer = null; 118 var customerTypeName = form.getCustomerTypeName(); 119 var customerType = customerTypeName == null ? customerControl.getDefaultCustomerType() : customerControl.getCustomerTypeByName(customerTypeName); 120 121 if(customerType != null) { 122 var cancellationPolicyName = form.getCancellationPolicyName(); 123 CancellationPolicy cancellationPolicy = null; 124 125 if(cancellationPolicyName != null) { 126 var cancellationPolicyControl = Session.getModelController(CancellationPolicyControl.class); 127 var returnKind = cancellationPolicyControl.getCancellationKindByName(CancellationKinds.CUSTOMER_CANCELLATION.name()); 128 129 cancellationPolicy = cancellationPolicyControl.getCancellationPolicyByName(returnKind, cancellationPolicyName); 130 } 131 132 if(cancellationPolicyName == null || cancellationPolicy != null) { 133 var returnPolicyName = form.getReturnPolicyName(); 134 ReturnPolicy returnPolicy = null; 135 136 if(returnPolicyName != null) { 137 var returnPolicyControl = Session.getModelController(ReturnPolicyControl.class); 138 var returnKind = returnPolicyControl.getReturnKindByName(ReturnKinds.CUSTOMER_RETURN.name()); 139 140 returnPolicy = returnPolicyControl.getReturnPolicyByName(returnKind, returnPolicyName); 141 } 142 143 if(returnPolicyName == null || returnPolicy != null) { 144 var accountingControl = Session.getModelController(AccountingControl.class); 145 var arGlAccountName = form.getArGlAccountName(); 146 var arGlAccount = arGlAccountName == null ? null : accountingControl.getGlAccountByName(arGlAccountName); 147 148 if(arGlAccountName == null || arGlAccount != null) { 149 var glAccountCategoryName = arGlAccount == null ? null 150 : arGlAccount.getLastDetail().getGlAccountCategory().getLastDetail().getGlAccountCategoryName(); 151 152 if(glAccountCategoryName == null || glAccountCategoryName.equals(AccountingConstants.GlAccountCategory_ACCOUNTS_RECEIVABLE)) { 153 var termControl = Session.getModelController(TermControl.class); 154 var customerTypeDetail = customerType.getLastDetail(); 155 var term = customerTypeDetail.getDefaultTerm(); 156 157 if(term == null) { 158 term = termControl.getDefaultTerm(); 159 } 160 161 if(term != null) { 162 var initialOfferName = form.getInitialOfferName(); 163 var initialUseName = form.getInitialUseName(); 164 var initialSourceName = form.getInitialSourceName(); 165 OfferUse initialOfferUse = null; 166 var invalidInitialOfferOrSourceSpecification = false; 167 168 if(initialOfferName != null && initialUseName != null && initialSourceName == null) { 169 var offerControl = Session.getModelController(OfferControl.class); 170 var initialOffer = offerControl.getOfferByName(initialOfferName); 171 172 if(initialOffer != null) { 173 var useControl = Session.getModelController(UseControl.class); 174 var initialUse = useControl.getUseByName(initialUseName); 175 176 if(initialUse != null) { 177 var offerUseControl = Session.getModelController(OfferUseControl.class); 178 initialOfferUse = offerUseControl.getOfferUse(initialOffer, initialUse); 179 180 if(initialOfferUse == null) { 181 addExecutionError(ExecutionErrors.UnknownInitialOfferUse.name()); 182 } 183 } else { 184 addExecutionError(ExecutionErrors.UnknownInitialUseName.name(), initialUseName); 185 } 186 } else { 187 addExecutionError(ExecutionErrors.UnknownInitialOfferName.name(), initialOfferName); 188 } 189 } else { 190 var sourceControl = Session.getModelController(SourceControl.class); 191 192 if(initialOfferName == null && initialUseName == null && initialSourceName != null) { 193 var source = sourceControl.getSourceByName(initialSourceName); 194 195 if(source != null) { 196 initialOfferUse = source.getLastDetail().getOfferUse(); 197 } else { 198 addExecutionError(ExecutionErrors.UnknownInitialSourceName.name(), initialSourceName); 199 } 200 } else { 201 initialOfferUse = getUserVisit().getOfferUse(); 202 203 if(initialOfferUse == null) { 204 // If all three parameters are null, then try to get the default Source and use its OfferUse. 205 var source = sourceControl.getDefaultSource(); 206 207 if(source != null) { 208 initialOfferUse = source.getLastDetail().getOfferUse(); 209 } else { 210 addExecutionError(ExecutionErrors.InvalidInitialOfferOrSourceSpecification.name()); 211 invalidInitialOfferOrSourceSpecification = true; 212 } 213 } 214 } 215 } 216 217 if(initialOfferUse != null) { 218 var partyControl = Session.getModelController(PartyControl.class); 219 var preferredLanguageIsoName = form.getPreferredLanguageIsoName(); 220 var preferredLanguage = preferredLanguageIsoName == null ? null : partyControl.getLanguageByIsoName(preferredLanguageIsoName); 221 222 if(preferredLanguageIsoName == null || (preferredLanguage != null)) { 223 var preferredJavaTimeZoneName = form.getPreferredJavaTimeZoneName(); 224 var preferredTimeZone = preferredJavaTimeZoneName == null ? null : partyControl.getTimeZoneByJavaName(preferredJavaTimeZoneName); 225 226 if(preferredJavaTimeZoneName == null || (preferredTimeZone != null)) { 227 var preferredDateTimeFormatName = form.getPreferredDateTimeFormatName(); 228 var preferredDateTimeFormat = preferredDateTimeFormatName == null ? null : partyControl.getDateTimeFormatByName(preferredDateTimeFormatName); 229 230 if(preferredDateTimeFormatName == null || (preferredDateTimeFormat != null)) { 231 var preferredCurrencyIsoName = form.getPreferredCurrencyIsoName(); 232 Currency preferredCurrency; 233 234 if(preferredCurrencyIsoName == null) { 235 preferredCurrency = null; 236 } else { 237 preferredCurrency = accountingControl.getCurrencyByIsoName(preferredCurrencyIsoName); 238 } 239 240 if(preferredCurrencyIsoName == null || (preferredCurrency != null)) { 241 var freeOnBoardControl = Session.getModelController(FreeOnBoardControl.class); 242 var partyFreeOnBoardControl = Session.getModelController(PartyFreeOnBoardControl.class); 243 var workflowControl = Session.getModelController(WorkflowControl.class); 244 var soundex = new Soundex(); 245 var partyType = partyControl.getPartyTypeByName(PartyTypes.CUSTOMER.name()); 246 BasePK createdBy = getPartyPK(); 247 var personalTitleId = form.getPersonalTitleId(); 248 var personalTitle = personalTitleId == null ? null : partyControl.convertPersonalTitleIdToEntity(personalTitleId, 249 EntityPermission.READ_ONLY); 250 var firstName = form.getFirstName(); 251 var middleName = form.getMiddleName(); 252 var lastName = form.getLastName(); 253 var nameSuffixId = form.getNameSuffixId(); 254 var nameSuffix = nameSuffixId == null ? null : partyControl.convertNameSuffixIdToEntity(nameSuffixId, 255 EntityPermission.READ_ONLY); 256 var name = form.getName(); 257 var emailAddress = form.getEmailAddress(); 258 var allowSolicitation = Boolean.valueOf(form.getAllowSolicitation()); 259 260 String firstNameSdx; 261 try { 262 firstNameSdx = firstName == null ? null : soundex.encode(firstName); 263 } catch(IllegalArgumentException iae) { 264 firstNameSdx = null; 265 } 266 267 String middleNameSdx; 268 try { 269 middleNameSdx = middleName == null ? null : soundex.encode(middleName); 270 } catch(IllegalArgumentException iae) { 271 middleNameSdx = null; 272 } 273 274 String lastNameSdx; 275 try { 276 lastNameSdx = lastName == null ? null : soundex.encode(lastName); 277 } catch(IllegalArgumentException iae) { 278 lastNameSdx = null; 279 } 280 281 var party = partyControl.createParty(null, partyType, preferredLanguage, preferredCurrency, preferredTimeZone, preferredDateTimeFormat, createdBy); 282 283 if(createdBy == null) { 284 createdBy = party.getPrimaryKey(); 285 } 286 if(personalTitle != null || firstName != null || middleName != null || lastName != null || nameSuffix != null) { 287 partyControl.createPerson(party, personalTitle, firstName, firstNameSdx, middleName, middleNameSdx, 288 lastName, lastNameSdx, nameSuffix, createdBy); 289 } 290 291 if(name != null) { 292 partyControl.createPartyGroup(party, name, createdBy); 293 } 294 295 customer = customerControl.createCustomer(party, customerType, initialOfferUse, cancellationPolicy, 296 returnPolicy, arGlAccount, customerTypeDetail.getDefaultHoldUntilComplete(), 297 customerTypeDetail.getDefaultAllowBackorders(), customerTypeDetail.getDefaultAllowSubstitutions(), 298 customerTypeDetail.getDefaultAllowCombiningShipments(), customerTypeDetail.getDefaultRequireReference(), 299 customerTypeDetail.getDefaultAllowReferenceDuplicates(), customerTypeDetail.getDefaultReferenceValidationPattern(), 300 createdBy); 301 302 if(emailAddress != null) { 303 ContactEmailAddressLogic.getInstance().createContactEmailAddress(party, 304 emailAddress, allowSolicitation, null, 305 ContactMechanismPurposes.PRIMARY_EMAIL.name(), createdBy); 306 } 307 308 termControl.createPartyTerm(party, term, customerTypeDetail.getDefaultTaxable(), createdBy); 309 310 partyFreeOnBoardControl.createPartyFreeOnBoard(party, freeOnBoardControl.getDefaultFreeOnBoard(), createdBy); 311 312 for(var customerTypeCreditLimit : termControl.getCustomerTypeCreditLimitsByCustomerType(customerType)) { 313 var currency = customerTypeCreditLimit.getCurrency(); 314 var creditLimit = customerTypeCreditLimit.getCreditLimit(); 315 var potentialCreditLimit = customerTypeCreditLimit.getPotentialCreditLimit(); 316 317 termControl.createPartyCreditLimit(party, currency, creditLimit, potentialCreditLimit, createdBy); 318 } 319 320 // TODO: error checking for unknown customerStatusChoice 321 var customerStatusChoice = form.getCustomerStatusChoice(); 322 var customerStatusWorkflow = workflowControl.getWorkflowByName(CustomerStatusConstants.Workflow_CUSTOMER_STATUS); 323 var customerStatusWorkflowEntrance = customerStatusChoice == null ? customerTypeDetail.getDefaultCustomerStatus() 324 : workflowControl.getWorkflowEntranceByName(customerStatusWorkflow, 325 customerStatusChoice); 326 327 if(customerStatusWorkflowEntrance == null) { 328 customerStatusWorkflowEntrance = workflowControl.getDefaultWorkflowEntrance(customerStatusWorkflow); 329 } 330 331 // TODO: error checking for unknown customerCreditStatusChoice 332 var customerCreditStatusChoice = form.getCustomerCreditStatusChoice(); 333 var customerCreditStatusWorkflow = workflowControl.getWorkflowByName(CustomerCreditStatusConstants.Workflow_CUSTOMER_CREDIT_STATUS); 334 var customerCreditStatusWorkflowEntrance = customerCreditStatusChoice == null ? customerTypeDetail.getDefaultCustomerCreditStatus() 335 : workflowControl.getWorkflowEntranceByName(customerCreditStatusWorkflow, customerCreditStatusChoice); 336 337 if(customerCreditStatusWorkflowEntrance == null) { 338 customerCreditStatusWorkflowEntrance = workflowControl.getDefaultWorkflowEntrance(customerCreditStatusWorkflow); 339 } 340 341 var entityInstanceControl = Session.getModelController(EntityInstanceControl.class); 342 var entityInstance = entityInstanceControl.getEntityInstanceByBasePK(party.getPrimaryKey()); 343 workflowControl.addEntityToWorkflow(customerStatusWorkflowEntrance, entityInstance, null, null, createdBy); 344 workflowControl.addEntityToWorkflow(customerCreditStatusWorkflowEntrance, entityInstance, null, null, createdBy); 345 346 ContactListLogic.getInstance().setupInitialContactLists(this, party, createdBy); 347 348 // ExecutionErrorAccumulator is passed in as null so that an Exception will be thrown if there is an error. 349 PartyChainLogic.getInstance().createPartyWelcomeChainInstance(null, party, createdBy); 350 } else { 351 addExecutionError(ExecutionErrors.UnknownCurrencyIsoName.name(), preferredCurrencyIsoName); 352 } 353 } else { 354 addExecutionError(ExecutionErrors.UnknownDateTimeFormatName.name(), preferredDateTimeFormatName); 355 } 356 } else { 357 addExecutionError(ExecutionErrors.UnknownJavaTimeZoneName.name(), preferredJavaTimeZoneName); 358 } 359 } else { 360 addExecutionError(ExecutionErrors.UnknownLanguageIsoName.name(), preferredLanguageIsoName); 361 } 362 } 363 } else { 364 addExecutionError(ExecutionErrors.UnknownDefaultTerm.name()); 365 } 366 } else { 367 addExecutionError(ExecutionErrors.InvalidGlAccountCategory.name(), glAccountCategoryName); 368 } 369 } else { 370 addExecutionError(ExecutionErrors.UnknownArGlAccountName.name(), arGlAccountName); 371 } 372 } else { 373 addExecutionError(ExecutionErrors.UnknownReturnPolicyName.name(), returnPolicyName); 374 } 375 } else { 376 addExecutionError(ExecutionErrors.UnknownCancellationPolicyName.name(), cancellationPolicyName); 377 } 378 } else { 379 if(customerTypeName != null) { 380 addExecutionError(ExecutionErrors.UnknownCustomerTypeName.name(), customerTypeName); 381 } else { 382 addExecutionError(ExecutionErrors.UnknownDefaultCustomerType.name()); 383 } 384 } 385 386 if(customer != null) { 387 var party = customer.getParty(); 388 389 result.setEntityRef(party.getPrimaryKey().getEntityRef()); 390 result.setCustomerName(customer.getCustomerName()); 391 result.setPartyName(party.getLastDetail().getPartyName()); 392 } 393 394 return result; 395 } 396 397}