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.customer.server.command;
018
019import com.echothree.control.user.customer.common.edit.CustomerEdit;
020import com.echothree.control.user.customer.common.edit.CustomerEditFactory;
021import com.echothree.control.user.customer.common.form.EditCustomerForm;
022import com.echothree.control.user.customer.common.result.CustomerResultFactory;
023import com.echothree.control.user.customer.common.result.EditCustomerResult;
024import com.echothree.control.user.customer.common.spec.CustomerSpec;
025import com.echothree.model.control.accounting.common.AccountingConstants;
026import com.echothree.model.control.accounting.server.control.AccountingControl;
027import com.echothree.model.control.cancellationpolicy.common.CancellationKinds;
028import com.echothree.model.control.cancellationpolicy.server.control.CancellationPolicyControl;
029import com.echothree.model.control.customer.server.control.CustomerControl;
030import com.echothree.model.control.party.server.control.PartyControl;
031import com.echothree.model.control.returnpolicy.common.ReturnKinds;
032import com.echothree.model.control.returnpolicy.server.control.ReturnPolicyControl;
033import com.echothree.model.data.accounting.server.entity.Currency;
034import com.echothree.model.data.accounting.server.entity.GlAccount;
035import com.echothree.model.data.cancellationpolicy.server.entity.CancellationPolicy;
036import com.echothree.model.data.customer.server.entity.Customer;
037import com.echothree.model.data.customer.server.entity.CustomerType;
038import com.echothree.model.data.party.server.entity.DateTimeFormat;
039import com.echothree.model.data.party.server.entity.Language;
040import com.echothree.model.data.party.server.entity.Party;
041import com.echothree.model.data.party.server.entity.TimeZone;
042import com.echothree.model.data.returnpolicy.server.entity.ReturnPolicy;
043import com.echothree.model.data.user.common.pk.UserVisitPK;
044import com.echothree.util.common.command.EditMode;
045import com.echothree.util.common.message.ExecutionErrors;
046import com.echothree.util.common.validation.FieldDefinition;
047import com.echothree.util.common.validation.FieldType;
048import com.echothree.util.server.control.BaseAbstractEditCommand;
049import com.echothree.util.server.persistence.EntityPermission;
050import java.util.List;
051import org.apache.commons.codec.language.Soundex;
052import javax.enterprise.context.Dependent;
053import javax.inject.Inject;
054
055@Dependent
056public class EditCustomerCommand
057        extends BaseAbstractEditCommand<CustomerSpec, CustomerEdit, EditCustomerResult, Party, Party> {
058    
059    private final static List<FieldDefinition> SPEC_FIELD_DEFINITIONS;
060    private final static List<FieldDefinition> EDIT_FIELD_DEFINITIONS;
061    
062    static {
063        SPEC_FIELD_DEFINITIONS = List.of(
064                new FieldDefinition("CustomerName", FieldType.ENTITY_NAME, true, null, null)
065        );
066        
067        EDIT_FIELD_DEFINITIONS = List.of(
068                new FieldDefinition("CustomerTypeName", FieldType.ENTITY_NAME, true, null, null),
069                new FieldDefinition("CancellationPolicyName", FieldType.ENTITY_NAME, false, null, null),
070                new FieldDefinition("ReturnPolicyName", FieldType.ENTITY_NAME, false, null, null),
071                new FieldDefinition("ArGlAccountName", FieldType.ENTITY_NAME, false, null, null),
072                new FieldDefinition("PersonalTitleId", FieldType.ID, false, null, null),
073                new FieldDefinition("FirstName", FieldType.STRING, false, 1L, 20L),
074                new FieldDefinition("MiddleName", FieldType.STRING, false, 1L, 20L),
075                new FieldDefinition("LastName", FieldType.STRING, false, 1L, 20L),
076                new FieldDefinition("NameSuffixId", FieldType.ID, false, null, null),
077                new FieldDefinition("Name", FieldType.STRING, false, 1L, 60L),
078                new FieldDefinition("PreferredLanguageIsoName", FieldType.ENTITY_NAME, false, null, null),
079                new FieldDefinition("PreferredCurrencyIsoName", FieldType.ENTITY_NAME, false, null, null),
080                new FieldDefinition("PreferredJavaTimeZoneName", FieldType.TIME_ZONE_NAME, false, null, null),
081                new FieldDefinition("PreferredDateTimeFormatName", FieldType.ENTITY_NAME, false, null, null),
082                new FieldDefinition("HoldUntilComplete", FieldType.BOOLEAN, true, null, null),
083                new FieldDefinition("AllowBackorders", FieldType.BOOLEAN, true, null, null),
084                new FieldDefinition("AllowSubstitutions", FieldType.BOOLEAN, true, null, null),
085                new FieldDefinition("AllowCombiningShipments", FieldType.BOOLEAN, true, null, null),
086                new FieldDefinition("RequireReference", FieldType.BOOLEAN, true, null, null),
087                new FieldDefinition("AllowReferenceDuplicates", FieldType.BOOLEAN, true, null, null),
088                new FieldDefinition("ReferenceValidationPattern", FieldType.REGULAR_EXPRESSION, false, null, null)
089        );
090    }
091
092    @Inject
093    AccountingControl accountingControl;
094
095    @Inject
096    CancellationPolicyControl cancellationPolicyControl;
097
098    @Inject
099    CustomerControl customerControl;
100
101    @Inject
102    PartyControl partyControl;
103
104    @Inject
105    ReturnPolicyControl returnPolicyControl;
106
107    
108    /** Creates a new instance of EditCustomerCommand */
109    public EditCustomerCommand() {
110        super(null, SPEC_FIELD_DEFINITIONS, EDIT_FIELD_DEFINITIONS);
111    }
112
113    @Override
114    public EditCustomerResult getResult() {
115        return CustomerResultFactory.getEditCustomerResult();
116    }
117
118    @Override
119    public CustomerEdit getEdit() {
120        return CustomerEditFactory.getCustomerEdit();
121    }
122
123    @Override
124    public Party getEntity(EditCustomerResult result) {
125        Customer customer;
126        var customerName = spec.getCustomerName();
127
128        if(editMode.equals(EditMode.LOCK) || editMode.equals(EditMode.ABANDON)) {
129            customer = customerControl.getCustomerByName(customerName);
130        } else { // EditMode.UPDATE
131            customer = customerControl.getCustomerByNameForUpdate(customerName);
132        }
133
134        if(customer != null) {
135            result.setCustomer(customerControl.getCustomerTransfer(getUserVisit(), customer));
136        } else {
137            addExecutionError(ExecutionErrors.UnknownCustomerName.name(), customerName);
138        }
139
140        return customer.getParty();
141    }
142
143    @Override
144    public Party getLockEntity(Party party) {
145        return party;
146    }
147
148    @Override
149    public void fillInResult(EditCustomerResult result, Party party) {
150        result.setCustomer(customerControl.getCustomerTransfer(getUserVisit(), party));
151    }
152
153    @Override
154    public void doLock(CustomerEdit edit, Party party) {
155        var customer = customerControl.getCustomer(party);
156        var partyDetail = party.getLastDetail();
157        var partyGroup = partyControl.getPartyGroup(party);
158        var arGlAccount = customer.getArGlAccount();
159        var cancellationPolicy = customer.getCancellationPolicy();
160        var returnPolicy = customer.getReturnPolicy();
161        var preferredLanguage = partyDetail.getPreferredLanguage();
162        var preferredCurrency = partyDetail.getPreferredCurrency();
163        var preferredTimeZone = partyDetail.getPreferredTimeZone();
164        var dateTimeFormat = partyDetail.getPreferredDateTimeFormat();
165        var person = partyControl.getPerson(party);
166        var personalTitle = person == null ? null : person.getPersonalTitle();
167        var nameSuffix = person == null ? null : person.getNameSuffix();
168
169        edit.setCustomerTypeName(customer.getCustomerType().getLastDetail().getCustomerTypeName());
170        edit.setPersonalTitleId(personalTitle == null ? null : personalTitle.getPrimaryKey().getEntityId().toString());
171        edit.setFirstName(person == null ? null : person.getFirstName());
172        edit.setMiddleName(person == null ? null : person.getMiddleName());
173        edit.setLastName(person == null ? null : person.getLastName());
174        edit.setNameSuffixId(nameSuffix == null ? null : nameSuffix.getPrimaryKey().getEntityId().toString());
175        edit.setName(partyGroup == null ? null : partyGroup.getName());
176        edit.setPreferredLanguageIsoName(preferredLanguage == null ? null : preferredLanguage.getLanguageIsoName());
177        edit.setPreferredCurrencyIsoName(preferredCurrency == null ? null : preferredCurrency.getCurrencyIsoName());
178        edit.setPreferredJavaTimeZoneName(preferredTimeZone == null ? null : preferredTimeZone.getLastDetail().getJavaTimeZoneName());
179        edit.setPreferredDateTimeFormatName(dateTimeFormat == null ? null : dateTimeFormat.getLastDetail().getDateTimeFormatName());
180        edit.setArGlAccountName(arGlAccount == null ? null : arGlAccount.getLastDetail().getGlAccountName());
181        edit.setCancellationPolicyName(cancellationPolicy == null ? null : cancellationPolicy.getLastDetail().getCancellationPolicyName());
182        edit.setReturnPolicyName(returnPolicy == null ? null : returnPolicy.getLastDetail().getReturnPolicyName());
183        edit.setHoldUntilComplete(customer.getHoldUntilComplete().toString());
184        edit.setAllowBackorders(customer.getAllowBackorders().toString());
185        edit.setAllowSubstitutions(customer.getAllowSubstitutions().toString());
186        edit.setAllowCombiningShipments(customer.getAllowCombiningShipments().toString());
187        edit.setRequireReference(customer.getRequireReference().toString());
188        edit.setAllowReferenceDuplicates(customer.getAllowReferenceDuplicates().toString());
189        edit.setReferenceValidationPattern(customer.getReferenceValidationPattern());
190    }
191
192    CustomerType customerType;
193    CancellationPolicy cancellationPolicy;
194    ReturnPolicy returnPolicy;
195    GlAccount arGlAccount;
196    Language preferredLanguage;
197    TimeZone preferredTimeZone;
198    DateTimeFormat preferredDateTimeFormat;
199    Currency preferredCurrency;
200
201    @Override
202    public void canUpdate(Party party) {
203        var customerTypeName = edit.getCustomerTypeName();
204
205        customerType = customerControl.getCustomerTypeByName(customerTypeName);
206
207        if(customerType != null) {
208            var cancellationPolicyName = edit.getCancellationPolicyName();
209
210            if(cancellationPolicyName != null) {
211                var cancellationKind = cancellationPolicyControl.getCancellationKindByName(CancellationKinds.CUSTOMER_CANCELLATION.name());
212
213                cancellationPolicy = cancellationPolicyControl.getCancellationPolicyByName(cancellationKind, cancellationPolicyName);
214            }
215
216            if(cancellationPolicyName == null || cancellationPolicy != null) {
217                var returnPolicyName = edit.getReturnPolicyName();
218
219                if(returnPolicyName != null) {
220                    var returnKind = returnPolicyControl.getReturnKindByName(ReturnKinds.CUSTOMER_RETURN.name());
221
222                    returnPolicy = returnPolicyControl.getReturnPolicyByName(returnKind, returnPolicyName);
223                }
224
225                if(returnPolicyName == null || returnPolicy != null) {
226                    var arGlAccountName = edit.getArGlAccountName();
227
228                    arGlAccount = arGlAccountName == null ? null : accountingControl.getGlAccountByName(arGlAccountName);
229
230                    if(arGlAccountName == null || arGlAccount != null) {
231                        var glAccountCategoryName = arGlAccount == null ? null : arGlAccount.getLastDetail().getGlAccountCategory().getLastDetail().getGlAccountCategoryName();
232
233                        if(glAccountCategoryName == null || glAccountCategoryName.equals(AccountingConstants.GlAccountCategory_ACCOUNTS_RECEIVABLE)) {
234                            var preferredLanguageIsoName = edit.getPreferredLanguageIsoName();
235
236                            preferredLanguage = preferredLanguageIsoName == null ? null : partyControl.getLanguageByIsoName(preferredLanguageIsoName);
237
238                            if(preferredLanguageIsoName == null || (preferredLanguage != null)) {
239                                var preferredJavaTimeZoneName = edit.getPreferredJavaTimeZoneName();
240
241                                preferredTimeZone = preferredJavaTimeZoneName == null ? null : partyControl.getTimeZoneByJavaName(preferredJavaTimeZoneName);
242
243                                if(preferredJavaTimeZoneName == null || (preferredTimeZone != null)) {
244                                    var preferredDateTimeFormatName = edit.getPreferredDateTimeFormatName();
245
246                                    preferredDateTimeFormat = preferredDateTimeFormatName == null ? null : partyControl.getDateTimeFormatByName(preferredDateTimeFormatName);
247
248                                    if(preferredDateTimeFormatName == null || (preferredDateTimeFormat != null)) {
249                                        var preferredCurrencyIsoName = edit.getPreferredCurrencyIsoName();
250
251                                        if(preferredCurrencyIsoName == null) {
252                                            preferredCurrency = null;
253                                        } else {
254                                            preferredCurrency = accountingControl.getCurrencyByIsoName(preferredCurrencyIsoName);
255                                        }
256
257                                        if(preferredCurrencyIsoName != null && (preferredCurrency == null)) {
258                                            addExecutionError(ExecutionErrors.UnknownCurrencyIsoName.name(), preferredCurrencyIsoName);
259                                        }
260                                    } else {
261                                        addExecutionError(ExecutionErrors.UnknownDateTimeFormatName.name(), preferredDateTimeFormatName);
262                                    }
263                                } else {
264                                    addExecutionError(ExecutionErrors.UnknownJavaTimeZoneName.name(), preferredJavaTimeZoneName);
265                                }
266                            } else {
267                                addExecutionError(ExecutionErrors.UnknownLanguageIsoName.name(), preferredLanguageIsoName);
268                            }
269                        } else {
270                            addExecutionError(ExecutionErrors.InvalidGlAccountCategory.name(), glAccountCategoryName);
271                        }
272                    } else {
273                        addExecutionError(ExecutionErrors.UnknownArGlAccountName.name(), arGlAccountName);
274                    }
275                } else {
276                    addExecutionError(ExecutionErrors.UnknownReturnPolicyName.name(), returnPolicyName);
277                }
278            } else {
279                addExecutionError(ExecutionErrors.UnknownCancellationPolicyName.name(), cancellationPolicyName);
280            }
281        } else {
282            addExecutionError(ExecutionErrors.UnknownCustomerTypeName.name(), customerTypeName);
283        }
284    }
285
286    @Override
287    public void doUpdate(Party party) {
288        var soundex = new Soundex();
289        var partyDetailValue = partyControl.getPartyDetailValueForUpdate(party);
290        var customer = customerControl.getCustomerForUpdate(party);
291        var customerValue = customerControl.getCustomerValue(customer);
292        var partyGroup = partyControl.getPartyGroupForUpdate(party);
293        var person = partyControl.getPersonForUpdate(party);
294        var personalTitleId = edit.getPersonalTitleId();
295        var personalTitle = personalTitleId == null ? null : partyControl.convertPersonalTitleIdToEntity(personalTitleId, EntityPermission.READ_ONLY);
296        var firstName = edit.getFirstName();
297        var firstNameSdx = soundex.encode(firstName);
298        var middleName = edit.getMiddleName();
299        var middleNameSdx = middleName == null ? null : soundex.encode(middleName);
300        var lastName = edit.getLastName();
301        var lastNameSdx = soundex.encode(lastName);
302        var nameSuffixId = edit.getNameSuffixId();
303        var nameSuffix = nameSuffixId == null ? null : partyControl.convertNameSuffixIdToEntity(nameSuffixId, EntityPermission.READ_ONLY);
304        var name = edit.getName();
305        var updatedBy = getPartyPK();
306
307        customerValue.setCustomerTypePK(customerType.getPrimaryKey());
308        customerValue.setArGlAccountPK(arGlAccount == null ? null : arGlAccount.getPrimaryKey());
309        customerValue.setCancellationPolicyPK(cancellationPolicy == null ? null : cancellationPolicy.getPrimaryKey());
310        customerValue.setReturnPolicyPK(returnPolicy == null ? null : returnPolicy.getPrimaryKey());
311        customerValue.setHoldUntilComplete(Boolean.valueOf(edit.getHoldUntilComplete()));
312        customerValue.setAllowBackorders(Boolean.valueOf(edit.getAllowBackorders()));
313        customerValue.setAllowSubstitutions(Boolean.valueOf(edit.getAllowSubstitutions()));
314        customerValue.setAllowCombiningShipments(Boolean.valueOf(edit.getAllowCombiningShipments()));
315        customerValue.setRequireReference(Boolean.valueOf(edit.getRequireReference()));
316        customerValue.setAllowReferenceDuplicates(Boolean.valueOf(edit.getAllowReferenceDuplicates()));
317        customerValue.setReferenceValidationPattern(edit.getReferenceValidationPattern());
318
319        partyDetailValue.setPreferredLanguagePK(preferredLanguage == null ? null : preferredLanguage.getPrimaryKey());
320        partyDetailValue.setPreferredTimeZonePK(preferredTimeZone == null ? null : preferredTimeZone.getPrimaryKey());
321        partyDetailValue.setPreferredDateTimeFormatPK(preferredDateTimeFormat == null ? null : preferredDateTimeFormat.getPrimaryKey());
322        partyDetailValue.setPreferredCurrencyPK(preferredCurrency == null ? null : preferredCurrency.getPrimaryKey());
323
324        if(name != null) {
325            if(partyGroup != null) {
326                var partyGroupValue = partyControl.getPartyGroupValue(partyGroup);
327
328                partyGroupValue.setName(name);
329                partyControl.updatePartyGroupFromValue(partyGroupValue, updatedBy);
330            } else {
331                partyControl.createPartyGroup(party, name, updatedBy);
332            }
333        } else {
334            if(partyGroup != null) {
335                partyControl.deletePartyGroup(partyGroup, updatedBy);
336            }
337        }
338
339        if(personalTitle != null || firstName != null || middleName != null || lastName != null || nameSuffix != null) {
340            if(person != null) {
341                var personValue = partyControl.getPersonValue(person);
342
343                personValue.setPersonalTitlePK(personalTitle == null ? null : personalTitle.getPrimaryKey());
344                personValue.setFirstName(firstName);
345                personValue.setFirstNameSdx(firstNameSdx);
346                personValue.setMiddleName(middleName);
347                personValue.setMiddleNameSdx(middleNameSdx);
348                personValue.setLastName(lastName);
349                personValue.setLastNameSdx(lastNameSdx);
350                personValue.setNameSuffixPK(nameSuffix == null ? null : nameSuffix.getPrimaryKey());
351                partyControl.updatePersonFromValue(personValue, updatedBy);
352            } else {
353                partyControl.createPerson(party, personalTitle, firstName, firstNameSdx, middleName, middleNameSdx,
354                        lastName, lastNameSdx, nameSuffix, updatedBy);
355            }
356        } else {
357            if(person != null) {
358                partyControl.deletePerson(person, updatedBy);
359            }
360        }
361
362        customerControl.updateCustomerFromValue(customerValue, updatedBy);
363        partyControl.updatePartyFromValue(partyDetailValue, updatedBy);
364    }
365
366}