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 com.echothree.util.server.persistence.Session;
051import java.util.List;
052import org.apache.commons.codec.language.Soundex;
053import javax.enterprise.context.Dependent;
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    /** Creates a new instance of EditCustomerCommand */
093    public EditCustomerCommand() {
094        super(null, SPEC_FIELD_DEFINITIONS, EDIT_FIELD_DEFINITIONS);
095    }
096
097    @Override
098    public EditCustomerResult getResult() {
099        return CustomerResultFactory.getEditCustomerResult();
100    }
101
102    @Override
103    public CustomerEdit getEdit() {
104        return CustomerEditFactory.getCustomerEdit();
105    }
106
107    @Override
108    public Party getEntity(EditCustomerResult result) {
109        var customerControl = Session.getModelController(CustomerControl.class);
110        Customer customer;
111        var customerName = spec.getCustomerName();
112
113        if(editMode.equals(EditMode.LOCK) || editMode.equals(EditMode.ABANDON)) {
114            customer = customerControl.getCustomerByName(customerName);
115        } else { // EditMode.UPDATE
116            customer = customerControl.getCustomerByNameForUpdate(customerName);
117        }
118
119        if(customer != null) {
120            result.setCustomer(customerControl.getCustomerTransfer(getUserVisit(), customer));
121        } else {
122            addExecutionError(ExecutionErrors.UnknownCustomerName.name(), customerName);
123        }
124
125        return customer.getParty();
126    }
127
128    @Override
129    public Party getLockEntity(Party party) {
130        return party;
131    }
132
133    @Override
134    public void fillInResult(EditCustomerResult result, Party party) {
135        var customerControl = Session.getModelController(CustomerControl.class);
136
137        result.setCustomer(customerControl.getCustomerTransfer(getUserVisit(), party));
138    }
139
140    @Override
141    public void doLock(CustomerEdit edit, Party party) {
142        var customerControl = Session.getModelController(CustomerControl.class);
143        var partyControl = Session.getModelController(PartyControl.class);
144        var customer = customerControl.getCustomer(party);
145        var partyDetail = party.getLastDetail();
146        var partyGroup = partyControl.getPartyGroup(party);
147        var arGlAccount = customer.getArGlAccount();
148        var cancellationPolicy = customer.getCancellationPolicy();
149        var returnPolicy = customer.getReturnPolicy();
150        var preferredLanguage = partyDetail.getPreferredLanguage();
151        var preferredCurrency = partyDetail.getPreferredCurrency();
152        var preferredTimeZone = partyDetail.getPreferredTimeZone();
153        var dateTimeFormat = partyDetail.getPreferredDateTimeFormat();
154        var person = partyControl.getPerson(party);
155        var personalTitle = person == null ? null : person.getPersonalTitle();
156        var nameSuffix = person == null ? null : person.getNameSuffix();
157
158        edit.setCustomerTypeName(customer.getCustomerType().getLastDetail().getCustomerTypeName());
159        edit.setPersonalTitleId(personalTitle == null ? null : personalTitle.getPrimaryKey().getEntityId().toString());
160        edit.setFirstName(person == null ? null : person.getFirstName());
161        edit.setMiddleName(person == null ? null : person.getMiddleName());
162        edit.setLastName(person == null ? null : person.getLastName());
163        edit.setNameSuffixId(nameSuffix == null ? null : nameSuffix.getPrimaryKey().getEntityId().toString());
164        edit.setName(partyGroup == null ? null : partyGroup.getName());
165        edit.setPreferredLanguageIsoName(preferredLanguage == null ? null : preferredLanguage.getLanguageIsoName());
166        edit.setPreferredCurrencyIsoName(preferredCurrency == null ? null : preferredCurrency.getCurrencyIsoName());
167        edit.setPreferredJavaTimeZoneName(preferredTimeZone == null ? null : preferredTimeZone.getLastDetail().getJavaTimeZoneName());
168        edit.setPreferredDateTimeFormatName(dateTimeFormat == null ? null : dateTimeFormat.getLastDetail().getDateTimeFormatName());
169        edit.setArGlAccountName(arGlAccount == null ? null : arGlAccount.getLastDetail().getGlAccountName());
170        edit.setCancellationPolicyName(cancellationPolicy == null ? null : cancellationPolicy.getLastDetail().getCancellationPolicyName());
171        edit.setReturnPolicyName(returnPolicy == null ? null : returnPolicy.getLastDetail().getReturnPolicyName());
172        edit.setHoldUntilComplete(customer.getHoldUntilComplete().toString());
173        edit.setAllowBackorders(customer.getAllowBackorders().toString());
174        edit.setAllowSubstitutions(customer.getAllowSubstitutions().toString());
175        edit.setAllowCombiningShipments(customer.getAllowCombiningShipments().toString());
176        edit.setRequireReference(customer.getRequireReference().toString());
177        edit.setAllowReferenceDuplicates(customer.getAllowReferenceDuplicates().toString());
178        edit.setReferenceValidationPattern(customer.getReferenceValidationPattern());
179    }
180
181    CustomerType customerType;
182    CancellationPolicy cancellationPolicy;
183    ReturnPolicy returnPolicy;
184    GlAccount arGlAccount;
185    Language preferredLanguage;
186    TimeZone preferredTimeZone;
187    DateTimeFormat preferredDateTimeFormat;
188    Currency preferredCurrency;
189
190    @Override
191    public void canUpdate(Party party) {
192        var customerControl = Session.getModelController(CustomerControl.class);
193        var partyControl = Session.getModelController(PartyControl.class);
194        var customerTypeName = edit.getCustomerTypeName();
195
196        customerType = customerControl.getCustomerTypeByName(customerTypeName);
197
198        if(customerType != null) {
199            var cancellationPolicyName = edit.getCancellationPolicyName();
200
201            if(cancellationPolicyName != null) {
202                var cancellationPolicyControl = Session.getModelController(CancellationPolicyControl.class);
203                var cancellationKind = cancellationPolicyControl.getCancellationKindByName(CancellationKinds.CUSTOMER_CANCELLATION.name());
204
205                cancellationPolicy = cancellationPolicyControl.getCancellationPolicyByName(cancellationKind, cancellationPolicyName);
206            }
207
208            if(cancellationPolicyName == null || cancellationPolicy != null) {
209                var returnPolicyName = edit.getReturnPolicyName();
210
211                if(returnPolicyName != null) {
212                    var returnPolicyControl = Session.getModelController(ReturnPolicyControl.class);
213                    var returnKind = returnPolicyControl.getReturnKindByName(ReturnKinds.CUSTOMER_RETURN.name());
214
215                    returnPolicy = returnPolicyControl.getReturnPolicyByName(returnKind, returnPolicyName);
216                }
217
218                if(returnPolicyName == null || returnPolicy != null) {
219                    var accountingControl = Session.getModelController(AccountingControl.class);
220                    var arGlAccountName = edit.getArGlAccountName();
221
222                    arGlAccount = arGlAccountName == null ? null : accountingControl.getGlAccountByName(arGlAccountName);
223
224                    if(arGlAccountName == null || arGlAccount != null) {
225                        var glAccountCategoryName = arGlAccount == null ? null : arGlAccount.getLastDetail().getGlAccountCategory().getLastDetail().getGlAccountCategoryName();
226
227                        if(glAccountCategoryName == null || glAccountCategoryName.equals(AccountingConstants.GlAccountCategory_ACCOUNTS_RECEIVABLE)) {
228                            var preferredLanguageIsoName = edit.getPreferredLanguageIsoName();
229
230                            preferredLanguage = preferredLanguageIsoName == null ? null : partyControl.getLanguageByIsoName(preferredLanguageIsoName);
231
232                            if(preferredLanguageIsoName == null || (preferredLanguage != null)) {
233                                var preferredJavaTimeZoneName = edit.getPreferredJavaTimeZoneName();
234
235                                preferredTimeZone = preferredJavaTimeZoneName == null ? null : partyControl.getTimeZoneByJavaName(preferredJavaTimeZoneName);
236
237                                if(preferredJavaTimeZoneName == null || (preferredTimeZone != null)) {
238                                    var preferredDateTimeFormatName = edit.getPreferredDateTimeFormatName();
239
240                                    preferredDateTimeFormat = preferredDateTimeFormatName == null ? null : partyControl.getDateTimeFormatByName(preferredDateTimeFormatName);
241
242                                    if(preferredDateTimeFormatName == null || (preferredDateTimeFormat != null)) {
243                                        var preferredCurrencyIsoName = edit.getPreferredCurrencyIsoName();
244
245                                        if(preferredCurrencyIsoName == null) {
246                                            preferredCurrency = null;
247                                        } else {
248                                            preferredCurrency = accountingControl.getCurrencyByIsoName(preferredCurrencyIsoName);
249                                        }
250
251                                        if(preferredCurrencyIsoName != null && (preferredCurrency == null)) {
252                                            addExecutionError(ExecutionErrors.UnknownCurrencyIsoName.name(), preferredCurrencyIsoName);
253                                        }
254                                    } else {
255                                        addExecutionError(ExecutionErrors.UnknownDateTimeFormatName.name(), preferredDateTimeFormatName);
256                                    }
257                                } else {
258                                    addExecutionError(ExecutionErrors.UnknownJavaTimeZoneName.name(), preferredJavaTimeZoneName);
259                                }
260                            } else {
261                                addExecutionError(ExecutionErrors.UnknownLanguageIsoName.name(), preferredLanguageIsoName);
262                            }
263                        } else {
264                            addExecutionError(ExecutionErrors.InvalidGlAccountCategory.name(), glAccountCategoryName);
265                        }
266                    } else {
267                        addExecutionError(ExecutionErrors.UnknownArGlAccountName.name(), arGlAccountName);
268                    }
269                } else {
270                    addExecutionError(ExecutionErrors.UnknownReturnPolicyName.name(), returnPolicyName);
271                }
272            } else {
273                addExecutionError(ExecutionErrors.UnknownCancellationPolicyName.name(), cancellationPolicyName);
274            }
275        } else {
276            addExecutionError(ExecutionErrors.UnknownCustomerTypeName.name(), customerTypeName);
277        }
278    }
279
280    @Override
281    public void doUpdate(Party party) {
282        var customerControl = Session.getModelController(CustomerControl.class);
283        var partyControl = Session.getModelController(PartyControl.class);
284        var soundex = new Soundex();
285        var partyDetailValue = partyControl.getPartyDetailValueForUpdate(party);
286        var customer = customerControl.getCustomerForUpdate(party);
287        var customerValue = customerControl.getCustomerValue(customer);
288        var partyGroup = partyControl.getPartyGroupForUpdate(party);
289        var person = partyControl.getPersonForUpdate(party);
290        var personalTitleId = edit.getPersonalTitleId();
291        var personalTitle = personalTitleId == null ? null : partyControl.convertPersonalTitleIdToEntity(personalTitleId, EntityPermission.READ_ONLY);
292        var firstName = edit.getFirstName();
293        var firstNameSdx = soundex.encode(firstName);
294        var middleName = edit.getMiddleName();
295        var middleNameSdx = middleName == null ? null : soundex.encode(middleName);
296        var lastName = edit.getLastName();
297        var lastNameSdx = soundex.encode(lastName);
298        var nameSuffixId = edit.getNameSuffixId();
299        var nameSuffix = nameSuffixId == null ? null : partyControl.convertNameSuffixIdToEntity(nameSuffixId, EntityPermission.READ_ONLY);
300        var name = edit.getName();
301        var updatedBy = getPartyPK();
302
303        customerValue.setCustomerTypePK(customerType.getPrimaryKey());
304        customerValue.setArGlAccountPK(arGlAccount == null ? null : arGlAccount.getPrimaryKey());
305        customerValue.setCancellationPolicyPK(cancellationPolicy == null ? null : cancellationPolicy.getPrimaryKey());
306        customerValue.setReturnPolicyPK(returnPolicy == null ? null : returnPolicy.getPrimaryKey());
307        customerValue.setHoldUntilComplete(Boolean.valueOf(edit.getHoldUntilComplete()));
308        customerValue.setAllowBackorders(Boolean.valueOf(edit.getAllowBackorders()));
309        customerValue.setAllowSubstitutions(Boolean.valueOf(edit.getAllowSubstitutions()));
310        customerValue.setAllowCombiningShipments(Boolean.valueOf(edit.getAllowCombiningShipments()));
311        customerValue.setRequireReference(Boolean.valueOf(edit.getRequireReference()));
312        customerValue.setAllowReferenceDuplicates(Boolean.valueOf(edit.getAllowReferenceDuplicates()));
313        customerValue.setReferenceValidationPattern(edit.getReferenceValidationPattern());
314
315        partyDetailValue.setPreferredLanguagePK(preferredLanguage == null ? null : preferredLanguage.getPrimaryKey());
316        partyDetailValue.setPreferredTimeZonePK(preferredTimeZone == null ? null : preferredTimeZone.getPrimaryKey());
317        partyDetailValue.setPreferredDateTimeFormatPK(preferredDateTimeFormat == null ? null : preferredDateTimeFormat.getPrimaryKey());
318        partyDetailValue.setPreferredCurrencyPK(preferredCurrency == null ? null : preferredCurrency.getPrimaryKey());
319
320        if(name != null) {
321            if(partyGroup != null) {
322                var partyGroupValue = partyControl.getPartyGroupValue(partyGroup);
323
324                partyGroupValue.setName(name);
325                partyControl.updatePartyGroupFromValue(partyGroupValue, updatedBy);
326            } else {
327                partyControl.createPartyGroup(party, name, updatedBy);
328            }
329        } else {
330            if(partyGroup != null) {
331                partyControl.deletePartyGroup(partyGroup, updatedBy);
332            }
333        }
334
335        if(personalTitle != null || firstName != null || middleName != null || lastName != null || nameSuffix != null) {
336            if(person != null) {
337                var personValue = partyControl.getPersonValue(person);
338
339                personValue.setPersonalTitlePK(personalTitle == null ? null : personalTitle.getPrimaryKey());
340                personValue.setFirstName(firstName);
341                personValue.setFirstNameSdx(firstNameSdx);
342                personValue.setMiddleName(middleName);
343                personValue.setMiddleNameSdx(middleNameSdx);
344                personValue.setLastName(lastName);
345                personValue.setLastNameSdx(lastNameSdx);
346                personValue.setNameSuffixPK(nameSuffix == null ? null : nameSuffix.getPrimaryKey());
347                partyControl.updatePersonFromValue(personValue, updatedBy);
348            } else {
349                partyControl.createPerson(party, personalTitle, firstName, firstNameSdx, middleName, middleNameSdx,
350                        lastName, lastNameSdx, nameSuffix, updatedBy);
351            }
352        } else {
353            if(person != null) {
354                partyControl.deletePerson(person, updatedBy);
355            }
356        }
357
358        customerControl.updateCustomerFromValue(customerValue, updatedBy);
359        partyControl.updatePartyFromValue(partyDetailValue, updatedBy);
360    }
361
362}