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