View Javadoc
1   /*
2    * MoneyWise: Finance Application
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package io.github.tonywasher.joceanus.moneywise.data.validate;
18  
19  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
20  import io.github.tonywasher.joceanus.metis.field.MetisFieldRequired;
21  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseAssetBase;
22  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicDataType;
23  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicResource;
24  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDataValidator.MoneyWiseDataValidatorAutoCorrect;
25  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePayee;
26  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePayee.MoneyWisePayeeList;
27  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity;
28  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity.MoneyWiseSecurityDataMap;
29  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity.MoneyWiseSecurityList;
30  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityHolding;
31  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityInfoSet;
32  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseAccountInfoClass;
33  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseCurrency;
34  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWisePayeeClass;
35  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseSecurityClass;
36  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseSecurityType;
37  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseSecurityType.MoneyWiseSecurityTypeList;
38  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseStaticDataType;
39  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataItem;
40  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataResource;
41  import io.github.tonywasher.joceanus.prometheus.views.PrometheusEditSet;
42  
43  import java.util.Iterator;
44  
45  /**
46   * Validator for Security.
47   */
48  public class MoneyWiseValidateSecurity
49          extends MoneyWiseValidateAccount<MoneyWiseSecurity>
50          implements MoneyWiseDataValidatorAutoCorrect<MoneyWiseSecurity> {
51      /**
52       * New Account name.
53       */
54      private static final String NAME_NEWACCOUNT = MoneyWiseBasicResource.SECURITY_NEWACCOUNT.getValue();
55  
56      /**
57       * The infoSet validator.
58       */
59      private final MoneyWiseValidateSecurityInfoSet theInfoSet;
60  
61      /**
62       * Constructor.
63       */
64      MoneyWiseValidateSecurity() {
65          theInfoSet = new MoneyWiseValidateSecurityInfoSet();
66      }
67  
68      @Override
69      public void setEditSet(final PrometheusEditSet pEditSet) {
70          super.setEditSet(pEditSet);
71          theInfoSet.storeEditSet(pEditSet);
72      }
73  
74      @Override
75      public void validate(final PrometheusDataItem pSecurity) {
76          final MoneyWiseSecurity mySecurity = (MoneyWiseSecurity) pSecurity;
77          final MoneyWiseSecurityList myList = mySecurity.getList();
78          final MoneyWisePayee myParent = mySecurity.getParent();
79          final MoneyWiseSecurityType mySecType = mySecurity.getCategory();
80          final MoneyWiseCurrency myCurrency = mySecurity.getAssetCurrency();
81          final String mySymbol = mySecurity.getSymbol();
82  
83          /* Validate base components */
84          super.validate(pSecurity);
85  
86          /* SecurityType must be non-null */
87          if (mySecType == null) {
88              pSecurity.addError(PrometheusDataItem.ERROR_MISSING, MoneyWiseBasicResource.CATEGORY_NAME);
89          } else {
90              /* Access the class */
91              final MoneyWiseSecurityClass myClass = mySecType.getSecurityClass();
92  
93              /* SecurityType must be enabled */
94              if (!mySecType.getEnabled()) {
95                  pSecurity.addError(PrometheusDataItem.ERROR_DISABLED, MoneyWiseBasicResource.CATEGORY_NAME);
96              }
97  
98              /* If the SecurityType is singular */
99              if (myClass.isSingular()) {
100                 /* Count the elements of this class */
101                 final MoneyWiseSecurityDataMap myMap = myList.getDataMap();
102                 if (!myMap.validSingularCount(myClass)) {
103                     pSecurity.addError(PrometheusDataItem.ERROR_MULT, MoneyWiseBasicResource.CATEGORY_NAME);
104                 }
105             }
106         }
107 
108         /* Currency must be non-null and enabled */
109         if (myCurrency == null) {
110             pSecurity.addError(PrometheusDataItem.ERROR_MISSING, MoneyWiseStaticDataType.CURRENCY);
111         } else if (!myCurrency.getEnabled()) {
112             pSecurity.addError(PrometheusDataItem.ERROR_DISABLED, MoneyWiseStaticDataType.CURRENCY);
113         }
114 
115         /* Parent must be non-null */
116         if (myParent == null) {
117             pSecurity.addError(PrometheusDataItem.ERROR_MISSING, MoneyWiseBasicResource.ASSET_PARENT);
118         } else {
119             /* If we are open then parent must be open */
120             if (!mySecurity.isClosed() && Boolean.TRUE.equals(myParent.isClosed())) {
121                 pSecurity.addError(ERROR_PARCLOSED, MoneyWiseBasicResource.ASSET_CLOSED);
122             }
123 
124             /* Check class */
125             if (mySecType != null) {
126                 /* Access the classes */
127                 final MoneyWiseSecurityClass myClass = mySecType.getSecurityClass();
128                 final MoneyWisePayeeClass myParClass = myParent.getCategoryClass();
129 
130                 /* Parent must be suitable */
131                 if (!myParClass.canParentSecurity(myClass)) {
132                     pSecurity.addError(ERROR_BADPARENT, MoneyWiseBasicResource.ASSET_PARENT);
133                 }
134             }
135         }
136 
137         /* If we have a securityType */
138         if (mySecType != null) {
139             /* Check symbol rules */
140             if (mySecType.getSecurityClass().needsSymbol()) {
141                 if (mySymbol == null) {
142                     pSecurity.addError(PrometheusDataItem.ERROR_MISSING, MoneyWiseSecurityInfoSet.getFieldForClass(MoneyWiseAccountInfoClass.SYMBOL));
143                 } else if (!myList.validSymbolCount(mySymbol)) {
144                     pSecurity.addError(PrometheusDataItem.ERROR_DUPLICATE, MoneyWiseSecurityInfoSet.getFieldForClass(MoneyWiseAccountInfoClass.SYMBOL));
145                 }
146             } else if (mySymbol != null) {
147                 pSecurity.addError(PrometheusDataItem.ERROR_EXIST, MoneyWiseSecurityInfoSet.getFieldForClass(MoneyWiseAccountInfoClass.SYMBOL));
148             }
149         }
150 
151         /* If we have an infoSet */
152         if (mySecurity.getInfoSet() != null) {
153             /* Validate the InfoSet */
154             theInfoSet.validate(mySecurity.getInfoSet());
155         }
156 
157         /* Set validation flag */
158         if (!pSecurity.hasErrors()) {
159             pSecurity.setValidEdit();
160         }
161     }
162 
163     @Override
164     public void validateName(final MoneyWiseAssetBase pSecurity,
165                              final String pName) {
166         /* Perform basic checks */
167         super.validateName(pSecurity, pName);
168 
169         /* Check that the name is not a reserved name */
170         if (pName.equals(MoneyWiseSecurityHolding.SECURITYHOLDING_NEW)
171                 || pName.equals(MoneyWiseValidatePortfolio.NAME_CASHACCOUNT)) {
172             pSecurity.addError(ERROR_RESERVED, PrometheusDataResource.DATAITEM_FIELD_NAME);
173         }
174     }
175 
176     /**
177      * Determine if an infoSet class is required.
178      *
179      * @param pClass the infoSet class
180      * @return the status
181      */
182     public MetisFieldRequired isClassRequired(final MoneyWiseAccountInfoClass pClass) {
183         return theInfoSet.isClassRequired(pClass);
184     }
185 
186     @Override
187     public void setDefaults(final MoneyWiseSecurity pSecurity) throws OceanusException {
188         /* Set values */
189         final MoneyWiseSecurityList myList = pSecurity.getList();
190         pSecurity.setName(getUniqueName(myList, NAME_NEWACCOUNT));
191         pSecurity.setCategory(getDefaultSecurityType());
192         pSecurity.setAssetCurrency(getReportingCurrency());
193         pSecurity.setSymbol(pSecurity.getName());
194         pSecurity.setClosed(Boolean.FALSE);
195         autoCorrect(pSecurity);
196     }
197 
198     @Override
199     public void autoCorrect(final MoneyWiseSecurity pSecurity) throws OceanusException {
200         /* Access category class and parent */
201         final MoneyWiseSecurityClass myClass = pSecurity.getCategoryClass();
202         final MoneyWisePayee myParent = pSecurity.getParent();
203 
204         /* Ensure that we have a valid parent */
205         if (myParent == null
206                 || myParent.getCategoryClass().canParentSecurity(myClass)) {
207             pSecurity.setParent(getDefaultParent(pSecurity));
208         }
209 
210         /* autoCorrect the infoSet */
211         theInfoSet.autoCorrect(pSecurity.getInfoSet());
212     }
213 
214     /**
215      * Obtain security type for new security account.
216      *
217      * @return the security type
218      */
219     private MoneyWiseSecurityType getDefaultSecurityType() {
220         /* loop through the security types */
221         final MoneyWiseSecurityTypeList myTypes
222                 = getEditSet().getDataList(MoneyWiseStaticDataType.SECURITYTYPE, MoneyWiseSecurityTypeList.class);
223         final Iterator<MoneyWiseSecurityType> myIterator = myTypes.iterator();
224         while (myIterator.hasNext()) {
225             final MoneyWiseSecurityType myType = myIterator.next();
226 
227             /* Ignore deleted types */
228             if (!myType.isDeleted()) {
229                 return myType;
230             }
231         }
232 
233         /* Return no category */
234         return null;
235     }
236 
237     /**
238      * Obtain default parent for new security.
239      *
240      * @param pSecurity the security
241      * @return the default parent
242      */
243     private MoneyWisePayee getDefaultParent(final MoneyWiseSecurity pSecurity) {
244         /* Access details */
245         final MoneyWisePayeeList myPayees = getEditSet().getDataList(MoneyWiseBasicDataType.PAYEE, MoneyWisePayeeList.class);
246         final MoneyWiseSecurityClass myClass = pSecurity.getCategoryClass();
247 
248         /* loop through the payees */
249         final Iterator<MoneyWisePayee> myIterator = myPayees.iterator();
250         while (myIterator.hasNext()) {
251             final MoneyWisePayee myPayee = myIterator.next();
252 
253             /* Ignore deleted and closed payees */
254             if (myPayee.isDeleted() || Boolean.TRUE.equals(myPayee.isClosed())) {
255                 continue;
256             }
257 
258             /* If the payee can parent */
259             if (myPayee.getCategoryClass().canParentSecurity(myClass)) {
260                 return myPayee;
261             }
262         }
263 
264         /* Return no payee */
265         return null;
266     }
267 }