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}