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}