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