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.geo.server.command;
018
019import com.echothree.control.user.geo.common.edit.CountryEdit;
020import com.echothree.control.user.geo.common.edit.GeoEditFactory;
021import com.echothree.control.user.geo.common.result.EditCountryResult;
022import com.echothree.control.user.geo.common.result.GeoResultFactory;
023import com.echothree.control.user.geo.common.spec.GeoCodeSpec;
024import com.echothree.model.control.contact.server.control.ContactControl;
025import com.echothree.model.control.geo.common.GeoCodeAliasTypes;
026import com.echothree.model.control.geo.common.GeoCodeScopes;
027import com.echothree.model.control.geo.common.GeoCodeTypes;
028import com.echothree.model.control.geo.server.control.GeoControl;
029import com.echothree.model.control.geo.server.logic.GeoCodeAliasLogic;
030import com.echothree.model.control.geo.server.logic.GeoCodeLogic;
031import com.echothree.model.control.geo.server.logic.GeoCodeScopeLogic;
032import com.echothree.model.control.geo.server.logic.GeoCodeTypeLogic;
033import com.echothree.model.control.party.common.PartyTypes;
034import com.echothree.model.control.security.common.SecurityRoleGroups;
035import com.echothree.model.control.security.common.SecurityRoles;
036import com.echothree.model.data.contact.server.entity.PostalAddressFormat;
037import com.echothree.model.data.geo.server.entity.GeoCode;
038import com.echothree.util.common.command.EditMode;
039import com.echothree.util.common.message.ExecutionErrors;
040import com.echothree.util.common.validation.FieldDefinition;
041import com.echothree.util.common.validation.FieldType;
042import com.echothree.util.server.control.BaseAbstractEditCommand;
043import com.echothree.util.server.control.CommandSecurityDefinition;
044import com.echothree.util.server.control.PartyTypeDefinition;
045import com.echothree.util.server.control.SecurityRoleDefinition;
046import java.util.List;
047import javax.enterprise.context.Dependent;
048import javax.inject.Inject;
049
050@Dependent
051public class EditCountryCommand
052        extends BaseAbstractEditCommand<GeoCodeSpec, CountryEdit, EditCountryResult, GeoCode, GeoCode> {
053
054    private final static CommandSecurityDefinition COMMAND_SECURITY_DEFINITION;
055    private final static List<FieldDefinition> SPEC_FIELD_DEFINITIONS;
056    private final static List<FieldDefinition> EDIT_FIELD_DEFINITIONS;
057
058    static {
059        COMMAND_SECURITY_DEFINITION = new CommandSecurityDefinition(List.of(
060                new PartyTypeDefinition(PartyTypes.UTILITY.name(), null),
061                new PartyTypeDefinition(PartyTypes.EMPLOYEE.name(), List.of(
062                        new SecurityRoleDefinition(SecurityRoleGroups.Country.name(), SecurityRoles.Edit.name())
063                ))
064        ));
065
066        SPEC_FIELD_DEFINITIONS = List.of(
067                new FieldDefinition("GeoCodeName", FieldType.ENTITY_NAME, true, null, null)
068        );
069
070        EDIT_FIELD_DEFINITIONS = List.of(
071                new FieldDefinition("CountryName", FieldType.ENTITY_NAME, true, null, null),
072                new FieldDefinition("Iso3Number", FieldType.NUMBER_3, true, null, null),
073                new FieldDefinition("Iso3Letter", FieldType.UPPER_LETTER_3, true, null, null),
074                new FieldDefinition("Iso2Letter", FieldType.UPPER_LETTER_2, true, null, null),
075                new FieldDefinition("TelephoneCode", FieldType.STRING, false, 1L, 5L),
076                new FieldDefinition("AreaCodePattern", FieldType.REGULAR_EXPRESSION, false, null, null),
077                new FieldDefinition("AreaCodeRequired", FieldType.BOOLEAN, true, null, null),
078                new FieldDefinition("AreaCodeExample", FieldType.STRING, false, 1L, 5L),
079                new FieldDefinition("TelephoneNumberPattern", FieldType.REGULAR_EXPRESSION, false, null, null),
080                new FieldDefinition("TelephoneNumberExample", FieldType.STRING, false, 1L, 25L),
081                new FieldDefinition("PostalAddressFormatName", FieldType.ENTITY_NAME, true, null, null),
082                new FieldDefinition("CityRequired", FieldType.BOOLEAN, true, null, null),
083                new FieldDefinition("CityGeoCodeRequired", FieldType.BOOLEAN, true, null, null),
084                new FieldDefinition("StateRequired", FieldType.BOOLEAN, true, null, null),
085                new FieldDefinition("StateGeoCodeRequired", FieldType.BOOLEAN, true, null, null),
086                new FieldDefinition("PostalCodePattern", FieldType.REGULAR_EXPRESSION, false, null, null),
087                new FieldDefinition("PostalCodeRequired", FieldType.BOOLEAN, true, null, null),
088                new FieldDefinition("PostalCodeGeoCodeRequired", FieldType.BOOLEAN, true, null, null),
089                new FieldDefinition("PostalCodeLength", FieldType.UNSIGNED_INTEGER, false, null, null),
090                new FieldDefinition("PostalCodeGeoCodeLength", FieldType.UNSIGNED_INTEGER, false, null, null),
091                new FieldDefinition("PostalCodeExample", FieldType.STRING, false, 1L, 15L),
092                new FieldDefinition("IsDefault", FieldType.BOOLEAN, true, null, null),
093                new FieldDefinition("SortOrder", FieldType.SIGNED_INTEGER, true, null, null),
094                new FieldDefinition("Description", FieldType.STRING, false, 1L, 132L)
095        );
096    }
097
098    @Inject
099    ContactControl contactControl;
100
101    @Inject
102    GeoControl geoControl;
103
104    @Inject
105    GeoCodeAliasLogic geoCodeAliasLogic;
106
107    @Inject
108    GeoCodeLogic geoCodeLogic;
109
110    @Inject
111    GeoCodeScopeLogic geoCodeScopeLogic;
112
113    @Inject
114    GeoCodeTypeLogic geoCodeTypeLogic;
115
116    /** Creates a new instance of EditCountryCommand */
117    public EditCountryCommand() {
118        super(COMMAND_SECURITY_DEFINITION, SPEC_FIELD_DEFINITIONS, EDIT_FIELD_DEFINITIONS);
119    }
120
121    @Override
122    public EditCountryResult getResult() {
123        return GeoResultFactory.getEditCountryResult();
124    }
125
126    @Override
127    public CountryEdit getEdit() {
128        return GeoEditFactory.getCountryEdit();
129    }
130
131    @Override
132    public GeoCode getEntity(EditCountryResult result) {
133        GeoCode geoCode;
134        var geoCodeName = spec.getGeoCodeName();
135
136        if(editMode.equals(EditMode.LOCK) || editMode.equals(EditMode.ABANDON)) {
137            geoCode = geoControl.getGeoCodeByName(geoCodeName);
138        } else { // EditMode.UPDATE
139            geoCode = geoControl.getGeoCodeByNameForUpdate(geoCodeName);
140        }
141
142        if(geoCode != null) {
143            var geoCodeTypeName = geoCode.getLastDetail().getGeoCodeType().getLastDetail().getGeoCodeTypeName();
144            
145            if(!geoCodeTypeName.equals(GeoCodeTypes.COUNTRY.name())) {
146                addExecutionError(ExecutionErrors.InvalidGeoCodeType.name(), geoCodeTypeName);
147            }
148        } else {
149            addExecutionError(ExecutionErrors.UnknownGeoCodeName.name(), geoCodeName);
150        }
151
152        return geoCode;
153    }
154
155    @Override
156    public GeoCode getLockEntity(GeoCode geoCode) {
157        return geoCode;
158    }
159
160    @Override
161    public void fillInResult(EditCountryResult result, GeoCode geoCode) {
162        result.setCountry(geoControl.getCountryTransfer(getUserVisit(), geoCode));
163    }
164
165    @Override
166    public void doLock(CountryEdit edit, GeoCode geoCode) {
167        var geoCodeDescription = geoControl.getGeoCodeDescription(geoCode, getPreferredLanguage());
168        var geoCodeDetail = geoCode.getLastDetail();
169        var geoCodeCountry = geoControl.getGeoCodeCountry(geoCode);
170        var postalCodeLength = geoCodeCountry.getPostalCodeGeoCodeLength();
171        var postalCodeGeoCodeLength = geoCodeCountry.getPostalCodeGeoCodeLength();
172
173        var geoCodeAlias = geoCodeAliasLogic.getGeoCodeAliasUsingNames(null, geoCode, GeoCodeAliasTypes.COUNTRY_NAME.name());
174        edit.setCountryName(geoCodeAlias.getAlias());
175
176        geoCodeAlias = geoCodeAliasLogic.getGeoCodeAliasUsingNames(null, geoCode, GeoCodeAliasTypes.ISO_3_NUMBER.name());
177        edit.setIso3Number(geoCodeAlias.getAlias());
178
179        geoCodeAlias = geoCodeAliasLogic.getGeoCodeAliasUsingNames(null, geoCode, GeoCodeAliasTypes.ISO_3_LETTER.name());
180        edit.setIso3Letter(geoCodeAlias.getAlias());
181
182        geoCodeAlias = geoCodeAliasLogic.getGeoCodeAliasUsingNames(null, geoCode, GeoCodeAliasTypes.ISO_2_LETTER.name());
183        edit.setIso2Letter(geoCodeAlias.getAlias());
184
185        edit.setTelephoneCode(geoCodeCountry.getTelephoneCode());
186        edit.setAreaCodePattern(geoCodeCountry.getAreaCodePattern());
187        edit.setAreaCodeRequired(geoCodeCountry.getAreaCodeRequired().toString());
188        edit.setAreaCodeExample(geoCodeCountry.getAreaCodeExample());
189        edit.setTelephoneNumberPattern(geoCodeCountry.getTelephoneNumberPattern());
190        edit.setTelephoneNumberExample(geoCodeCountry.getTelephoneNumberExample());
191        edit.setPostalAddressFormatName(geoCodeCountry.getPostalAddressFormat().getLastDetail().getPostalAddressFormatName());
192        edit.setCityRequired(geoCodeCountry.getCityRequired().toString());
193        edit.setCityGeoCodeRequired(geoCodeCountry.getCityGeoCodeRequired().toString());
194        edit.setStateRequired(geoCodeCountry.getStateRequired().toString());
195        edit.setStateGeoCodeRequired(geoCodeCountry.getStateGeoCodeRequired().toString());
196        edit.setPostalCodePattern(geoCodeCountry.getPostalCodePattern());
197        edit.setPostalCodeRequired(geoCodeCountry.getPostalCodeRequired().toString());
198        edit.setPostalCodeGeoCodeRequired(geoCodeCountry.getPostalCodeGeoCodeRequired().toString());
199        edit.setPostalCodeLength(postalCodeLength == null ? null : postalCodeLength.toString());
200        edit.setPostalCodeGeoCodeLength(postalCodeGeoCodeLength == null ? null : postalCodeGeoCodeLength.toString());
201        edit.setPostalCodeExample(geoCodeCountry.getPostalCodeExample());
202        edit.setIsDefault(geoCodeDetail.getIsDefault().toString());
203        edit.setSortOrder(geoCodeDetail.getSortOrder().toString());
204
205        if(geoCodeDescription != null) {
206            edit.setDescription(geoCodeDescription.getDescription());
207        }
208    }
209
210    PostalAddressFormat postalAddressFormat;
211
212    @Override
213    public void canUpdate(GeoCode geoCode) {
214        var geoCodeType = geoCodeTypeLogic.getGeoCodeTypeByName(this, GeoCodeTypes.COUNTRY.name());
215
216        if(!hasExecutionErrors()) {
217            var geoCodeScope = geoCodeScopeLogic.getGeoCodeScopeByName(this, GeoCodeScopes.COUNTRIES.name());
218
219            if(!hasExecutionErrors()) {
220                var iso3Number = edit.getIso3Number();
221                var duplicateGeoCode = geoCodeLogic.getGeoCodeByAlias(this, geoCodeType, geoCodeScope, GeoCodeAliasTypes.ISO_3_NUMBER.name(), iso3Number);
222
223                if((duplicateGeoCode == null || duplicateGeoCode.equals(geoCode)) && !hasExecutionErrors()) {
224                    var iso3Letter = edit.getIso3Letter();
225
226                    duplicateGeoCode = geoCodeLogic.getGeoCodeByAlias(this, geoCodeType, geoCodeScope, GeoCodeAliasTypes.ISO_3_LETTER.name(), iso3Letter);
227
228                    if((duplicateGeoCode == null || duplicateGeoCode.equals(geoCode)) && !hasExecutionErrors()) {
229                        var iso2Letter = edit.getIso2Letter();
230
231                        duplicateGeoCode = geoCodeLogic.getGeoCodeByAlias(this, geoCodeType, geoCodeScope, GeoCodeAliasTypes.ISO_2_LETTER.name(), iso2Letter);
232
233                        if((duplicateGeoCode == null || duplicateGeoCode.equals(geoCode)) && !hasExecutionErrors()) {
234                            var countryName = edit.getCountryName();
235
236                            duplicateGeoCode = geoCodeLogic.getGeoCodeByAlias(this, geoCodeType, geoCodeScope, GeoCodeAliasTypes.COUNTRY_NAME.name(), countryName);
237
238                            if((duplicateGeoCode == null || duplicateGeoCode.equals(geoCode)) && !hasExecutionErrors()) {
239                                var postalAddressFormatName = edit.getPostalAddressFormatName();
240
241                                postalAddressFormat = contactControl.getPostalAddressFormatByName(postalAddressFormatName);
242
243                                if(postalAddressFormat == null) {
244                                    addExecutionError(ExecutionErrors.UnknownPostalAddressFormatName.name(), postalAddressFormatName);
245                                }
246                            } else {
247                                if(geoCode != null) {
248                                    addExecutionError(ExecutionErrors.DuplicateCountryName.name(), countryName);
249                                }
250                            }
251                        } else {
252                            if(geoCode != null) {
253                                addExecutionError(ExecutionErrors.DuplicateIso2Letter.name(), iso2Letter);
254                            }
255                        }
256                    } else {
257                        if(geoCode != null) {
258                            addExecutionError(ExecutionErrors.DuplicateIso3Letter.name(), iso3Letter);
259                        }
260                    }
261                } else {
262                    if(geoCode != null) {
263                        addExecutionError(ExecutionErrors.DuplicateIso3Number.name(), iso3Number);
264                    }
265                }
266            }
267        }
268    }
269
270    @Override
271    public void doUpdate(GeoCode geoCode) {
272        var partyPK = getPartyPK();
273        var geoCodeDetailValue = geoControl.getGeoCodeDetailValueForUpdate(geoCode);
274        var geoCodeDescription = geoControl.getGeoCodeDescriptionForUpdate(geoCode, getPreferredLanguage());
275        var geoCodeCountryValue = geoControl.getGeoCodeCountryValueForUpdate(geoCode);
276        var strPostalCodeLength = edit.getPostalCodeLength();
277        var postalCodeLength = strPostalCodeLength == null ? null : Integer.valueOf(strPostalCodeLength);
278        var strPostalCodeGeoCodeLength = edit.getPostalCodeGeoCodeLength();
279        var postalCodeGeoCodeLength = strPostalCodeGeoCodeLength == null ? null : Integer.valueOf(strPostalCodeGeoCodeLength);
280        var description = edit.getDescription();
281
282        var geoCodeAliasValue = geoCodeAliasLogic.getGeoCodeAliasValueUsingNames(null, geoCode, GeoCodeAliasTypes.COUNTRY_NAME.name());
283        geoCodeAliasValue.setAlias(edit.getCountryName());
284        geoControl.updateGeoCodeAliasFromValue(geoCodeAliasValue, partyPK);
285
286        geoCodeAliasValue = geoCodeAliasLogic.getGeoCodeAliasValueUsingNames(null, geoCode, GeoCodeAliasTypes.ISO_3_NUMBER.name());
287        geoCodeAliasValue.setAlias(edit.getIso3Number());
288        geoControl.updateGeoCodeAliasFromValue(geoCodeAliasValue, partyPK);
289
290        geoCodeAliasValue = geoCodeAliasLogic.getGeoCodeAliasValueUsingNames(null, geoCode, GeoCodeAliasTypes.ISO_3_LETTER.name());
291        geoCodeAliasValue.setAlias(edit.getIso3Letter());
292        geoControl.updateGeoCodeAliasFromValue(geoCodeAliasValue, partyPK);
293
294        geoCodeAliasValue = geoCodeAliasLogic.getGeoCodeAliasValueUsingNames(null, geoCode, GeoCodeAliasTypes.ISO_2_LETTER.name());
295        geoCodeAliasValue.setAlias(edit.getIso2Letter());
296        geoControl.updateGeoCodeAliasFromValue(geoCodeAliasValue, partyPK);
297
298        geoCodeCountryValue.setTelephoneCode(edit.getTelephoneCode());
299        geoCodeCountryValue.setAreaCodePattern(edit.getAreaCodePattern());
300        geoCodeCountryValue.setAreaCodeRequired(Boolean.valueOf(edit.getAreaCodeRequired()));
301        geoCodeCountryValue.setAreaCodeExample(edit.getAreaCodeExample());
302        geoCodeCountryValue.setTelephoneNumberPattern(edit.getTelephoneNumberPattern());
303        geoCodeCountryValue.setTelephoneNumberExample(edit.getTelephoneNumberExample());
304        geoCodeCountryValue.setPostalAddressFormatPK(postalAddressFormat.getPrimaryKey());
305        geoCodeCountryValue.setCityRequired(Boolean.valueOf(edit.getCityRequired()));
306        geoCodeCountryValue.setCityGeoCodeRequired(Boolean.valueOf(edit.getCityGeoCodeRequired()));
307        geoCodeCountryValue.setStateRequired(Boolean.valueOf(edit.getStateRequired()));
308        geoCodeCountryValue.setStateGeoCodeRequired(Boolean.valueOf(edit.getStateGeoCodeRequired()));
309        geoCodeCountryValue.setPostalCodePattern(edit.getPostalCodePattern());
310        geoCodeCountryValue.setPostalCodeRequired(Boolean.valueOf(edit.getPostalCodeRequired()));
311        geoCodeCountryValue.setPostalCodeGeoCodeRequired(Boolean.valueOf(edit.getPostalCodeGeoCodeRequired()));
312        geoCodeCountryValue.setPostalCodeLength(postalCodeLength);
313        geoCodeCountryValue.setPostalCodeGeoCodeLength(postalCodeGeoCodeLength);
314        geoCodeCountryValue.setPostalCodeExample(edit.getPostalCodeExample());
315        
316        geoCodeDetailValue.setIsDefault(Boolean.valueOf(edit.getIsDefault()));
317        geoCodeDetailValue.setSortOrder(Integer.valueOf(edit.getSortOrder()));
318
319        geoControl.updateGeoCodeFromValue(geoCodeDetailValue, partyPK);
320        geoControl.updateGeoCodeCountryFromValue(geoCodeCountryValue, partyPK);
321
322        if(geoCodeDescription == null && description != null) {
323            geoControl.createGeoCodeDescription(geoCode, getPreferredLanguage(), description, partyPK);
324        } else {
325            if(geoCodeDescription != null && description == null) {
326                geoControl.deleteGeoCodeDescription(geoCodeDescription, partyPK);
327            } else {
328                if(geoCodeDescription != null && description != null) {
329                    var geoCodeDescriptionValue = geoControl.getGeoCodeDescriptionValue(geoCodeDescription);
330
331                    geoCodeDescriptionValue.setDescription(description);
332                    geoControl.updateGeoCodeDescriptionFromValue(geoCodeDescriptionValue, partyPK);
333                }
334            }
335        }
336    }
337
338}