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}