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.basic;
18  
19  import io.github.tonywasher.joceanus.metis.data.MetisDataDifference;
20  import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataFieldId;
21  import io.github.tonywasher.joceanus.metis.data.MetisDataResource;
22  import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
23  import io.github.tonywasher.joceanus.metis.field.MetisFieldVersionedSet;
24  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseStaticDataType;
25  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransCategoryClass;
26  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransCategoryType;
27  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransCategoryType.MoneyWiseTransCategoryTypeList;
28  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransInfoClass;
29  import io.github.tonywasher.joceanus.moneywise.exc.MoneyWiseDataException;
30  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
31  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
32  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataItem;
33  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataResource;
34  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataSet;
35  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataValues;
36  import io.github.tonywasher.joceanus.prometheus.data.PrometheusListStyle;
37  import io.github.tonywasher.joceanus.prometheus.data.PrometheusStaticDataItem;
38  import io.github.tonywasher.joceanus.prometheus.views.PrometheusEditSet;
39  
40  import java.util.HashMap;
41  import java.util.Iterator;
42  import java.util.Map;
43  import java.util.Objects;
44  
45  /**
46   * Transaction Category class.
47   */
48  public final class MoneyWiseTransCategory
49          extends MoneyWiseCategoryBase {
50      /**
51       * Object name.
52       */
53      public static final String OBJECT_NAME = MoneyWiseBasicDataType.TRANSCATEGORY.getItemName();
54  
55      /**
56       * List name.
57       */
58      public static final String LIST_NAME = MoneyWiseBasicDataType.TRANSCATEGORY.getListName();
59  
60      /**
61       * Local Report fields.
62       */
63      private static final MetisFieldVersionedSet<MoneyWiseTransCategory> FIELD_DEFS = MetisFieldVersionedSet.newVersionedFieldSet(MoneyWiseTransCategory.class);
64  
65      /*
66       * FieldIds.
67       */
68      static {
69          FIELD_DEFS.declareLinkField(MoneyWiseStaticDataType.TRANSTYPE);
70      }
71  
72      /**
73       * Copy Constructor.
74       *
75       * @param pList     the list
76       * @param pCategory The Category to copy
77       */
78      MoneyWiseTransCategory(final MoneyWiseTransCategoryList pList,
79                             final MoneyWiseTransCategory pCategory) {
80          /* Set standard values */
81          super(pList, pCategory);
82      }
83  
84      /**
85       * Values constructor.
86       *
87       * @param pList   the List to add to
88       * @param pValues the values constructor
89       * @throws OceanusException on error
90       */
91      private MoneyWiseTransCategory(final MoneyWiseTransCategoryList pList,
92                                     final PrometheusDataValues pValues) throws OceanusException {
93          /* Initialise the item */
94          super(pList, pValues);
95  
96          /* Store the Category Type */
97          final Object myValue = pValues.getValue(MoneyWiseStaticDataType.TRANSTYPE);
98          if (myValue instanceof Integer i) {
99              setValueType(i);
100         } else if (myValue instanceof String s) {
101             setValueType(s);
102         }
103     }
104 
105     /**
106      * Edit Constructor.
107      *
108      * @param pList the list
109      */
110     public MoneyWiseTransCategory(final MoneyWiseTransCategoryList pList) {
111         super(pList);
112     }
113 
114     @Override
115     public MetisFieldSetDef getDataFieldSet() {
116         return FIELD_DEFS;
117     }
118 
119     @Override
120     public boolean isActive() {
121         return super.isActive() || isHidden();
122     }
123 
124     @Override
125     public boolean includeXmlField(final MetisDataFieldId pField) {
126         /* Determine whether fields should be included */
127         if (MoneyWiseStaticDataType.TRANSTYPE.equals(pField)) {
128             return true;
129         }
130 
131         /* Pass call on */
132         return super.includeXmlField(pField);
133     }
134 
135     @Override
136     public MoneyWiseTransCategoryType getCategoryType() {
137         return getValues().getValue(MoneyWiseStaticDataType.TRANSTYPE, MoneyWiseTransCategoryType.class);
138     }
139 
140     @Override
141     public MoneyWiseTransCategoryClass getCategoryTypeClass() {
142         final MoneyWiseTransCategoryType myType = getCategoryType();
143         return myType == null
144                 ? null
145                 : myType.getCategoryClass();
146     }
147 
148     @Override
149     public MoneyWiseTransCategory getParentCategory() {
150         return getValues().getValue(PrometheusDataResource.DATAGROUP_PARENT, MoneyWiseTransCategory.class);
151     }
152 
153     /**
154      * Set category type value.
155      *
156      * @param pValue the value
157      */
158     private void setValueType(final MoneyWiseTransCategoryType pValue) {
159         getValues().setUncheckedValue(MoneyWiseStaticDataType.TRANSTYPE, pValue);
160     }
161 
162     /**
163      * Set category type id.
164      *
165      * @param pValue the value
166      */
167     private void setValueType(final Integer pValue) {
168         getValues().setUncheckedValue(MoneyWiseStaticDataType.TRANSTYPE, pValue);
169     }
170 
171     /**
172      * Set category type name.
173      *
174      * @param pValue the value
175      */
176     private void setValueType(final String pValue) {
177         getValues().setUncheckedValue(MoneyWiseStaticDataType.TRANSTYPE, pValue);
178     }
179 
180     @Override
181     public MoneyWiseTransCategoryList getList() {
182         return (MoneyWiseTransCategoryList) super.getList();
183     }
184 
185     /**
186      * Is this event category the required class.
187      *
188      * @param pClass the required category class.
189      * @return true/false
190      */
191     public boolean isCategoryClass(final MoneyWiseTransCategoryClass pClass) {
192         /* Check for match */
193         return getCategoryTypeClass() == pClass;
194     }
195 
196     /**
197      * Is this event category a transfer?
198      *
199      * @return true/false
200      */
201     public boolean isTransfer() {
202         /* Check for match */
203         final MoneyWiseTransCategoryClass myClass = getCategoryTypeClass();
204         return myClass != null
205                 && myClass.isTransfer();
206     }
207 
208     /**
209      * Set defaults.
210      *
211      * @param pParent the parent
212      * @throws OceanusException on error
213      */
214     public void setDefaults(final MoneyWiseTransCategory pParent) throws OceanusException {
215         getList().getValidator().setDefaults(pParent, this);
216     }
217 
218     @Override
219     public void resolveDataSetLinks() throws OceanusException {
220         /* Update the Underlying details */
221         super.resolveDataSetLinks();
222 
223         /* Resolve category type and parent */
224         resolveDataLink(MoneyWiseStaticDataType.TRANSTYPE, MoneyWiseStaticDataType.TRANSTYPE);
225     }
226 
227     @Override
228     protected void resolveEditSetLinks() throws OceanusException {
229         /* Resolve parent within list */
230         resolveDataLink(PrometheusDataResource.DATAGROUP_PARENT, getList());
231 
232         /* Resolve StaticType if required */
233         final PrometheusEditSet myEditSet = getList().getEditSet();
234         if (myEditSet.hasDataType(MoneyWiseStaticDataType.TRANSTYPE)) {
235             resolveDataLink(MoneyWiseStaticDataType.TRANSTYPE, myEditSet.getDataList(MoneyWiseStaticDataType.TRANSTYPE, MoneyWiseTransCategoryTypeList.class));
236         }
237     }
238 
239     @Override
240     public void setCategoryType(final PrometheusStaticDataItem pType) {
241         setValueType((MoneyWiseTransCategoryType) pType);
242     }
243 
244     /**
245      * Update base category from an edited category.
246      *
247      * @param pCategory the edited category
248      * @return whether changes have been made
249      */
250     @Override
251     public boolean applyChanges(final PrometheusDataItem pCategory) {
252         /* Can only update from a transaction category */
253         if (!(pCategory instanceof MoneyWiseTransCategory myCategory)) {
254             return false;
255         }
256 
257         /* Store the current detail into history */
258         pushHistory();
259 
260         /* Apply basic changes */
261         applyBasicChanges(myCategory);
262 
263         /* Update the category type if required */
264         if (!MetisDataDifference.isEqual(getCategoryType(), myCategory.getCategoryType())) {
265             setValueType(myCategory.getCategoryType());
266         }
267 
268         /* Check for changes */
269         return checkForHistory();
270     }
271 
272     /**
273      * Is the category hidden?
274      *
275      * @return true/false
276      */
277     public boolean isHidden() {
278         final MoneyWiseTransCategoryClass myClass = this.getCategoryTypeClass();
279         return myClass != null
280                 && myClass.isHiddenType();
281     }
282 
283     /**
284      * The Transaction Category List class.
285      */
286     public static class MoneyWiseTransCategoryList
287             extends MoneyWiseCategoryBaseList<MoneyWiseTransCategory> {
288         /**
289          * Report fields.
290          */
291         private static final MetisFieldSet<MoneyWiseTransCategoryList> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseTransCategoryList.class);
292 
293         /**
294          * The EditSet.
295          */
296         private PrometheusEditSet theEditSet;
297 
298         /**
299          * Construct an empty CORE Category list.
300          *
301          * @param pData the DataSet for the list
302          */
303         public MoneyWiseTransCategoryList(final PrometheusDataSet pData) {
304             super(pData, MoneyWiseTransCategory.class, MoneyWiseBasicDataType.TRANSCATEGORY);
305         }
306 
307         /**
308          * Constructor for a cloned List.
309          *
310          * @param pSource the source List
311          */
312         protected MoneyWiseTransCategoryList(final MoneyWiseTransCategoryList pSource) {
313             super(pSource);
314         }
315 
316         @Override
317         public MetisFieldSet<MoneyWiseTransCategoryList> getDataFieldSet() {
318             return FIELD_DEFS;
319         }
320 
321         @Override
322         public String listName() {
323             return LIST_NAME;
324         }
325 
326         @Override
327         public MetisFieldSetDef getItemFields() {
328             return MoneyWiseTransCategory.FIELD_DEFS;
329         }
330 
331         /**
332          * Obtain editSet.
333          *
334          * @return the editSet
335          */
336         public PrometheusEditSet getEditSet() {
337             return theEditSet;
338         }
339 
340         @Override
341         public MoneyWiseTransCategoryDataMap getDataMap() {
342             return (MoneyWiseTransCategoryDataMap) super.getDataMap();
343         }
344 
345         @Override
346         protected MoneyWiseTransCategoryList getEmptyList(final PrometheusListStyle pStyle) {
347             final MoneyWiseTransCategoryList myList = new MoneyWiseTransCategoryList(this);
348             myList.setStyle(pStyle);
349             return myList;
350         }
351 
352         /**
353          * Derive Edit list.
354          *
355          * @param pEditSet the editSet
356          * @return the edit list
357          * @throws OceanusException on error
358          */
359         public MoneyWiseTransCategoryList deriveEditList(final PrometheusEditSet pEditSet) throws OceanusException {
360             /* Build an empty List */
361             final MoneyWiseTransCategoryList myList = getEmptyList(PrometheusListStyle.EDIT);
362             myList.ensureMap();
363             pEditSet.setEditEntryList(MoneyWiseBasicDataType.TRANSCATEGORY, myList);
364             myList.getValidator().setEditSet(pEditSet);
365 
366             /* Store the editSet */
367             myList.theEditSet = pEditSet;
368 
369             /* Loop through the categories */
370             final Iterator<MoneyWiseTransCategory> myIterator = iterator();
371             while (myIterator.hasNext()) {
372                 final MoneyWiseTransCategory myCurr = myIterator.next();
373 
374                 /* Ignore deleted events */
375                 if (myCurr.isDeleted()) {
376                     continue;
377                 }
378 
379                 /* Build the new linked category and add it to the list */
380                 final MoneyWiseTransCategory myCategory = new MoneyWiseTransCategory(myList, myCurr);
381                 myList.add(myCategory);
382                 myCategory.resolveEditSetLinks();
383 
384                 /* Adjust the map */
385                 myCategory.adjustMapForItem();
386             }
387 
388             /* Return the list */
389             return myList;
390         }
391 
392         /**
393          * Add a new item to the core list.
394          *
395          * @param pCategory item
396          * @return the newly added item
397          */
398         @Override
399         public MoneyWiseTransCategory addCopyItem(final PrometheusDataItem pCategory) {
400             /* Can only clone a TransactionCategory */
401             if (!(pCategory instanceof MoneyWiseTransCategory)) {
402                 throw new UnsupportedOperationException();
403             }
404 
405             final MoneyWiseTransCategory myCategory = new MoneyWiseTransCategory(this, (MoneyWiseTransCategory) pCategory);
406             add(myCategory);
407             return myCategory;
408         }
409 
410         /**
411          * Add a new item to the edit list.
412          *
413          * @return the new item
414          */
415         @Override
416         public MoneyWiseTransCategory addNewItem() {
417             final MoneyWiseTransCategory myCategory = new MoneyWiseTransCategory(this);
418             add(myCategory);
419             return myCategory;
420         }
421 
422         /**
423          * Obtain the first category for the specified class.
424          *
425          * @param pClass the category class
426          * @return the category
427          */
428         public MoneyWiseTransCategory getSingularClass(final MoneyWiseTransCategoryClass pClass) {
429             /* Lookup in the map */
430             return getDataMap().findSingularItem(pClass);
431         }
432 
433         /**
434          * Obtain singular category for EventInfoClass.
435          *
436          * @param pInfoClass the Event info class
437          * @return the corresponding category.
438          */
439         public MoneyWiseTransCategory getEventInfoCategory(final MoneyWiseTransInfoClass pInfoClass) {
440             /* Switch on info class */
441             switch (pInfoClass) {
442                 case TAXCREDIT:
443                     return getSingularClass(MoneyWiseTransCategoryClass.INCOMETAX);
444                 case DEEMEDBENEFIT:
445                     return getSingularClass(MoneyWiseTransCategoryClass.VIRTUALINCOME);
446                 case EMPLOYEENATINS:
447                     return getSingularClass(MoneyWiseTransCategoryClass.EMPLOYEENATINS);
448                 case EMPLOYERNATINS:
449                     return getSingularClass(MoneyWiseTransCategoryClass.EMPLOYERNATINS);
450                 case WITHHELD:
451                     return getSingularClass(MoneyWiseTransCategoryClass.WITHHELD);
452                 default:
453                     return null;
454             }
455         }
456 
457         @Override
458         public MoneyWiseTransCategory addValuesItem(final PrometheusDataValues pValues) throws OceanusException {
459             /* Create the category */
460             final MoneyWiseTransCategory myCategory = new MoneyWiseTransCategory(this, pValues);
461 
462             /* Check that this CategoryId has not been previously added */
463             if (!isIdUnique(myCategory.getIndexedId())) {
464                 myCategory.addError(ERROR_DUPLICATE, MetisDataResource.DATA_ID);
465                 throw new MoneyWiseDataException(myCategory, ERROR_VALIDATION);
466             }
467 
468             /* Add to the list */
469             add(myCategory);
470 
471             /* Return it */
472             return myCategory;
473         }
474 
475         @Override
476         protected MoneyWiseTransCategoryDataMap allocateDataMap() {
477             return new MoneyWiseTransCategoryDataMap();
478         }
479     }
480 
481     /**
482      * The dataMap class.
483      */
484     public static class MoneyWiseTransCategoryDataMap
485             extends MoneyWiseCategoryDataMap<MoneyWiseTransCategory> {
486         /**
487          * Report fields.
488          */
489         private static final MetisFieldSet<MoneyWiseTransCategoryDataMap> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseTransCategoryDataMap.class);
490 
491         /*
492          * Declare Fields.
493          */
494         static {
495             FIELD_DEFS.declareLocalField(MoneyWiseBasicResource.MONEYWISEDATA_MAP_SINGULARMAP, MoneyWiseTransCategoryDataMap::getSingularMap);
496             FIELD_DEFS.declareLocalField(MoneyWiseBasicResource.MONEYWISEDATA_MAP_SINGULARCOUNTS, MoneyWiseTransCategoryDataMap::getSingularCountMap);
497         }
498 
499         /**
500          * Map of category counts.
501          */
502         private final Map<Integer, Integer> theCategoryCountMap;
503 
504         /**
505          * Map of singular categories.
506          */
507         private final Map<Integer, MoneyWiseTransCategory> theCategoryMap;
508 
509         /**
510          * Constructor.
511          */
512         public MoneyWiseTransCategoryDataMap() {
513             /* Create the maps */
514             theCategoryCountMap = new HashMap<>();
515             theCategoryMap = new HashMap<>();
516         }
517 
518         @Override
519         public MetisFieldSet<MoneyWiseTransCategoryDataMap> getDataFieldSet() {
520             return FIELD_DEFS;
521         }
522 
523         @Override
524         public String formatObject(final OceanusDataFormatter pFormatter) {
525             return FIELD_DEFS.getName();
526         }
527 
528         /**
529          * Obtain the categoryMap.
530          *
531          * @return the map
532          */
533         private Map<Integer, MoneyWiseTransCategory> getSingularMap() {
534             return theCategoryMap;
535         }
536 
537         /**
538          * Obtain the categoryCountMap.
539          *
540          * @return the map
541          */
542         private Map<Integer, Integer> getSingularCountMap() {
543             return theCategoryCountMap;
544         }
545 
546         @Override
547         public void resetMap() {
548             super.resetMap();
549             theCategoryCountMap.clear();
550             theCategoryMap.clear();
551         }
552 
553         @Override
554         public void adjustForItem(final PrometheusDataItem pItem) {
555             /* Access item */
556             final MoneyWiseTransCategory myItem = (MoneyWiseTransCategory) pItem;
557 
558             /* If the class is singular */
559             final MoneyWiseTransCategoryClass myClass = Objects.requireNonNull(myItem.getCategoryTypeClass());
560             if (myClass.isSingular()) {
561                 /* Adjust category count */
562                 final Integer myId = myClass.getClassId();
563                 final Integer myCount = theCategoryCountMap.get(myId);
564                 if (myCount == null) {
565                     theCategoryCountMap.put(myId, ONE);
566                 } else {
567                     theCategoryCountMap.put(myId, myCount + 1);
568                 }
569 
570                 /* Adjust category map */
571                 theCategoryMap.put(myId, myItem);
572             }
573 
574             /* Adjust name count */
575             adjustForItem(myItem, myItem.getName());
576         }
577 
578         /**
579          * find singular item.
580          *
581          * @param pClass the class to look up
582          * @return the matching item
583          */
584         public MoneyWiseTransCategory findSingularItem(final MoneyWiseTransCategoryClass pClass) {
585             return theCategoryMap.get(pClass.getClassId());
586         }
587 
588         /**
589          * Check validity of singular count.
590          *
591          * @param pClass the class to look up
592          * @return true/false
593          */
594         public boolean validSingularCount(final MoneyWiseTransCategoryClass pClass) {
595             final Integer myResult = theCategoryCountMap.get(pClass.getClassId());
596             return ONE.equals(myResult);
597         }
598     }
599 }