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.metis.field.MetisFieldRequired;
20  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicDataType;
21  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseCash;
22  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseCashCategory;
23  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseCashInfo;
24  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseCashInfoSet;
25  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDepositInfoSet;
26  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePayee;
27  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePayee.MoneyWisePayeeList;
28  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransCategory;
29  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransCategory.MoneyWiseTransCategoryList;
30  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseAccountInfoClass;
31  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseCurrency;
32  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransCategoryClass;
33  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
34  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusMoney;
35  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataInfoClass;
36  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataItem;
37  import io.github.tonywasher.joceanus.prometheus.validate.PrometheusValidateInfoSet;
38  
39  import java.util.Currency;
40  import java.util.Iterator;
41  
42  /**
43   * Validate CashInfoSet.
44   */
45  public class MoneyWiseValidateCashInfoSet
46          extends PrometheusValidateInfoSet<MoneyWiseCashInfo> {
47      /**
48       * ClosedPayee Error string.
49       */
50      private static final String ERROR_PAYEECLOSED = "AutoPayee is closed for non-closed autoCash";
51  
52      @Override
53      public MoneyWiseCash getOwner() {
54          return (MoneyWiseCash) super.getOwner();
55      }
56  
57      @Override
58      public MoneyWiseCashInfoSet getInfoSet() {
59          return (MoneyWiseCashInfoSet) super.getInfoSet();
60      }
61  
62      @Override
63      public MetisFieldRequired isClassRequired(final PrometheusDataInfoClass pClass) {
64          /* Access details about the Cash */
65          final MoneyWiseCash myCash = getOwner();
66          final MoneyWiseCashCategory myCategory = myCash.getCategory();
67  
68          /* If we have no Category, no class is allowed */
69          if (myCategory == null) {
70              return MetisFieldRequired.NOTALLOWED;
71          }
72  
73          /* Switch on class */
74          return switch ((MoneyWiseAccountInfoClass) pClass) {
75              /* Allowed set */
76              case NOTES -> MetisFieldRequired.CANEXIST;
77              case OPENINGBALANCE -> myCash.isAutoExpense()
78                      ? MetisFieldRequired.NOTALLOWED
79                      : MetisFieldRequired.CANEXIST;
80              case AUTOPAYEE, AUTOEXPENSE -> myCash.isAutoExpense()
81                      ? MetisFieldRequired.MUSTEXIST
82                      : MetisFieldRequired.NOTALLOWED;
83  
84              /* Disallowed Set */
85              default -> MetisFieldRequired.NOTALLOWED;
86          };
87      }
88  
89      @Override
90      public void validateClass(final MoneyWiseCashInfo pInfo,
91                                final PrometheusDataInfoClass pClass) {
92          /* Switch on class */
93          switch ((MoneyWiseAccountInfoClass) pClass) {
94              case OPENINGBALANCE:
95                  validateOpeningBalance(pInfo);
96                  break;
97              case AUTOEXPENSE:
98                  validateAutoExpense(pInfo);
99                  break;
100             case AUTOPAYEE:
101                 validateAutoPayee(pInfo);
102                 break;
103             case NOTES:
104                 validateNotes(pInfo);
105                 break;
106             default:
107                 break;
108         }
109     }
110 
111     /**
112      * Validate the opening balance.
113      *
114      * @param pInfo the info
115      */
116     private void validateOpeningBalance(final MoneyWiseCashInfo pInfo) {
117         final OceanusMoney myBalance = pInfo.getValue(OceanusMoney.class);
118         if (!myBalance.getCurrency().equals(getOwner().getCurrency())) {
119             getOwner().addError(MoneyWiseDepositInfoSet.ERROR_CURRENCY, MoneyWiseCashInfoSet.getFieldForClass(MoneyWiseAccountInfoClass.OPENINGBALANCE));
120         }
121     }
122 
123     /**
124      * Validate the autoExpense info.
125      *
126      * @param pInfo the info
127      */
128     private void validateAutoExpense(final MoneyWiseCashInfo pInfo) {
129         final MoneyWiseTransCategory myExpense = pInfo.getEventCategory();
130         final MoneyWiseTransCategoryClass myCatClass = myExpense.getCategoryTypeClass();
131         if (!myCatClass.isExpense() || myCatClass.canParentCategory()) {
132             getOwner().addError(MoneyWiseCashInfoSet.ERROR_AUTOEXP, MoneyWiseCashInfoSet.getFieldForClass(MoneyWiseAccountInfoClass.AUTOEXPENSE));
133         }
134     }
135 
136     /**
137      * Validate the autoPayee info.
138      *
139      * @param pInfo the info
140      */
141     private void validateAutoPayee(final MoneyWiseCashInfo pInfo) {
142         final MoneyWisePayee myPayee = pInfo.getPayee();
143         if (myPayee.isClosed() && !getOwner().isClosed()) {
144             getOwner().addError(ERROR_PAYEECLOSED, MoneyWiseCashInfoSet.getFieldForClass(MoneyWiseAccountInfoClass.AUTOPAYEE));
145         }
146     }
147 
148     /**
149      * Validate the Notes info.
150      *
151      * @param pInfo the info
152      */
153     private void validateNotes(final MoneyWiseCashInfo pInfo) {
154         final char[] myArray = pInfo.getValue(char[].class);
155         if (myArray.length > MoneyWiseAccountInfoClass.NOTES.getMaximumLength()) {
156             getOwner().addError(PrometheusDataItem.ERROR_LENGTH, MoneyWiseCashInfoSet.getFieldForClass(MoneyWiseAccountInfoClass.NOTES));
157         }
158     }
159 
160     @Override
161     protected void setDefault(final PrometheusDataInfoClass pClass) throws OceanusException {
162         /* Switch on the class */
163         switch ((MoneyWiseAccountInfoClass) pClass) {
164             case AUTOEXPENSE:
165                 getInfoSet().setValue(pClass, getDefaultAutoExpense());
166                 break;
167             case AUTOPAYEE:
168                 getInfoSet().setValue(pClass, getDefaultAutoPayee());
169                 break;
170             default:
171                 break;
172         }
173     }
174 
175     /**
176      * Obtain default expense for autoExpense cash.
177      *
178      * @return the default expense
179      */
180     private MoneyWiseTransCategory getDefaultAutoExpense() {
181         /* Access the category list */
182         final MoneyWiseTransCategoryList myCategories
183                 = getEditSet().getDataList(MoneyWiseBasicDataType.TRANSCATEGORY, MoneyWiseTransCategoryList.class);
184 
185         /* loop through the categories */
186         final Iterator<MoneyWiseTransCategory> myIterator = myCategories.iterator();
187         while (myIterator.hasNext()) {
188             final MoneyWiseTransCategory myCategory = myIterator.next();
189 
190             /* Ignore deleted categories */
191             if (myCategory.isDeleted()) {
192                 continue;
193             }
194 
195             /* Ignore categories that are the wrong class */
196             final MoneyWiseTransCategoryClass myCatClass = myCategory.getCategoryTypeClass();
197             if (myCatClass.isExpense() && !myCatClass.canParentCategory()) {
198                 return myCategory;
199             }
200         }
201 
202         /* Return no category */
203         return null;
204     }
205 
206     /**
207      * Obtain default payee for autoExpense cash.
208      *
209      * @return the default payee
210      */
211     private MoneyWisePayee getDefaultAutoPayee() {
212         /* Access the payee list */
213         final MoneyWisePayeeList myPayees
214                 = getEditSet().getDataList(MoneyWiseBasicDataType.PAYEE, MoneyWisePayeeList.class);
215 
216         /* loop through the payees */
217         final Iterator<MoneyWisePayee> myIterator = myPayees.iterator();
218         while (myIterator.hasNext()) {
219             final MoneyWisePayee myPayee = myIterator.next();
220 
221             /* Ignore deleted and closed payees */
222             if (!myPayee.isDeleted() && Boolean.TRUE.equals(!myPayee.isClosed())) {
223                 return myPayee;
224             }
225         }
226 
227         /* Return no payee */
228         return null;
229     }
230 
231     @Override
232     protected void autoCorrect(final PrometheusDataInfoClass pClass) throws OceanusException {
233         /* If the info is Opening balance */
234         if (MoneyWiseAccountInfoClass.OPENINGBALANCE.equals(pClass)) {
235             /* Access the value */
236             final MoneyWiseCash myOwner = getOwner();
237             OceanusMoney myOpening = myOwner.getOpeningBalance();
238             final MoneyWiseCurrency myAssetCurrency = myOwner.getAssetCurrency();
239             final Currency myCurrency = myAssetCurrency.getCurrency();
240 
241             /* If we need to change currency */
242             if (!myCurrency.equals(myOpening.getCurrency())) {
243                 myOpening = myOpening.changeCurrency(myCurrency);
244                 getInfoSet().setValue(pClass, myOpening);
245             }
246         }
247     }
248 }