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.MetisDataEditState;
21  import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataFieldId;
22  import io.github.tonywasher.joceanus.metis.data.MetisDataResource;
23  import io.github.tonywasher.joceanus.metis.data.MetisDataState;
24  import io.github.tonywasher.joceanus.metis.field.MetisFieldItem;
25  import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
26  import io.github.tonywasher.joceanus.metis.field.MetisFieldVersionedSet;
27  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePayeeInfo.MoneyWisePayeeInfoList;
28  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseAccountInfoClass;
29  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseAccountInfoType.MoneyWiseAccountInfoTypeList;
30  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWisePayeeClass;
31  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWisePayeeType;
32  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWisePayeeType.MoneyWisePayeeTypeList;
33  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseStaticDataType;
34  import io.github.tonywasher.joceanus.moneywise.exc.MoneyWiseDataException;
35  import io.github.tonywasher.joceanus.moneywise.exc.MoneyWiseLogicException;
36  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
37  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
38  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataInstanceMap;
39  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataItem;
40  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataMapItem;
41  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataResource;
42  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataValues;
43  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataValues.PrometheusInfoItem;
44  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataValues.PrometheusInfoSetItem;
45  import io.github.tonywasher.joceanus.prometheus.views.PrometheusEditSet;
46  
47  import java.util.HashMap;
48  import java.util.Iterator;
49  import java.util.Map;
50  
51  /**
52   * Payee class.
53   */
54  public class MoneyWisePayee
55          extends MoneyWiseAssetBase
56          implements PrometheusInfoSetItem {
57      /**
58       * Object name.
59       */
60      public static final String OBJECT_NAME = MoneyWiseBasicDataType.PAYEE.getItemName();
61  
62      /**
63       * List name.
64       */
65      public static final String LIST_NAME = MoneyWiseBasicDataType.PAYEE.getListName();
66  
67      /**
68       * Local Report fields.
69       */
70      private static final MetisFieldVersionedSet<MoneyWisePayee> FIELD_DEFS = MetisFieldVersionedSet.newVersionedFieldSet(MoneyWisePayee.class);
71  
72      /*
73       * FieldIds.
74       */
75      static {
76          FIELD_DEFS.declareLocalField(PrometheusDataResource.DATAINFOSET_NAME, MoneyWisePayee::getInfoSet);
77          FIELD_DEFS.buildFieldMap(MoneyWiseAccountInfoClass.class, MoneyWisePayee::getFieldValue);
78      }
79  
80      /**
81       * Do we have an InfoSet.
82       */
83      private final boolean hasInfoSet;
84  
85      /**
86       * Should we use infoSet for DataState etc.
87       */
88      private final boolean useInfoSet;
89  
90      /**
91       * PayeeInfoSet.
92       */
93      private final MoneyWisePayeeInfoSet theInfoSet;
94  
95      /**
96       * Copy Constructor.
97       *
98       * @param pList  the list
99       * @param pPayee The Payee to copy
100      */
101     protected MoneyWisePayee(final MoneyWisePayeeList pList,
102                              final MoneyWisePayee pPayee) {
103         /* Set standard values */
104         super(pList, pPayee);
105 
106         /* switch on list type */
107         switch (getList().getStyle()) {
108             case EDIT:
109                 theInfoSet = new MoneyWisePayeeInfoSet(this, pList.getActInfoTypes(), pList.getPayeeInfo());
110                 theInfoSet.cloneDataInfoSet(pPayee.getInfoSet());
111                 hasInfoSet = true;
112                 useInfoSet = true;
113                 break;
114             case CLONE, CORE:
115                 theInfoSet = new MoneyWisePayeeInfoSet(this, pList.getActInfoTypes(), pList.getPayeeInfo());
116                 hasInfoSet = true;
117                 useInfoSet = false;
118                 break;
119             default:
120                 theInfoSet = null;
121                 hasInfoSet = false;
122                 useInfoSet = false;
123                 break;
124         }
125     }
126 
127     /**
128      * Values constructor.
129      *
130      * @param pList   the List to add to
131      * @param pValues the values constructor
132      * @throws OceanusException on error
133      */
134     private MoneyWisePayee(final MoneyWisePayeeList pList,
135                            final PrometheusDataValues pValues) throws OceanusException {
136         /* Initialise the item */
137         super(pList, pValues);
138 
139         /* Create the InfoSet */
140         theInfoSet = new MoneyWisePayeeInfoSet(this, pList.getActInfoTypes(), pList.getPayeeInfo());
141         hasInfoSet = true;
142         useInfoSet = false;
143     }
144 
145     /**
146      * Edit Constructor.
147      *
148      * @param pList the list
149      */
150     public MoneyWisePayee(final MoneyWisePayeeList pList) {
151         super(pList);
152 
153         /* Build InfoSet */
154         theInfoSet = new MoneyWisePayeeInfoSet(this, pList.getActInfoTypes(), pList.getPayeeInfo());
155         hasInfoSet = true;
156         useInfoSet = true;
157     }
158 
159     @Override
160     public MetisFieldSetDef getDataFieldSet() {
161         return FIELD_DEFS;
162     }
163 
164     @Override
165     public boolean includeXmlField(final MetisDataFieldId pField) {
166         /* Determine whether fields should be included */
167         if (MoneyWiseBasicResource.CATEGORY_NAME.equals(pField)) {
168             return true;
169         }
170 
171         /* Pass call on */
172         return super.includeXmlField(pField);
173     }
174 
175     @Override
176     public Long getExternalId() {
177         return MoneyWiseAssetType.createExternalId(MoneyWiseAssetType.PAYEE, getIndexedId());
178     }
179 
180     @Override
181     public MoneyWisePayeeInfoSet getInfoSet() {
182         return theInfoSet;
183     }
184 
185     /**
186      * Obtain fieldValue for infoSet.
187      *
188      * @param pFieldId the fieldId
189      * @return the value
190      */
191     private Object getFieldValue(final MetisDataFieldId pFieldId) {
192         return theInfoSet != null ? theInfoSet.getFieldValue(pFieldId) : null;
193     }
194 
195     /**
196      * Obtain WebSite.
197      *
198      * @return the webSite
199      */
200     public char[] getWebSite() {
201         return hasInfoSet
202                 ? theInfoSet.getValue(MoneyWiseAccountInfoClass.WEBSITE, char[].class)
203                 : null;
204     }
205 
206     /**
207      * Obtain CustNo.
208      *
209      * @return the customer #
210      */
211     public char[] getCustNo() {
212         return hasInfoSet
213                 ? theInfoSet.getValue(MoneyWiseAccountInfoClass.CUSTOMERNO, char[].class)
214                 : null;
215     }
216 
217     /**
218      * Obtain UserId.
219      *
220      * @return the userId
221      */
222     public char[] getUserId() {
223         return hasInfoSet
224                 ? theInfoSet.getValue(MoneyWiseAccountInfoClass.USERID, char[].class)
225                 : null;
226     }
227 
228     /**
229      * Obtain Password.
230      *
231      * @return the password
232      */
233     public char[] getPassword() {
234         return hasInfoSet
235                 ? theInfoSet.getValue(MoneyWiseAccountInfoClass.PASSWORD, char[].class)
236                 : null;
237     }
238 
239     /**
240      * Obtain SortCode.
241      *
242      * @return the sort code
243      */
244     public char[] getSortCode() {
245         return hasInfoSet
246                 ? theInfoSet.getValue(MoneyWiseAccountInfoClass.SORTCODE, char[].class)
247                 : null;
248     }
249 
250     /**
251      * Obtain Reference.
252      *
253      * @return the reference
254      */
255     public char[] getReference() {
256         return hasInfoSet
257                 ? theInfoSet.getValue(MoneyWiseAccountInfoClass.REFERENCE, char[].class)
258                 : null;
259     }
260 
261     /**
262      * Obtain Account.
263      *
264      * @return the account
265      */
266     public char[] getAccount() {
267         return hasInfoSet
268                 ? theInfoSet.getValue(MoneyWiseAccountInfoClass.ACCOUNT, char[].class)
269                 : null;
270     }
271 
272     /**
273      * Obtain Notes.
274      *
275      * @return the notes
276      */
277     public char[] getNotes() {
278         return hasInfoSet
279                 ? theInfoSet.getValue(MoneyWiseAccountInfoClass.NOTES, char[].class)
280                 : null;
281     }
282 
283     @Override
284     public MoneyWisePayeeType getCategory() {
285         return getValues().getValue(MoneyWiseBasicResource.CATEGORY_NAME, MoneyWisePayeeType.class);
286     }
287 
288     /**
289      * Obtain categoryId.
290      *
291      * @return the categoryTypeId
292      */
293     public Integer getCategoryId() {
294         final MoneyWisePayeeType myType = getCategory();
295         return (myType == null)
296                 ? null
297                 : myType.getIndexedId();
298     }
299 
300     /**
301      * Obtain categoryName.
302      *
303      * @return the categoryName
304      */
305     public String getCategoryName() {
306         final MoneyWisePayeeType myType = getCategory();
307         return myType == null
308                 ? null
309                 : myType.getName();
310     }
311 
312     /**
313      * Obtain categoryClass.
314      *
315      * @return the categoryClass
316      */
317     public MoneyWisePayeeClass getCategoryClass() {
318         final MoneyWisePayeeType myType = getCategory();
319         return (myType == null)
320                 ? null
321                 : myType.getPayeeClass();
322     }
323 
324     @Override
325     public MoneyWisePayee getBase() {
326         return (MoneyWisePayee) super.getBase();
327     }
328 
329     @Override
330     public MoneyWisePayeeList getList() {
331         return (MoneyWisePayeeList) super.getList();
332     }
333 
334     @Override
335     public MetisDataState getState() {
336         /* Pop history for self */
337         MetisDataState myState = super.getState();
338 
339         /* If we should use the InfoSet */
340         if ((myState == MetisDataState.CLEAN) && useInfoSet) {
341             /* Get state for infoSet */
342             myState = theInfoSet.getState();
343         }
344 
345         /* Return the state */
346         return myState;
347     }
348 
349     @Override
350     public MetisDataEditState getEditState() {
351         /* Pop history for self */
352         MetisDataEditState myState = super.getEditState();
353 
354         /* If we should use the InfoSet */
355         if (myState == MetisDataEditState.CLEAN
356                 && useInfoSet) {
357             /* Get state for infoSet */
358             myState = theInfoSet.getEditState();
359         }
360 
361         /* Return the state */
362         return myState;
363     }
364 
365     @Override
366     public boolean hasHistory() {
367         /* Check for history for self */
368         boolean hasHistory = super.hasHistory();
369 
370         /* If we should use the InfoSet */
371         if (!hasHistory && useInfoSet) {
372             /* Check history for infoSet */
373             hasHistory = theInfoSet.hasHistory();
374         }
375 
376         /* Return details */
377         return hasHistory;
378     }
379 
380     @Override
381     public void pushHistory() {
382         /* Push history for self */
383         super.pushHistory();
384 
385         /* If we should use the InfoSet */
386         if (useInfoSet) {
387             /* Push history for infoSet */
388             theInfoSet.pushHistory();
389         }
390     }
391 
392     @Override
393     public void popHistory() {
394         /* Pop history for self */
395         super.popHistory();
396 
397         /* If we should use the InfoSet */
398         if (useInfoSet) {
399             /* Pop history for infoSet */
400             theInfoSet.popHistory();
401         }
402     }
403 
404     @Override
405     public boolean checkForHistory() {
406         /* Check for history for self */
407         boolean bChanges = super.checkForHistory();
408 
409         /* If we should use the InfoSet */
410         if (useInfoSet) {
411             /* Check for history for infoSet */
412             bChanges |= theInfoSet.checkForHistory();
413         }
414 
415         /* return result */
416         return bChanges;
417     }
418 
419     @Override
420     public MetisDataDifference fieldChanged(final MetisDataFieldId pField) {
421         /* Handle InfoSet fields */
422         final MoneyWiseAccountInfoClass myClass = MoneyWisePayeeInfoSet.getClassForField(pField);
423         if (myClass != null) {
424             return useInfoSet
425                     ? theInfoSet.fieldChanged(myClass)
426                     : MetisDataDifference.IDENTICAL;
427         }
428 
429         /* Check super fields */
430         return super.fieldChanged(pField);
431     }
432 
433     @Override
434     public void setDeleted(final boolean bDeleted) {
435         /* Pass call to infoSet if required */
436         if (useInfoSet) {
437             theInfoSet.setDeleted(bDeleted);
438         }
439 
440         /* Pass call onwards */
441         super.setDeleted(bDeleted);
442     }
443 
444     /**
445      * Is this payee the required class.
446      *
447      * @param pClass the required payee class.
448      * @return true/false
449      */
450     public boolean isPayeeClass(final MoneyWisePayeeClass pClass) {
451         /* Check for match */
452         return getCategoryClass() == pClass;
453     }
454 
455     /**
456      * Is the category hidden?
457      *
458      * @return true/false
459      */
460     @Override
461     public boolean isHidden() {
462         final MoneyWisePayeeClass myClass = this.getCategoryClass();
463         return myClass != null
464                 && myClass.isHiddenType();
465     }
466 
467     /**
468      * Set defaults.
469      *
470      * @throws OceanusException on error
471      */
472     public void setDefaults() throws OceanusException {
473         getList().getValidator().setDefaults(this);
474     }
475 
476     @Override
477     public int compareValues(final PrometheusDataItem pThat) {
478         /* Check the category and then the name */
479         final MoneyWisePayee myThat = (MoneyWisePayee) pThat;
480         int iDiff = MetisDataDifference.compareObject(getCategory(), myThat.getCategory());
481         if (iDiff == 0) {
482             iDiff = MetisDataDifference.compareObject(getName(), myThat.getName());
483         }
484         return iDiff;
485     }
486 
487     @Override
488     public void resolveDataSetLinks() throws OceanusException {
489         /* Update the Base details */
490         super.resolveDataSetLinks();
491 
492         /* Resolve data links */
493         final MoneyWiseDataSet myData = getDataSet();
494         resolveDataLink(MoneyWiseBasicResource.CATEGORY_NAME, myData.getPayeeTypes());
495     }
496 
497     @Override
498     protected void resolveEditSetLinks() throws OceanusException {
499         /* Access the editSet */
500         final PrometheusEditSet myEditSet = getList().getEditSet();
501 
502         /* Resolve Parent/Category/Currency if required */
503         if (myEditSet.hasDataType(MoneyWiseStaticDataType.PAYEETYPE)) {
504             resolveDataLink(MoneyWiseBasicResource.CATEGORY_NAME, myEditSet.getDataList(MoneyWiseStaticDataType.PAYEETYPE, MoneyWisePayeeTypeList.class));
505         }
506 
507         /* Resolve links in infoSet */
508         theInfoSet.resolveEditSetLinks(myEditSet);
509     }
510 
511     /**
512      * Set a new WebSite.
513      *
514      * @param pWebSite the new webSite
515      * @throws OceanusException on error
516      */
517     public void setWebSite(final char[] pWebSite) throws OceanusException {
518         setInfoSetValue(MoneyWiseAccountInfoClass.WEBSITE, pWebSite);
519     }
520 
521     /**
522      * Set a new CustNo.
523      *
524      * @param pCustNo the new custNo
525      * @throws OceanusException on error
526      */
527     public void setCustNo(final char[] pCustNo) throws OceanusException {
528         setInfoSetValue(MoneyWiseAccountInfoClass.CUSTOMERNO, pCustNo);
529     }
530 
531     /**
532      * Set a new UserId.
533      *
534      * @param pUserId the new userId
535      * @throws OceanusException on error
536      */
537     public void setUserId(final char[] pUserId) throws OceanusException {
538         setInfoSetValue(MoneyWiseAccountInfoClass.USERID, pUserId);
539     }
540 
541     /**
542      * Set a new Password.
543      *
544      * @param pPassword the new password
545      * @throws OceanusException on error
546      */
547     public void setPassword(final char[] pPassword) throws OceanusException {
548         setInfoSetValue(MoneyWiseAccountInfoClass.PASSWORD, pPassword);
549     }
550 
551     /**
552      * Set a new SortCode.
553      *
554      * @param pSortCode the new sort code
555      * @throws OceanusException on error
556      */
557     public void setSortCode(final char[] pSortCode) throws OceanusException {
558         setInfoSetValue(MoneyWiseAccountInfoClass.SORTCODE, pSortCode);
559     }
560 
561     /**
562      * Set a new Account.
563      *
564      * @param pAccount the new account
565      * @throws OceanusException on error
566      */
567     public void setAccount(final char[] pAccount) throws OceanusException {
568         setInfoSetValue(MoneyWiseAccountInfoClass.ACCOUNT, pAccount);
569     }
570 
571     /**
572      * Set a new Reference.
573      *
574      * @param pReference the new reference
575      * @throws OceanusException on error
576      */
577     public void setReference(final char[] pReference) throws OceanusException {
578         setInfoSetValue(MoneyWiseAccountInfoClass.REFERENCE, pReference);
579     }
580 
581     /**
582      * Set a new Notes.
583      *
584      * @param pNotes the new notes
585      * @throws OceanusException on error
586      */
587     public void setNotes(final char[] pNotes) throws OceanusException {
588         setInfoSetValue(MoneyWiseAccountInfoClass.NOTES, pNotes);
589     }
590 
591     /**
592      * Set an infoSet value.
593      *
594      * @param pInfoClass the class of info to set
595      * @param pValue     the value to set
596      * @throws OceanusException on error
597      */
598     private void setInfoSetValue(final MoneyWiseAccountInfoClass pInfoClass,
599                                  final Object pValue) throws OceanusException {
600         /* Reject if there is no infoSet */
601         if (!hasInfoSet) {
602             throw new MoneyWiseLogicException(ERROR_BADINFOSET);
603         }
604 
605         /* Set the value */
606         theInfoSet.setValue(pInfoClass, pValue);
607     }
608 
609     @Override
610     public void touchUnderlyingItems() {
611         /* touch the payee type */
612         getCategory().touchItem(this);
613 
614         /* touch infoSet items */
615         theInfoSet.touchUnderlyingItems();
616     }
617 
618     @Override
619     public void touchOnUpdate() {
620         /* Reset touches from update set */
621         clearTouches(MoneyWiseBasicDataType.CASHINFO);
622         clearTouches(MoneyWiseBasicDataType.SECURITY);
623         clearTouches(MoneyWiseBasicDataType.DEPOSIT);
624         clearTouches(MoneyWiseBasicDataType.LOAN);
625         clearTouches(MoneyWiseBasicDataType.PORTFOLIO);
626     }
627 
628     /**
629      * Update base payee from an edited payee.
630      *
631      * @param pPayee the edited payee
632      * @return whether changes have been made
633      */
634     @Override
635     public boolean applyChanges(final PrometheusDataItem pPayee) {
636         /* Can only update from a payee */
637         if (!(pPayee instanceof MoneyWisePayee myPayee)) {
638             return false;
639         }
640 
641         /* Store the current detail into history */
642         pushHistory();
643 
644         /* Apply basic changes */
645         applyBasicChanges(myPayee);
646 
647         /* Check for changes */
648         return checkForHistory();
649     }
650 
651     @Override
652     public void adjustMapForItem() {
653         final MoneyWisePayeeList myList = getList();
654         final MoneyWisePayeeDataMap myMap = myList.getDataMap();
655         myMap.adjustForItem(this);
656     }
657 
658     @Override
659     public void removeItem() {
660         theInfoSet.removeItems();
661         super.removeItem();
662     }
663 
664     /**
665      * The Payee List class.
666      */
667     public static class MoneyWisePayeeList
668             extends MoneyWiseAssetBaseList<MoneyWisePayee> {
669         /**
670          * Report fields.
671          */
672         private static final MetisFieldSet<MoneyWisePayeeList> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWisePayeeList.class);
673 
674         /**
675          * The PayeeInfo List.
676          */
677         private MoneyWisePayeeInfoList theInfoList;
678 
679         /**
680          * The AccountInfoType list.
681          */
682         private MoneyWiseAccountInfoTypeList theInfoTypeList;
683 
684         /**
685          * Construct an empty CORE Payee list.
686          *
687          * @param pData the DataSet for the list
688          */
689         public MoneyWisePayeeList(final MoneyWiseDataSet pData) {
690             super(pData, MoneyWisePayee.class, MoneyWiseBasicDataType.PAYEE);
691         }
692 
693         /**
694          * Constructor for a cloned List.
695          *
696          * @param pSource the source List
697          */
698         protected MoneyWisePayeeList(final MoneyWisePayeeList pSource) {
699             super(pSource);
700         }
701 
702         @Override
703         public MetisFieldSet<MoneyWisePayeeList> getDataFieldSet() {
704             return FIELD_DEFS;
705         }
706 
707         @Override
708         public String listName() {
709             return LIST_NAME;
710         }
711 
712         @Override
713         public MetisFieldSetDef getItemFields() {
714             return MoneyWisePayee.FIELD_DEFS;
715         }
716 
717         @Override
718         public MoneyWisePayeeDataMap getDataMap() {
719             return (MoneyWisePayeeDataMap) super.getDataMap();
720         }
721 
722         /**
723          * Obtain the payeeInfoList.
724          *
725          * @return the payee info list
726          */
727         public MoneyWisePayeeInfoList getPayeeInfo() {
728             if (theInfoList == null) {
729                 theInfoList = getDataSet().getPayeeInfo();
730             }
731             return theInfoList;
732         }
733 
734         /**
735          * Obtain the accountInfoTypeList.
736          *
737          * @return the account info type list
738          */
739         public MoneyWiseAccountInfoTypeList getActInfoTypes() {
740             if (theInfoTypeList == null) {
741                 theInfoTypeList = getEditSet() == null
742                         ? getDataSet().getActInfoTypes()
743                         : getEditSet().getDataList(MoneyWiseStaticDataType.ACCOUNTINFOTYPE, MoneyWiseAccountInfoTypeList.class);
744             }
745             return theInfoTypeList;
746         }
747 
748         @Override
749         protected MoneyWisePayeeList getEmptyList(final PrometheusListStyle pStyle) {
750             final MoneyWisePayeeList myList = new MoneyWisePayeeList(this);
751             myList.setStyle(pStyle);
752             return myList;
753         }
754 
755         /**
756          * Derive Edit list.
757          *
758          * @param pEditSet the editSet
759          * @return the edit list
760          * @throws OceanusException on error
761          */
762         public MoneyWisePayeeList deriveEditList(final PrometheusEditSet pEditSet) throws OceanusException {
763             /* Build an empty List */
764             final MoneyWisePayeeList myList = getEmptyList(PrometheusListStyle.EDIT);
765             myList.ensureMap();
766             pEditSet.setEditEntryList(MoneyWiseBasicDataType.PAYEE, myList);
767             myList.getValidator().setEditSet(pEditSet);
768 
769             /* Store InfoType list */
770             myList.theInfoTypeList = pEditSet.getDataList(MoneyWiseStaticDataType.ACCOUNTINFOTYPE, MoneyWiseAccountInfoTypeList.class);
771 
772             /* Create info List */
773             final MoneyWisePayeeInfoList myPayeeInfo = getPayeeInfo();
774             myList.theInfoList = myPayeeInfo.getEmptyList(PrometheusListStyle.EDIT);
775             pEditSet.setEditEntryList(MoneyWiseBasicDataType.PAYEEINFO, myList.theInfoList);
776 
777             /* Store the editSet */
778             myList.setEditSet(pEditSet);
779 
780             /* Loop through the payees */
781             final Iterator<MoneyWisePayee> myIterator = iterator();
782             while (myIterator.hasNext()) {
783                 final MoneyWisePayee myCurr = myIterator.next();
784 
785                 /* Ignore deleted payees */
786                 if (myCurr.isDeleted()) {
787                     continue;
788                 }
789 
790                 /* Build the new linked payee and add it to the list */
791                 final MoneyWisePayee myPayee = new MoneyWisePayee(myList, myCurr);
792                 myList.add(myPayee);
793                 myPayee.resolveEditSetLinks();
794 
795                 /* Adjust the map */
796                 myPayee.adjustMapForItem();
797             }
798 
799             /* Return the list */
800             return myList;
801         }
802 
803         @Override
804         public MoneyWisePayee findItemByName(final String pName) {
805             /* Look up in map */
806             return getDataMap().findItemByName(pName);
807         }
808 
809         @Override
810         public boolean checkAvailableName(final String pName) {
811             /* check availability */
812             return findItemByName(pName) == null;
813         }
814 
815         @Override
816         public boolean validNameCount(final String pName) {
817             /* check availability in map */
818             return getDataMap().validNameCount(pName);
819         }
820 
821         /**
822          * Add a new item to the core list.
823          *
824          * @param pPayee item
825          * @return the newly added item
826          */
827         @Override
828         public MoneyWisePayee addCopyItem(final PrometheusDataItem pPayee) {
829             /* Can only clone a Payee */
830             if (!(pPayee instanceof MoneyWisePayee)) {
831                 throw new UnsupportedOperationException();
832             }
833 
834             final MoneyWisePayee myPayee = new MoneyWisePayee(this, (MoneyWisePayee) pPayee);
835             add(myPayee);
836             return myPayee;
837         }
838 
839         /**
840          * Add a new item to the edit list.
841          *
842          * @return the new item
843          */
844         @Override
845         public MoneyWisePayee addNewItem() {
846             final MoneyWisePayee myPayee = new MoneyWisePayee(this);
847             add(myPayee);
848             return myPayee;
849         }
850 
851         /**
852          * Obtain the first payee for the specified class.
853          *
854          * @param pClass the payee class
855          * @return the payee
856          */
857         public MoneyWisePayee getSingularClass(final MoneyWisePayeeClass pClass) {
858             /* Lookup in the map */
859             return getDataMap().findSingularItem(pClass);
860         }
861 
862         @Override
863         public MoneyWisePayee addValuesItem(final PrometheusDataValues pValues) throws OceanusException {
864             /* Create the payee */
865             final MoneyWisePayee myPayee = new MoneyWisePayee(this, pValues);
866 
867             /* Check that this PayeeId has not been previously added */
868             if (!isIdUnique(myPayee.getIndexedId())) {
869                 myPayee.addError(ERROR_DUPLICATE, MetisDataResource.DATA_ID);
870                 throw new MoneyWiseDataException(myPayee, ERROR_VALIDATION);
871             }
872 
873             /* Add to the list */
874             add(myPayee);
875 
876             /* Loop through the info items */
877             if (pValues.hasInfoItems()) {
878                 /* Loop through the items */
879                 final Iterator<PrometheusInfoItem> myIterator = pValues.infoIterator();
880                 while (myIterator.hasNext()) {
881                     final PrometheusInfoItem myItem = myIterator.next();
882 
883                     /* Build info */
884                     final PrometheusDataValues myValues = myItem.getValues(myPayee);
885                     theInfoList.addValuesItem(myValues);
886                 }
887             }
888 
889             /* Return it */
890             return myPayee;
891         }
892 
893         @Override
894         protected MoneyWisePayeeDataMap allocateDataMap() {
895             return new MoneyWisePayeeDataMap();
896         }
897 
898         @Override
899         public void postProcessOnLoad() throws OceanusException {
900             /* Resolve links and sort the data */
901             super.resolveDataSetLinks();
902             reSort();
903         }
904     }
905 
906     /**
907      * The dataMap class.
908      */
909     public static class MoneyWisePayeeDataMap
910             implements PrometheusDataMapItem, MetisFieldItem {
911         /**
912          * Report fields.
913          */
914         private static final MetisFieldSet<MoneyWisePayeeDataMap> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWisePayeeDataMap.class);
915 
916         /*
917          * UnderlyingMap Field Id.
918          */
919         static {
920             FIELD_DEFS.declareLocalField(MoneyWiseBasicResource.MONEYWISEDATA_MAP_UNDERLYING, MoneyWisePayeeDataMap::getUnderlyingMap);
921             FIELD_DEFS.declareLocalField(MoneyWiseBasicResource.MONEYWISEDATA_MAP_SINGULARMAP, MoneyWisePayeeDataMap::getPayeeMap);
922             FIELD_DEFS.declareLocalField(MoneyWiseBasicResource.MONEYWISEDATA_MAP_SINGULARCOUNTS, MoneyWisePayeeDataMap::getPayeeCountMap);
923         }
924 
925         /**
926          * The assetMap.
927          */
928         private final MoneyWiseAssetDataMap theUnderlyingMap;
929 
930         /**
931          * Map of category counts.
932          */
933         private final Map<Integer, Integer> thePayeeCountMap;
934 
935         /**
936          * Map of singular categories.
937          */
938         private final Map<Integer, MoneyWisePayee> thePayeeMap;
939 
940         /**
941          * Constructor.
942          */
943         public MoneyWisePayeeDataMap() {
944             /* Create underlying map */
945             theUnderlyingMap = new MoneyWiseAssetDataMap();
946 
947             /* Create the maps */
948             thePayeeCountMap = new HashMap<>();
949             thePayeeMap = new HashMap<>();
950         }
951 
952         @Override
953         public MetisFieldSet<MoneyWisePayeeDataMap> getDataFieldSet() {
954             return FIELD_DEFS;
955         }
956 
957         @Override
958         public String formatObject(final OceanusDataFormatter pFormatter) {
959             return FIELD_DEFS.getName();
960         }
961 
962         /**
963          * Obtain the underlying map.
964          *
965          * @return the underlying map
966          */
967         public MoneyWiseAssetDataMap getUnderlyingMap() {
968             return theUnderlyingMap;
969         }
970 
971         /**
972          * Obtain the underlying map.
973          *
974          * @return the underlying map
975          */
976         private Map<Integer, MoneyWisePayee> getPayeeMap() {
977             return thePayeeMap;
978         }
979 
980         /**
981          * Obtain the underlying map.
982          *
983          * @return the underlying map
984          */
985         private Map<Integer, Integer> getPayeeCountMap() {
986             return thePayeeCountMap;
987         }
988 
989         @Override
990         public void resetMap() {
991             theUnderlyingMap.resetMap();
992             thePayeeCountMap.clear();
993             thePayeeMap.clear();
994         }
995 
996         @Override
997         public void adjustForItem(final PrometheusDataItem pItem) {
998             /* Access item */
999             final MoneyWisePayee myItem = (MoneyWisePayee) pItem;
1000 
1001             /* If the class is singular */
1002             final MoneyWisePayeeClass myClass = myItem.getCategoryClass();
1003             if (myClass.isSingular()) {
1004                 /* Adjust category count */
1005                 final Integer myId = myClass.getClassId();
1006                 final Integer myCount = thePayeeCountMap.get(myId);
1007                 if (myCount == null) {
1008                     thePayeeCountMap.put(myId, PrometheusDataInstanceMap.ONE);
1009                 } else {
1010                     thePayeeCountMap.put(myId, myCount + 1);
1011                 }
1012 
1013                 /* Adjust payee map */
1014                 thePayeeMap.put(myId, myItem);
1015             }
1016 
1017             /* Adjust name count */
1018             theUnderlyingMap.adjustForItem(pItem);
1019         }
1020 
1021         /**
1022          * find item by name.
1023          *
1024          * @param pName the name to look up
1025          * @return the matching item
1026          */
1027         public MoneyWisePayee findItemByName(final String pName) {
1028             final MoneyWiseAssetBase myAsset = theUnderlyingMap.findAssetByName(pName);
1029             return myAsset instanceof MoneyWisePayee myPayee
1030                     ? myPayee
1031                     : null;
1032         }
1033 
1034         /**
1035          * Check validity of name.
1036          *
1037          * @param pName the name to look up
1038          * @return true/false
1039          */
1040         public boolean validNameCount(final String pName) {
1041             return theUnderlyingMap.validKeyCount(pName);
1042         }
1043 
1044         /**
1045          * Check availability of name.
1046          *
1047          * @param pName the key to look up
1048          * @return true/false
1049          */
1050         public boolean availableName(final String pName) {
1051             return theUnderlyingMap.availableKey(pName);
1052         }
1053 
1054         /**
1055          * find singular item.
1056          *
1057          * @param pClass the class to look up
1058          * @return the matching item
1059          */
1060         public MoneyWisePayee findSingularItem(final MoneyWisePayeeClass pClass) {
1061             return thePayeeMap.get(pClass.getClassId());
1062         }
1063 
1064         /**
1065          * Check validity of singular count.
1066          *
1067          * @param pClass the class to look up
1068          * @return true/false
1069          */
1070         public boolean validSingularCount(final MoneyWisePayeeClass pClass) {
1071             final Integer myResult = thePayeeCountMap.get(pClass.getClassId());
1072             return PrometheusDataInstanceMap.ONE.equals(myResult);
1073         }
1074     }
1075 }