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 com.echothree.util.server.persistence.Session;
047import java.util.List;
048import javax.enterprise.context.Dependent;
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    /** Creates a new instance of EditCountryCommand */
099    public EditCountryCommand() {
100        super(COMMAND_SECURITY_DEFINITION, SPEC_FIELD_DEFINITIONS, EDIT_FIELD_DEFINITIONS);
101    }
102
103    @Override
104    public EditCountryResult getResult() {
105        return GeoResultFactory.getEditCountryResult();
106    }
107
108    @Override
109    public CountryEdit getEdit() {
110        return GeoEditFactory.getCountryEdit();
111    }
112
113    @Override
114    public GeoCode getEntity(EditCountryResult result) {
115        var geoControl = Session.getModelController(GeoControl.class);
116        GeoCode geoCode;
117        var geoCodeName = spec.getGeoCodeName();
118
119        if(editMode.equals(EditMode.LOCK) || editMode.equals(EditMode.ABANDON)) {
120            geoCode = geoControl.getGeoCodeByName(geoCodeName);
121        } else { // EditMode.UPDATE
122            geoCode = geoControl.getGeoCodeByNameForUpdate(geoCodeName);
123        }
124
125        if(geoCode != null) {
126            var geoCodeTypeName = geoCode.getLastDetail().getGeoCodeType().getLastDetail().getGeoCodeTypeName();
127            
128            if(!geoCodeTypeName.equals(GeoCodeTypes.COUNTRY.name())) {
129                addExecutionError(ExecutionErrors.InvalidGeoCodeType.name(), geoCodeTypeName);
130            }
131        } else {
132            addExecutionError(ExecutionErrors.UnknownGeoCodeName.name(), geoCodeName);
133        }
134
135        return geoCode;
136    }
137
138    @Override
139    public GeoCode getLockEntity(GeoCode geoCode) {
140        return geoCode;
141    }
142
143    @Override
144    public void fillInResult(EditCountryResult result, GeoCode geoCode) {
145        var geoControl = Session.getModelController(GeoControl.class);
146
147        result.setCountry(geoControl.getCountryTransfer(getUserVisit(), geoCode));
148    }
149
150    @Override
151    public void doLock(CountryEdit edit, GeoCode geoCode) {
152        var geoControl = Session.getModelController(GeoControl.class);
153        var geoCodeAliasLogic = GeoCodeAliasLogic.getInstance();
154        var geoCodeDescription = geoControl.getGeoCodeDescription(geoCode, getPreferredLanguage());
155        var geoCodeDetail = geoCode.getLastDetail();
156        var geoCodeCountry = geoControl.getGeoCodeCountry(geoCode);
157        var postalCodeLength = geoCodeCountry.getPostalCodeGeoCodeLength();
158        var postalCodeGeoCodeLength = geoCodeCountry.getPostalCodeGeoCodeLength();
159
160        var geoCodeAlias = geoCodeAliasLogic.getGeoCodeAliasUsingNames(null, geoCode, GeoCodeAliasTypes.COUNTRY_NAME.name());
161        edit.setCountryName(geoCodeAlias.getAlias());
162
163        geoCodeAlias = geoCodeAliasLogic.getGeoCodeAliasUsingNames(null, geoCode, GeoCodeAliasTypes.ISO_3_NUMBER.name());
164        edit.setIso3Number(geoCodeAlias.getAlias());
165
166        geoCodeAlias = geoCodeAliasLogic.getGeoCodeAliasUsingNames(null, geoCode, GeoCodeAliasTypes.ISO_3_LETTER.name());
167        edit.setIso3Letter(geoCodeAlias.getAlias());
168
169        geoCodeAlias = geoCodeAliasLogic.getGeoCodeAliasUsingNames(null, geoCode, GeoCodeAliasTypes.ISO_2_LETTER.name());
170        edit.setIso2Letter(geoCodeAlias.getAlias());
171
172        edit.setTelephoneCode(geoCodeCountry.getTelephoneCode());
173        edit.setAreaCodePattern(geoCodeCountry.getAreaCodePattern());
174        edit.setAreaCodeRequired(geoCodeCountry.getAreaCodeRequired().toString());
175        edit.setAreaCodeExample(geoCodeCountry.getAreaCodeExample());
176        edit.setTelephoneNumberPattern(geoCodeCountry.getTelephoneNumberPattern());
177        edit.setTelephoneNumberExample(geoCodeCountry.getTelephoneNumberExample());
178        edit.setPostalAddressFormatName(geoCodeCountry.getPostalAddressFormat().getLastDetail().getPostalAddressFormatName());
179        edit.setCityRequired(geoCodeCountry.getCityRequired().toString());
180        edit.setCityGeoCodeRequired(geoCodeCountry.getCityGeoCodeRequired().toString());
181        edit.setStateRequired(geoCodeCountry.getStateRequired().toString());
182        edit.setStateGeoCodeRequired(geoCodeCountry.getStateGeoCodeRequired().toString());
183        edit.setPostalCodePattern(geoCodeCountry.getPostalCodePattern());
184        edit.setPostalCodeRequired(geoCodeCountry.getPostalCodeRequired().toString());
185        edit.setPostalCodeGeoCodeRequired(geoCodeCountry.getPostalCodeGeoCodeRequired().toString());
186        edit.setPostalCodeLength(postalCodeLength == null ? null : postalCodeLength.toString());
187        edit.setPostalCodeGeoCodeLength(postalCodeGeoCodeLength == null ? null : postalCodeGeoCodeLength.toString());
188        edit.setPostalCodeExample(geoCodeCountry.getPostalCodeExample());
189        edit.setIsDefault(geoCodeDetail.getIsDefault().toString());
190        edit.setSortOrder(geoCodeDetail.getSortOrder().toString());
191
192        if(geoCodeDescription != null) {
193            edit.setDescription(geoCodeDescription.getDescription());
194        }
195    }
196
197    PostalAddressFormat postalAddressFormat;
198
199    @Override
200    public void canUpdate(GeoCode geoCode) {
201        var geoCodeTypeLogic = GeoCodeTypeLogic.getInstance();
202        var geoCodeType = geoCodeTypeLogic.getGeoCodeTypeByName(this, GeoCodeTypes.COUNTRY.name());
203
204        if(!hasExecutionErrors()) {
205            var geoCodeScopeLogic = GeoCodeScopeLogic.getInstance();
206            var geoCodeScope = geoCodeScopeLogic.getGeoCodeScopeByName(this, GeoCodeScopes.COUNTRIES.name());
207
208            if(!hasExecutionErrors()) {
209                var geoCodeLogic = GeoCodeLogic.getInstance();
210                var iso3Number = edit.getIso3Number();
211                var duplicateGeoCode = geoCodeLogic.getGeoCodeByAlias(this, geoCodeType, geoCodeScope, GeoCodeAliasTypes.ISO_3_NUMBER.name(), iso3Number);
212
213                if((duplicateGeoCode == null || duplicateGeoCode.equals(geoCode)) && !hasExecutionErrors()) {
214                    var iso3Letter = edit.getIso3Letter();
215
216                    duplicateGeoCode = geoCodeLogic.getGeoCodeByAlias(this, geoCodeType, geoCodeScope, GeoCodeAliasTypes.ISO_3_LETTER.name(), iso3Letter);
217
218                    if((duplicateGeoCode == null || duplicateGeoCode.equals(geoCode)) && !hasExecutionErrors()) {
219                        var iso2Letter = edit.getIso2Letter();
220
221                        duplicateGeoCode = geoCodeLogic.getGeoCodeByAlias(this, geoCodeType, geoCodeScope, GeoCodeAliasTypes.ISO_2_LETTER.name(), iso2Letter);
222
223                        if((duplicateGeoCode == null || duplicateGeoCode.equals(geoCode)) && !hasExecutionErrors()) {
224                            var countryName = edit.getCountryName();
225
226                            duplicateGeoCode = geoCodeLogic.getGeoCodeByAlias(this, geoCodeType, geoCodeScope, GeoCodeAliasTypes.COUNTRY_NAME.name(), countryName);
227
228                            if((duplicateGeoCode == null || duplicateGeoCode.equals(geoCode)) && !hasExecutionErrors()) {
229                                var contactControl = Session.getModelController(ContactControl.class);
230                                var postalAddressFormatName = edit.getPostalAddressFormatName();
231
232                                postalAddressFormat = contactControl.getPostalAddressFormatByName(postalAddressFormatName);
233
234                                if(postalAddressFormat == null) {
235                                    addExecutionError(ExecutionErrors.UnknownPostalAddressFormatName.name(), postalAddressFormatName);
236                                }
237                            } else {
238                                if(geoCode != null) {
239                                    addExecutionError(ExecutionErrors.DuplicateCountryName.name(), countryName);
240                                }
241                            }
242                        } else {
243                            if(geoCode != null) {
244                                addExecutionError(ExecutionErrors.DuplicateIso2Letter.name(), iso2Letter);
245                            }
246                        }
247                    } else {
248                        if(geoCode != null) {
249                            addExecutionError(ExecutionErrors.DuplicateIso3Letter.name(), iso3Letter);
250                        }
251                    }
252                } else {
253                    if(geoCode != null) {
254                        addExecutionError(ExecutionErrors.DuplicateIso3Number.name(), iso3Number);
255                    }
256                }
257            }
258        }
259    }
260
261    @Override
262    public void doUpdate(GeoCode geoCode) {
263        var geoControl = Session.getModelController(GeoControl.class);
264        var geoCodeAliasLogic = GeoCodeAliasLogic.getInstance();
265        var partyPK = getPartyPK();
266        var geoCodeDetailValue = geoControl.getGeoCodeDetailValueForUpdate(geoCode);
267        var geoCodeDescription = geoControl.getGeoCodeDescriptionForUpdate(geoCode, getPreferredLanguage());
268        var geoCodeCountryValue = geoControl.getGeoCodeCountryValueForUpdate(geoCode);
269        var strPostalCodeLength = edit.getPostalCodeLength();
270        var postalCodeLength = strPostalCodeLength == null ? null : Integer.valueOf(strPostalCodeLength);
271        var strPostalCodeGeoCodeLength = edit.getPostalCodeGeoCodeLength();
272        var postalCodeGeoCodeLength = strPostalCodeGeoCodeLength == null ? null : Integer.valueOf(strPostalCodeGeoCodeLength);
273        var description = edit.getDescription();
274
275        var geoCodeAliasValue = geoCodeAliasLogic.getGeoCodeAliasValueUsingNames(null, geoCode, GeoCodeAliasTypes.COUNTRY_NAME.name());
276        geoCodeAliasValue.setAlias(edit.getCountryName());
277        geoControl.updateGeoCodeAliasFromValue(geoCodeAliasValue, partyPK);
278
279        geoCodeAliasValue = geoCodeAliasLogic.getGeoCodeAliasValueUsingNames(null, geoCode, GeoCodeAliasTypes.ISO_3_NUMBER.name());
280        geoCodeAliasValue.setAlias(edit.getIso3Number());
281        geoControl.updateGeoCodeAliasFromValue(geoCodeAliasValue, partyPK);
282
283        geoCodeAliasValue = geoCodeAliasLogic.getGeoCodeAliasValueUsingNames(null, geoCode, GeoCodeAliasTypes.ISO_3_LETTER.name());
284        geoCodeAliasValue.setAlias(edit.getIso3Letter());
285        geoControl.updateGeoCodeAliasFromValue(geoCodeAliasValue, partyPK);
286
287        geoCodeAliasValue = geoCodeAliasLogic.getGeoCodeAliasValueUsingNames(null, geoCode, GeoCodeAliasTypes.ISO_2_LETTER.name());
288        geoCodeAliasValue.setAlias(edit.getIso2Letter());
289        geoControl.updateGeoCodeAliasFromValue(geoCodeAliasValue, partyPK);
290
291        geoCodeCountryValue.setTelephoneCode(edit.getTelephoneCode());
292        geoCodeCountryValue.setAreaCodePattern(edit.getAreaCodePattern());
293        geoCodeCountryValue.setAreaCodeRequired(Boolean.valueOf(edit.getAreaCodeRequired()));
294        geoCodeCountryValue.setAreaCodeExample(edit.getAreaCodeExample());
295        geoCodeCountryValue.setTelephoneNumberPattern(edit.getTelephoneNumberPattern());
296        geoCodeCountryValue.setTelephoneNumberExample(edit.getTelephoneNumberExample());
297        geoCodeCountryValue.setPostalAddressFormatPK(postalAddressFormat.getPrimaryKey());
298        geoCodeCountryValue.setCityRequired(Boolean.valueOf(edit.getCityRequired()));
299        geoCodeCountryValue.setCityGeoCodeRequired(Boolean.valueOf(edit.getCityGeoCodeRequired()));
300        geoCodeCountryValue.setStateRequired(Boolean.valueOf(edit.getStateRequired()));
301        geoCodeCountryValue.setStateGeoCodeRequired(Boolean.valueOf(edit.getStateGeoCodeRequired()));
302        geoCodeCountryValue.setPostalCodePattern(edit.getPostalCodePattern());
303        geoCodeCountryValue.setPostalCodeRequired(Boolean.valueOf(edit.getPostalCodeRequired()));
304        geoCodeCountryValue.setPostalCodeGeoCodeRequired(Boolean.valueOf(edit.getPostalCodeGeoCodeRequired()));
305        geoCodeCountryValue.setPostalCodeLength(postalCodeLength);
306        geoCodeCountryValue.setPostalCodeGeoCodeLength(postalCodeGeoCodeLength);
307        geoCodeCountryValue.setPostalCodeExample(edit.getPostalCodeExample());
308        
309        geoCodeDetailValue.setIsDefault(Boolean.valueOf(edit.getIsDefault()));
310        geoCodeDetailValue.setSortOrder(Integer.valueOf(edit.getSortOrder()));
311
312        geoControl.updateGeoCodeFromValue(geoCodeDetailValue, partyPK);
313        geoControl.updateGeoCodeCountryFromValue(geoCodeCountryValue, partyPK);
314
315        if(geoCodeDescription == null && description != null) {
316            geoControl.createGeoCodeDescription(geoCode, getPreferredLanguage(), description, partyPK);
317        } else {
318            if(geoCodeDescription != null && description == null) {
319                geoControl.deleteGeoCodeDescription(geoCodeDescription, partyPK);
320            } else {
321                if(geoCodeDescription != null && description != null) {
322                    var geoCodeDescriptionValue = geoControl.getGeoCodeDescriptionValue(geoCodeDescription);
323
324                    geoCodeDescriptionValue.setDescription(description);
325                    geoControl.updateGeoCodeDescriptionFromValue(geoCodeDescriptionValue, partyPK);
326                }
327            }
328        }
329    }
330
331}