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