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.MetisFieldSet;
25  import io.github.tonywasher.joceanus.metis.field.MetisFieldVersionedSet;
26  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseAssetBase.MoneyWiseAssetBaseList;
27  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseAssetBase.MoneyWiseDataSetCtl;
28  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTax.MoneyWiseTaxCredit;
29  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTax.MoneyWiseTaxFactory;
30  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransCategory.MoneyWiseTransCategoryList;
31  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransInfo.MoneyWiseTransInfoList;
32  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseStaticDataType;
33  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransInfoClass;
34  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransInfoType.MoneyWiseTransInfoTypeList;
35  import io.github.tonywasher.joceanus.moneywise.exc.MoneyWiseDataException;
36  import io.github.tonywasher.joceanus.moneywise.exc.MoneyWiseLogicException;
37  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
38  import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
39  import io.github.tonywasher.joceanus.oceanus.date.OceanusDateFormatter;
40  import io.github.tonywasher.joceanus.oceanus.date.OceanusDateRange;
41  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusMoney;
42  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusPrice;
43  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRate;
44  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRatio;
45  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusUnits;
46  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
47  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataItem;
48  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataList;
49  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataMapItem;
50  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataResource;
51  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataSet;
52  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataValues;
53  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataValues.PrometheusInfoItem;
54  import io.github.tonywasher.joceanus.prometheus.data.PrometheusEncrypted.PrometheusDataInfoSetItemCtl;
55  import io.github.tonywasher.joceanus.prometheus.data.PrometheusListStyle;
56  import io.github.tonywasher.joceanus.prometheus.views.PrometheusEditEntry;
57  import io.github.tonywasher.joceanus.prometheus.views.PrometheusEditSet;
58  
59  import java.util.Iterator;
60  import java.util.List;
61  
62  /**
63   * New version of Event DataItem utilising EventInfo.
64   *
65   * @author Tony Washer
66   */
67  public class MoneyWiseTransaction
68          extends MoneyWiseTransBase
69          implements PrometheusDataInfoSetItemCtl<MoneyWiseTransInfo> {
70      /**
71       * The name of the object.
72       */
73      public static final String OBJECT_NAME = MoneyWiseBasicDataType.TRANSACTION.getItemName();
74  
75      /**
76       * The name of the object.
77       */
78      public static final String LIST_NAME = MoneyWiseBasicDataType.TRANSACTION.getListName();
79  
80      /**
81       * Local Report fields.
82       */
83      private static final MetisFieldVersionedSet<MoneyWiseTransaction> FIELD_DEFS = MetisFieldVersionedSet.newVersionedFieldSet(MoneyWiseTransaction.class);
84  
85      /*
86       * FieldIds.
87       */
88      static {
89          FIELD_DEFS.declareDateField(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_DATE);
90          FIELD_DEFS.declareDerivedVersionedField(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_TAXYEAR);
91          FIELD_DEFS.declareLocalField(PrometheusDataResource.DATAINFOSET_NAME, MoneyWiseTransaction::getInfoSet);
92          FIELD_DEFS.buildFieldMap(MoneyWiseTransInfoClass.class, MoneyWiseTransaction::getFieldValue);
93      }
94  
95      /**
96       * Bad InfoSet Error Text.
97       */
98      private static final String ERROR_BADINFOSET = PrometheusDataResource.DATAINFOSET_ERROR_BADSET.getValue();
99  
100     /**
101      * Circular update Error Text.
102      */
103     public static final String ERROR_CIRCULAR = MoneyWiseBasicResource.TRANSACTION_ERROR_CIRCLE.getValue();
104 
105     /**
106      * Do we have an InfoSet.
107      */
108     private final boolean hasInfoSet;
109 
110     /**
111      * Should we use infoSet for DataState etc.
112      */
113     private final boolean useInfoSet;
114 
115     /**
116      * TransactionInfoSet.
117      */
118     private final MoneyWiseTransInfoSet theInfoSet;
119 
120     /**
121      * Copy Constructor.
122      *
123      * @param pList        the transaction list
124      * @param pTransaction The Transaction to copy
125      */
126     public MoneyWiseTransaction(final MoneyWiseTransactionList pList,
127                                 final MoneyWiseTransaction pTransaction) {
128         /* Set standard values */
129         super(pList, pTransaction);
130 
131         /* switch on list type */
132         switch (getList().getStyle()) {
133             case EDIT:
134                 theInfoSet = new MoneyWiseTransInfoSet(this, pList.getTransInfoTypes(), pList.getTransactionInfo());
135                 theInfoSet.cloneDataInfoSet(pTransaction.getInfoSet());
136                 hasInfoSet = true;
137                 useInfoSet = true;
138                 break;
139             case CLONE:
140             case CORE:
141                 theInfoSet = new MoneyWiseTransInfoSet(this, pList.getTransInfoTypes(), pList.getTransactionInfo());
142                 hasInfoSet = true;
143                 useInfoSet = false;
144                 break;
145             default:
146                 theInfoSet = null;
147                 hasInfoSet = false;
148                 useInfoSet = false;
149                 break;
150         }
151     }
152 
153     /**
154      * Edit constructor.
155      *
156      * @param pList the list
157      */
158     public MoneyWiseTransaction(final MoneyWiseTransactionList pList) {
159         super(pList);
160 
161         /* Build InfoSet */
162         theInfoSet = new MoneyWiseTransInfoSet(this, pList.getTransInfoTypes(), pList.getTransactionInfo());
163         hasInfoSet = true;
164         useInfoSet = true;
165     }
166 
167     /**
168      * Values constructor.
169      *
170      * @param pList   the List to add to
171      * @param pValues the values
172      * @throws OceanusException on error
173      */
174     private MoneyWiseTransaction(final MoneyWiseTransactionList pList,
175                                  final PrometheusDataValues pValues) throws OceanusException {
176         /* Initialise the item */
177         super(pList, pValues);
178 
179         /* Access formatter */
180         final OceanusDataFormatter myFormatter = getDataSet().getDataFormatter();
181 
182         /* Protect against exceptions */
183         try {
184             /* Store the Date */
185             final Object myValue = pValues.getValue(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_DATE);
186             if (myValue instanceof OceanusDate d) {
187                 setValueDate(d);
188             } else if (myValue instanceof String s) {
189                 final OceanusDateFormatter myParser = myFormatter.getDateFormatter();
190                 setValueDate(myParser.parseDate(s));
191             }
192             /* Catch Exceptions */
193         } catch (IllegalArgumentException e) {
194             /* Pass on exception */
195             throw new MoneyWiseDataException(this, ERROR_CREATEITEM, e);
196         }
197 
198         /* Create the InfoSet */
199         theInfoSet = new MoneyWiseTransInfoSet(this, pList.getTransInfoTypes(), pList.getTransactionInfo());
200         hasInfoSet = true;
201         useInfoSet = false;
202     }
203 
204     @Override
205     public MetisFieldSetDef getDataFieldSet() {
206         return FIELD_DEFS;
207     }
208 
209     @Override
210     public boolean includeXmlField(final MetisDataFieldId pField) {
211         /* Determine whether fields should be included */
212         if (MoneyWiseBasicResource.MONEYWISEDATA_FIELD_DATE.equals(pField)) {
213             return true;
214         }
215         if (MoneyWiseBasicResource.MONEYWISEDATA_FIELD_TAXYEAR.equals(pField)) {
216             return false;
217         }
218 
219         /* Pass call on */
220         return super.includeXmlField(pField);
221     }
222 
223     @Override
224     public OceanusDate getDate() {
225         return getValues().getValue(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_DATE, OceanusDate.class);
226     }
227 
228     /**
229      * Set date value.
230      *
231      * @param pValue the value
232      */
233     private void setValueDate(final OceanusDate pValue) {
234         getValues().setUncheckedValue(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_DATE, pValue);
235         if (getDataSet() instanceof MoneyWiseDataSetCtl myControl) {
236             final MoneyWiseTaxFactory myFactory = myControl.getTaxFactory();
237             setValueTaxYear(myFactory.findTaxYearForDate(pValue));
238         }
239     }
240 
241     /**
242      * Obtain TaxYear.
243      *
244      * @return the taxYear
245      */
246     public MoneyWiseTaxCredit getTaxYear() {
247         return getValues().getValue(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_TAXYEAR, MoneyWiseTaxCredit.class);
248     }
249 
250     /**
251      * Set taxYear value.
252      *
253      * @param pValue the value
254      */
255     private void setValueTaxYear(final MoneyWiseTaxCredit pValue) {
256         getValues().setUncheckedValue(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_TAXYEAR, pValue);
257     }
258 
259     @Override
260     public MoneyWiseTransInfoSet getInfoSet() {
261         return theInfoSet;
262     }
263 
264     /**
265      * Obtain fieldValue for infoSet.
266      *
267      * @param pFieldId the fieldId
268      * @return the value
269      */
270     private Object getFieldValue(final MetisDataFieldId pFieldId) {
271         return theInfoSet != null ? theInfoSet.getFieldValue(pFieldId) : null;
272     }
273 
274     /**
275      * Obtain Account Delta Units.
276      *
277      * @return the Account Delta Units
278      */
279     public final OceanusUnits getAccountDeltaUnits() {
280         return hasInfoSet
281                 ? theInfoSet.getValue(MoneyWiseTransInfoClass.ACCOUNTDELTAUNITS, OceanusUnits.class)
282                 : null;
283     }
284 
285     /**
286      * Obtain Partner Delta Units.
287      *
288      * @return the Partner Delta Units
289      */
290     public final OceanusUnits getPartnerDeltaUnits() {
291         return hasInfoSet
292                 ? theInfoSet.getValue(MoneyWiseTransInfoClass.PARTNERDELTAUNITS, OceanusUnits.class)
293                 : null;
294     }
295 
296     /**
297      * Obtain Tax Credit.
298      *
299      * @return the Tax Credit
300      */
301     public final OceanusMoney getTaxCredit() {
302         return hasInfoSet
303                 ? theInfoSet.getValue(MoneyWiseTransInfoClass.TAXCREDIT, OceanusMoney.class)
304                 : null;
305     }
306 
307     /**
308      * Obtain Price.
309      *
310      * @return the Price
311      */
312     public final OceanusPrice getPrice() {
313         return hasInfoSet
314                 ? theInfoSet.getValue(MoneyWiseTransInfoClass.PRICE, OceanusPrice.class)
315                 : null;
316     }
317 
318     /**
319      * Obtain Commission.
320      *
321      * @return the Commission
322      */
323     public final OceanusMoney getCommission() {
324         return hasInfoSet
325                 ? theInfoSet.getValue(MoneyWiseTransInfoClass.COMMISSION, OceanusMoney.class)
326                 : null;
327     }
328 
329     /**
330      * Obtain Dilution.
331      *
332      * @return the Dilution
333      */
334     public final OceanusRatio getDilution() {
335         return hasInfoSet
336                 ? theInfoSet.getValue(MoneyWiseTransInfoClass.DILUTION, OceanusRatio.class)
337                 : null;
338     }
339 
340     /**
341      * Obtain Qualifying Years.
342      *
343      * @return the Years
344      */
345     public final Integer getYears() {
346         return hasInfoSet
347                 ? theInfoSet.getValue(MoneyWiseTransInfoClass.QUALIFYYEARS, Integer.class)
348                 : null;
349     }
350 
351     /**
352      * Obtain Employer National Insurance.
353      *
354      * @return the NatInsurance
355      */
356     public final OceanusMoney getEmployerNatIns() {
357         return hasInfoSet
358                 ? theInfoSet.getValue(MoneyWiseTransInfoClass.EMPLOYERNATINS, OceanusMoney.class)
359                 : null;
360     }
361 
362     /**
363      * Obtain Employee National Insurance.
364      *
365      * @return the NatInsurance
366      */
367     public final OceanusMoney getEmployeeNatIns() {
368         return hasInfoSet
369                 ? theInfoSet.getValue(MoneyWiseTransInfoClass.EMPLOYEENATINS, OceanusMoney.class)
370                 : null;
371     }
372 
373     /**
374      * Obtain Deemed Benefit.
375      *
376      * @return the Benefit
377      */
378     public final OceanusMoney getDeemedBenefit() {
379         return hasInfoSet
380                 ? theInfoSet.getValue(MoneyWiseTransInfoClass.DEEMEDBENEFIT, OceanusMoney.class)
381                 : null;
382     }
383 
384     /**
385      * Obtain Withheld amount.
386      *
387      * @return the Withheld
388      */
389     public final OceanusMoney getWithheld() {
390         return hasInfoSet
391                 ? theInfoSet.getValue(MoneyWiseTransInfoClass.WITHHELD, OceanusMoney.class)
392                 : null;
393     }
394 
395     /**
396      * Obtain Reference.
397      *
398      * @return the Reference
399      */
400     public final String getReference() {
401         return hasInfoSet
402                 ? theInfoSet.getValue(MoneyWiseTransInfoClass.REFERENCE, String.class)
403                 : null;
404     }
405 
406     /**
407      * Obtain Comments.
408      *
409      * @return the Comments
410      */
411     public final String getComments() {
412         return hasInfoSet
413                 ? theInfoSet.getValue(MoneyWiseTransInfoClass.COMMENTS, String.class)
414                 : null;
415     }
416 
417     /**
418      * Obtain ReturnedCash Account.
419      *
420      * @return the ReturnedCash
421      */
422     public final MoneyWiseTransAsset getReturnedCashAccount() {
423         return hasInfoSet
424                 ? theInfoSet.getTransAsset(MoneyWiseTransInfoClass.RETURNEDCASHACCOUNT)
425                 : null;
426     }
427 
428     /**
429      * Obtain Partner Amount.
430      *
431      * @return the Partner Amount
432      */
433     public final OceanusMoney getPartnerAmount() {
434         return hasInfoSet
435                 ? theInfoSet.getValue(MoneyWiseTransInfoClass.PARTNERAMOUNT, OceanusMoney.class)
436                 : null;
437     }
438 
439     /**
440      * Obtain ExchangeRate.
441      *
442      * @return the ExchangeRate
443      */
444     public final OceanusRatio getExchangeRate() {
445         return hasInfoSet
446                 ? theInfoSet.getValue(MoneyWiseTransInfoClass.XCHANGERATE, OceanusRatio.class)
447                 : null;
448     }
449 
450     /**
451      * Obtain ReturnedCash.
452      *
453      * @return the ReturnedCash
454      */
455     public final OceanusMoney getReturnedCash() {
456         return hasInfoSet
457                 ? theInfoSet.getValue(MoneyWiseTransInfoClass.RETURNEDCASH, OceanusMoney.class)
458                 : null;
459     }
460 
461     /**
462      * Obtain the Transaction tagList.
463      *
464      * @return the list of transaction tags
465      */
466     @SuppressWarnings("unchecked")
467     public List<MoneyWiseTransTag> getTransactionTags() {
468         return hasInfoSet
469                 ? (List<MoneyWiseTransTag>) theInfoSet.getListValue(MoneyWiseTransInfoClass.TRANSTAG)
470                 : null;
471     }
472 
473     @Override
474     public MetisDataState getState() {
475         /* Pop history for self */
476         MetisDataState myState = super.getState();
477 
478         /* If we should use the InfoSet */
479         if ((myState == MetisDataState.CLEAN) && useInfoSet) {
480             /* Get state for infoSet */
481             myState = theInfoSet.getState();
482         }
483 
484         /* Return the state */
485         return myState;
486     }
487 
488     @Override
489     public MetisDataEditState getEditState() {
490         /* Pop history for self */
491         MetisDataEditState myState = super.getEditState();
492 
493         /* If we should use the InfoSet */
494         if (myState == MetisDataEditState.CLEAN
495                 && useInfoSet) {
496             /* Get state for infoSet */
497             myState = theInfoSet.getEditState();
498         }
499 
500         /* Return the state */
501         return myState;
502     }
503 
504     @Override
505     public boolean hasHistory() {
506         /* Check for history for self */
507         boolean hasHistory = super.hasHistory();
508 
509         /* If we should use the InfoSet */
510         if (!hasHistory && useInfoSet) {
511             /* Check history for infoSet */
512             hasHistory = theInfoSet.hasHistory();
513         }
514 
515         /* Return details */
516         return hasHistory;
517     }
518 
519     @Override
520     public void pushHistory() {
521         /* Push history for self */
522         super.pushHistory();
523 
524         /* If we should use the InfoSet */
525         if (useInfoSet) {
526             /* Push history for infoSet */
527             theInfoSet.pushHistory();
528         }
529     }
530 
531     @Override
532     public void popHistory() {
533         /* Pop history for self */
534         super.popHistory();
535 
536         /* If we should use the InfoSet */
537         if (useInfoSet) {
538             /* Pop history for infoSet */
539             theInfoSet.popHistory();
540         }
541     }
542 
543     @Override
544     public boolean checkForHistory() {
545         /* Check for history for self */
546         boolean bChanges = super.checkForHistory();
547 
548         /* If we should use the InfoSet */
549         if (useInfoSet) {
550             /* Check for history for infoSet */
551             bChanges |= theInfoSet.checkForHistory();
552         }
553 
554         /* return result */
555         return bChanges;
556     }
557 
558     @Override
559     public MetisDataDifference fieldChanged(final MetisDataFieldId pField) {
560         /* Handle InfoSet fields */
561         final MoneyWiseTransInfoClass myClass = MoneyWiseTransInfoSet.getClassForField(pField);
562         if (myClass != null) {
563             return useInfoSet
564                     ? theInfoSet.fieldChanged(myClass)
565                     : MetisDataDifference.IDENTICAL;
566         }
567 
568         /* Check super fields */
569         return super.fieldChanged(pField);
570     }
571 
572     @Override
573     public void setDeleted(final boolean bDeleted) {
574         /* Pass call to infoSet if required */
575         if (useInfoSet) {
576             theInfoSet.setDeleted(bDeleted);
577         }
578 
579         /* Pass call onwards */
580         super.setDeleted(bDeleted);
581     }
582 
583     @Override
584     public boolean isLocked() {
585         /* Check credit/debit */
586         if (super.isLocked()) {
587             return true;
588         }
589 
590         /* Check ReturnedCash Account */
591         final MoneyWiseTransAsset myReturnedCash = getReturnedCashAccount();
592         return myReturnedCash != null && myReturnedCash.isClosed();
593     }
594 
595     @Override
596     public MoneyWiseTransaction getBase() {
597         return (MoneyWiseTransaction) super.getBase();
598     }
599 
600     @Override
601     public MoneyWiseTransactionList getList() {
602         return (MoneyWiseTransactionList) super.getList();
603     }
604 
605     /**
606      * Compare this event to another to establish sort order.
607      *
608      * @param pThat The Event to compare to
609      * @return (-1,0,1) depending of whether this object is before, equal, or after the passed
610      * object in the sort order
611      */
612     @Override
613     public int compareValues(final PrometheusDataItem pThat) {
614         /* Handle the trivial cases */
615         final MoneyWiseTransaction myThat = (MoneyWiseTransaction) pThat;
616 
617         /* If header settings differ */
618         if (isHeader() != pThat.isHeader()) {
619             return isHeader()
620                     ? -1
621                     : 1;
622         }
623 
624         /* If the dates differ */
625         final int iDiff = MetisDataDifference.compareObject(getDate(), myThat.getDate());
626         if (iDiff != 0) {
627             return iDiff;
628         }
629 
630         /* Compare the underlying details */
631         return super.compareValues(pThat);
632     }
633 
634     @Override
635     public void resolveDataSetLinks() throws OceanusException {
636         /* Update the Transaction details */
637         super.resolveDataSetLinks();
638 
639         /* Sort linkSets */
640         if (hasInfoSet) {
641             theInfoSet.sortLinkSets();
642         }
643     }
644 
645     /**
646      * resolve editSet links.
647      *
648      * @throws OceanusException on error
649      */
650     public void resolveEditSetLinks() throws OceanusException {
651         /* Access the editSet */
652         final PrometheusEditSet myEditSet = getList().getEditSet();
653 
654         /* Resolve data links */
655         resolveTransactionAsset(myEditSet, this, MoneyWiseBasicResource.TRANSACTION_ACCOUNT);
656         resolveTransactionAsset(myEditSet, this, MoneyWiseBasicResource.TRANSACTION_PARTNER);
657         resolveDataLink(MoneyWiseBasicDataType.TRANSCATEGORY, myEditSet.getDataList(MoneyWiseBasicDataType.TRANSCATEGORY, MoneyWiseTransCategoryList.class));
658 
659         /* Resolve links in infoSet */
660         theInfoSet.resolveEditSetLinks(myEditSet);
661     }
662 
663     /**
664      * Calculate the tax credit for a transaction.
665      *
666      * @return the calculated tax credit
667      */
668     public final OceanusMoney calculateTaxCredit() {
669         final MoneyWiseTaxCredit myTax = getTaxYear();
670         final OceanusMoney myCredit = getTaxCredit();
671 
672         /* Ignore unless tax credit is null/zero */
673         if (myCredit != null
674                 && myCredit.isNonZero()) {
675             return myCredit;
676         }
677 
678         /* Ignore unless category is interest/dividend */
679         if ((getCategory() == null)
680                 || (!isInterest()
681                 && !isDividend())) {
682             return myCredit;
683         }
684 
685         /* Determine the tax credit rate */
686         final OceanusRate myRate = isInterest()
687                 ? myTax.getTaxCreditRateForInterest()
688                 : myTax.getTaxCreditRateForDividend();
689 
690         /* Calculate the tax credit */
691         return getAmount().taxCreditAtRate(myRate);
692     }
693 
694     /**
695      * Set a new date.
696      *
697      * @param pDate the new date
698      */
699     public void setDate(final OceanusDate pDate) {
700         setValueDate((pDate == null)
701                 ? null
702                 : new OceanusDate(pDate));
703     }
704 
705     /**
706      * Set a new AccountDeltaUnits.
707      *
708      * @param pUnits the new units
709      * @throws OceanusException on error
710      */
711     public final void setAccountDeltaUnits(final OceanusUnits pUnits) throws OceanusException {
712         setInfoSetValue(MoneyWiseTransInfoClass.ACCOUNTDELTAUNITS, pUnits);
713     }
714 
715     /**
716      * Set a new PartnerDeltaUnits.
717      *
718      * @param pUnits the new units
719      * @throws OceanusException on error
720      */
721     public final void setPartnerDeltaUnits(final OceanusUnits pUnits) throws OceanusException {
722         setInfoSetValue(MoneyWiseTransInfoClass.PARTNERDELTAUNITS, pUnits);
723     }
724 
725     /**
726      * Set a new TaxCredit.
727      *
728      * @param pCredit the new credit
729      * @throws OceanusException on error
730      */
731     public final void setTaxCredit(final OceanusMoney pCredit) throws OceanusException {
732         setInfoSetValue(MoneyWiseTransInfoClass.TAXCREDIT, pCredit);
733     }
734 
735     /**
736      * Set a new Price.
737      *
738      * @param pPrice the new price
739      * @throws OceanusException on error
740      */
741     public final void setPrice(final OceanusPrice pPrice) throws OceanusException {
742         setInfoSetValue(MoneyWiseTransInfoClass.PRICE, pPrice);
743     }
744 
745     /**
746      * Set a new Commission.
747      *
748      * @param pCommission the new commission
749      * @throws OceanusException on error
750      */
751     public final void setCommission(final OceanusMoney pCommission) throws OceanusException {
752         setInfoSetValue(MoneyWiseTransInfoClass.COMMISSION, pCommission);
753     }
754 
755     /**
756      * Set a new Dilution.
757      *
758      * @param pDilution the new dilution
759      * @throws OceanusException on error
760      */
761     public final void setDilution(final OceanusRatio pDilution) throws OceanusException {
762         setInfoSetValue(MoneyWiseTransInfoClass.DILUTION, pDilution);
763     }
764 
765     /**
766      * Set a new Qualifying Years.
767      *
768      * @param pYears the new years
769      * @throws OceanusException on error
770      */
771     public final void setYears(final Integer pYears) throws OceanusException {
772         setInfoSetValue(MoneyWiseTransInfoClass.QUALIFYYEARS, pYears);
773     }
774 
775     /**
776      * Set a new Employer NatInsurance.
777      *
778      * @param pNatIns the new insurance
779      * @throws OceanusException on error
780      */
781     public final void setEmployerNatIns(final OceanusMoney pNatIns) throws OceanusException {
782         setInfoSetValue(MoneyWiseTransInfoClass.EMPLOYERNATINS, pNatIns);
783     }
784 
785     /**
786      * Set a new Employee NatInsurance.
787      *
788      * @param pNatIns the new insurance
789      * @throws OceanusException on error
790      */
791     public final void setEmployeeNatIns(final OceanusMoney pNatIns) throws OceanusException {
792         setInfoSetValue(MoneyWiseTransInfoClass.EMPLOYEENATINS, pNatIns);
793     }
794 
795     /**
796      * Set a new deemed`Benefit.
797      *
798      * @param pBenefit the new benefit
799      * @throws OceanusException on error
800      */
801     public final void setDeemedBenefit(final OceanusMoney pBenefit) throws OceanusException {
802         setInfoSetValue(MoneyWiseTransInfoClass.DEEMEDBENEFIT, pBenefit);
803     }
804 
805     /**
806      * Set a new Withheld.
807      *
808      * @param pWithheld the new withheld
809      * @throws OceanusException on error
810      */
811     public final void setWithheld(final OceanusMoney pWithheld) throws OceanusException {
812         setInfoSetValue(MoneyWiseTransInfoClass.WITHHELD, pWithheld);
813     }
814 
815     /**
816      * Set a new Partner Amount.
817      *
818      * @param pValue the new partner amount
819      * @throws OceanusException on error
820      */
821     public final void setPartnerAmount(final OceanusMoney pValue) throws OceanusException {
822         setInfoSetValue(MoneyWiseTransInfoClass.PARTNERAMOUNT, pValue);
823     }
824 
825     /**
826      * Set a new ExchangeRate.
827      *
828      * @param pRate the new rate
829      * @throws OceanusException on error
830      */
831     public final void setExchangeRate(final OceanusRatio pRate) throws OceanusException {
832         setInfoSetValue(MoneyWiseTransInfoClass.XCHANGERATE, pRate);
833     }
834 
835     /**
836      * Set a new ReturnedCash.
837      *
838      * @param pValue the new returned cash
839      * @throws OceanusException on error
840      */
841     public final void setReturnedCash(final OceanusMoney pValue) throws OceanusException {
842         setInfoSetValue(MoneyWiseTransInfoClass.RETURNEDCASH, pValue);
843     }
844 
845     /**
846      * Set a new Reference.
847      *
848      * @param pReference the new reference
849      * @throws OceanusException on error
850      */
851     public final void setReference(final String pReference) throws OceanusException {
852         setInfoSetValue(MoneyWiseTransInfoClass.REFERENCE, pReference);
853     }
854 
855     /**
856      * Set new Comments.
857      *
858      * @param pComments the new comments
859      * @throws OceanusException on error
860      */
861     public final void setComments(final String pComments) throws OceanusException {
862         setInfoSetValue(MoneyWiseTransInfoClass.COMMENTS, pComments);
863     }
864 
865     /**
866      * Set a new ReturnedCasah Account.
867      *
868      * @param pAccount the new returned cash account
869      * @throws OceanusException on error
870      */
871     public final void setReturnedCashAccount(final MoneyWiseTransAsset pAccount) throws OceanusException {
872         setInfoSetValue(MoneyWiseTransInfoClass.RETURNEDCASHACCOUNT, pAccount);
873     }
874 
875     /**
876      * Set the transaction tags.
877      *
878      * @param pTags the tags
879      * @throws OceanusException on error
880      */
881     public final void setTransactionTags(final List<MoneyWiseTransTag> pTags) throws OceanusException {
882         /* Reject if there is no infoSet */
883         if (!hasInfoSet) {
884             throw new MoneyWiseLogicException(ERROR_BADINFOSET);
885         }
886 
887         /* Link the value */
888         theInfoSet.setListValue(MoneyWiseTransInfoClass.TRANSTAG, pTags);
889     }
890 
891     /**
892      * Set an infoSet value.
893      *
894      * @param pInfoClass the class of info to set
895      * @param pValue     the value to set
896      * @throws OceanusException on error
897      */
898     private void setInfoSetValue(final MoneyWiseTransInfoClass pInfoClass,
899                                  final Object pValue) throws OceanusException {
900         /* Reject if there is no infoSet */
901         if (!hasInfoSet) {
902             throw new MoneyWiseLogicException(ERROR_BADINFOSET);
903         }
904 
905         /* Set the value */
906         theInfoSet.setValue(pInfoClass, pValue);
907     }
908 
909     @Override
910     public void touchUnderlyingItems() {
911         /* touch underlying items */
912         super.touchUnderlyingItems();
913 
914         /* touch infoSet items */
915         theInfoSet.touchUnderlyingItems();
916     }
917 
918     /**
919      * Update base transaction from an edited transaction.
920      *
921      * @param pTrans the edited transaction
922      * @return whether changes have been made
923      */
924     @Override
925     public boolean applyChanges(final PrometheusDataItem pTrans) {
926         /* Can only update from a transaction */
927         if (!(pTrans instanceof MoneyWiseTransaction myTrans)) {
928             return false;
929         }
930 
931         /* Store the current detail into history */
932         pushHistory();
933 
934         /* Update the Date if required */
935         if (!MetisDataDifference.isEqual(getDate(), myTrans.getDate())) {
936             setValueDate(myTrans.getDate());
937         }
938 
939         /* Apply basic changes */
940         applyBasicChanges(myTrans);
941 
942         /* Check for changes */
943         return checkForHistory();
944     }
945 
946     @Override
947     public void flipAssets() throws OceanusException {
948         /* Handle underlying values */
949         super.flipAssets();
950 
951         /* Flip deltas */
952         final OceanusUnits myActDelta = getAccountDeltaUnits();
953         final OceanusUnits myPartDelta = getPartnerDeltaUnits();
954         setAccountDeltaUnits(myPartDelta);
955         setPartnerDeltaUnits(myActDelta);
956 
957         /* If we need to wipe memory of AccountDelta/PartnerDelta */
958         if (myActDelta != null && myPartDelta == null) {
959             theInfoSet.wipeInfo(MoneyWiseTransInfoClass.ACCOUNTDELTAUNITS);
960         }
961         if (myPartDelta != null && myActDelta == null) {
962             theInfoSet.wipeInfo(MoneyWiseTransInfoClass.PARTNERDELTAUNITS);
963         }
964     }
965 
966     @Override
967     public void removeItem() {
968         theInfoSet.removeItems();
969         super.removeItem();
970     }
971 
972     /**
973      * The Transaction List class.
974      */
975     public static class MoneyWiseTransactionList
976             extends MoneyWiseTransBaseList<MoneyWiseTransaction> {
977         /**
978          * Report fields.
979          */
980         private static final MetisFieldSet<MoneyWiseTransactionList> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseTransactionList.class);
981 
982         /**
983          * The TransactionInfo List.
984          */
985         private MoneyWiseTransInfoList theInfoList;
986 
987         /**
988          * The TransactionInfoType list.
989          */
990         private MoneyWiseTransInfoTypeList theInfoTypeList;
991 
992         /**
993          * The EditSet.
994          */
995         private PrometheusEditSet theEditSet;
996 
997         /**
998          * Construct an empty CORE list.
999          *
1000          * @param pData the DataSet for the list
1001          */
1002         public MoneyWiseTransactionList(final PrometheusDataSet pData) {
1003             super(pData, MoneyWiseTransaction.class, MoneyWiseBasicDataType.TRANSACTION);
1004         }
1005 
1006         /**
1007          * Constructor for a cloned List.
1008          *
1009          * @param pSource the source List
1010          */
1011         protected MoneyWiseTransactionList(final MoneyWiseTransactionList pSource) {
1012             super(pSource);
1013         }
1014 
1015         @Override
1016         public MetisFieldSet<MoneyWiseTransactionList> getDataFieldSet() {
1017             return FIELD_DEFS;
1018         }
1019 
1020         @Override
1021         public String listName() {
1022             return LIST_NAME;
1023         }
1024 
1025         @Override
1026         public MetisFieldSetDef getItemFields() {
1027             return MoneyWiseTransaction.FIELD_DEFS;
1028         }
1029 
1030         /**
1031          * Obtain the transactionInfoList.
1032          *
1033          * @return the transaction info list
1034          */
1035         public MoneyWiseTransInfoList getTransactionInfo() {
1036             if (theInfoList == null) {
1037                 theInfoList = getDataSet().getDataList(MoneyWiseBasicDataType.TRANSACTIONINFO, MoneyWiseTransInfoList.class);
1038             }
1039             return theInfoList;
1040         }
1041 
1042         /**
1043          * Set the transactionInfoTypeList.
1044          *
1045          * @param pInfoList the info type list
1046          */
1047         protected void setTransactionInfo(final MoneyWiseTransInfoList pInfoList) {
1048             theInfoList = pInfoList;
1049         }
1050 
1051         /**
1052          * Obtain the transactionInfoTypeList.
1053          *
1054          * @return the transaction info type list
1055          */
1056         public MoneyWiseTransInfoTypeList getTransInfoTypes() {
1057             if (theInfoTypeList == null) {
1058                 theInfoTypeList = getDataSet().getDataList(MoneyWiseStaticDataType.TRANSINFOTYPE, MoneyWiseTransInfoTypeList.class);
1059             }
1060             return theInfoTypeList;
1061         }
1062 
1063         /**
1064          * Set the transactionInfoTypeList.
1065          *
1066          * @param pInfoTypeList the info type list
1067          */
1068         protected void setTransInfoTypes(final MoneyWiseTransInfoTypeList pInfoTypeList) {
1069             theInfoTypeList = pInfoTypeList;
1070         }
1071 
1072         /**
1073          * Obtain editSet.
1074          *
1075          * @return the editSet
1076          */
1077         public PrometheusEditSet getEditSet() {
1078             return theEditSet;
1079         }
1080 
1081         /**
1082          * Set the editSet.
1083          *
1084          * @param pEditSet the editSet
1085          */
1086         public void setEditSet(final PrometheusEditSet pEditSet) {
1087             theEditSet = pEditSet;
1088         }
1089 
1090         @Override
1091         protected MoneyWiseTransactionList getEmptyList(final PrometheusListStyle pStyle) {
1092             final MoneyWiseTransactionList myList = new MoneyWiseTransactionList(this);
1093             myList.setStyle(pStyle);
1094             return myList;
1095         }
1096 
1097         /**
1098          * Derive Edit list.
1099          *
1100          * @param pEditSet the editSet
1101          * @return the edit list
1102          * @throws OceanusException on error
1103          */
1104         public MoneyWiseTransactionList deriveEditList(final PrometheusEditSet pEditSet) throws OceanusException {
1105             /* Build an empty List */
1106             final MoneyWiseTransactionList myList = getEmptyList(PrometheusListStyle.EDIT);
1107             pEditSet.setEditEntryList(MoneyWiseBasicDataType.TRANSACTION, myList);
1108 
1109             /* Store InfoType list */
1110             myList.theInfoTypeList = pEditSet.getDataList(MoneyWiseStaticDataType.TRANSINFOTYPE, MoneyWiseTransInfoTypeList.class);
1111 
1112             /* Create info List */
1113             final MoneyWiseTransInfoList myTransInfo = getTransactionInfo();
1114             myList.theInfoList = myTransInfo.getEmptyList(PrometheusListStyle.EDIT);
1115             pEditSet.setEditEntryList(MoneyWiseBasicDataType.TRANSACTIONINFO, myList.theInfoList);
1116 
1117             /* Store the editSet */
1118             myList.theEditSet = pEditSet;
1119             myList.getValidator().setEditSet(pEditSet);
1120 
1121             /* Loop through the portfolios */
1122             final Iterator<MoneyWiseTransaction> myIterator = iterator();
1123             while (myIterator.hasNext()) {
1124                 final MoneyWiseTransaction myCurr = myIterator.next();
1125 
1126                 /* Ignore deleted transactions */
1127                 if (myCurr.isDeleted()) {
1128                     continue;
1129                 }
1130 
1131                 /* Build the new linked transaction and add it to the list */
1132                 final MoneyWiseTransaction myTrans = new MoneyWiseTransaction(myList, myCurr);
1133                 myList.add(myTrans);
1134                 myTrans.resolveEditSetLinks();
1135 
1136                 /* Adjust the map */
1137                 myTrans.adjustMapForItem();
1138             }
1139 
1140             /* Return the list */
1141             return myList;
1142         }
1143 
1144         /**
1145          * Relink LastEvent etc. for assets.
1146          *
1147          * @throws OceanusException on error
1148          */
1149         public void relinkEditAssetEvents() throws OceanusException {
1150             final PrometheusEditSet myEditSet = getEditSet();
1151             final Iterator<PrometheusEditEntry<?>> myIterator = myEditSet.listIterator();
1152             while (myIterator.hasNext()) {
1153                 final PrometheusEditEntry<?> myEntry = myIterator.next();
1154                 final PrometheusDataList<?> myList = myEntry.getDataList();
1155                 if (myList instanceof MoneyWiseAssetBaseList<?> myAssetList) {
1156                     myAssetList.resolveLateEditSetLinks();
1157                 }
1158             }
1159         }
1160 
1161         /**
1162          * Add a new item to the list.
1163          *
1164          * @param pItem the item to add
1165          * @return the newly added item
1166          */
1167         @Override
1168         public MoneyWiseTransaction addCopyItem(final PrometheusDataItem pItem) {
1169             if (pItem instanceof MoneyWiseTransaction myItem) {
1170                 final MoneyWiseTransaction myTrans = new MoneyWiseTransaction(this, myItem);
1171                 add(myTrans);
1172                 return myTrans;
1173             }
1174             throw new UnsupportedOperationException();
1175         }
1176 
1177         /**
1178          * Add a new item to the edit list.
1179          *
1180          * @return the newly added item
1181          */
1182         @Override
1183         public MoneyWiseTransaction addNewItem() {
1184             /* Create a new Transaction */
1185             final MoneyWiseTransaction myTrans = new MoneyWiseTransaction(this);
1186 
1187             /* Set the Date as the start of the range */
1188             OceanusDate myDate = new OceanusDate();
1189             if (getDataSet() instanceof MoneyWiseDataSetCtl myControl) {
1190                 final OceanusDateRange myRange = myControl.getDateRange();
1191                 if (myRange.compareToDate(myDate) != 0) {
1192                     myDate = myRange.getStart();
1193                 }
1194             }
1195             myTrans.setDate(myDate);
1196 
1197             /* Add to list and return */
1198             add(myTrans);
1199             return myTrans;
1200         }
1201 
1202         @Override
1203         public MoneyWiseTransaction addValuesItem(final PrometheusDataValues pValues) throws OceanusException {
1204             /* Create the transaction */
1205             final MoneyWiseTransaction myTrans = new MoneyWiseTransaction(this, pValues);
1206 
1207             /* Check that this TransId has not been previously added */
1208             if (!isIdUnique(myTrans.getIndexedId())) {
1209                 myTrans.addError(ERROR_DUPLICATE, MetisDataResource.DATA_ID);
1210                 throw new MoneyWiseDataException(myTrans, ERROR_VALIDATION);
1211             }
1212 
1213             /* Add to the list */
1214             add(myTrans);
1215 
1216             /* Loop through the info items */
1217             if (pValues.hasInfoItems()) {
1218                 /* Loop through the items */
1219                 final Iterator<PrometheusInfoItem> myIterator = pValues.infoIterator();
1220                 while (myIterator.hasNext()) {
1221                     final PrometheusInfoItem myItem = myIterator.next();
1222 
1223                     /* Build info */
1224                     final PrometheusDataValues myValues = myItem.getValues(myTrans);
1225                     getTransactionInfo().addValuesItem(myValues);
1226                 }
1227             }
1228 
1229             /* Return it */
1230             return myTrans;
1231         }
1232 
1233         @Override
1234         protected PrometheusDataMapItem allocateDataMap() {
1235             /* Unused */
1236             throw new UnsupportedOperationException();
1237         }
1238 
1239         @Override
1240         public void postProcessOnLoad() throws OceanusException {
1241             /* Resolve links and sort the data */
1242             super.resolveDataSetLinks();
1243             reSort();
1244         }
1245 
1246         @Override
1247         public void updateMaps() {
1248             /* Null operation */
1249         }
1250     }
1251 }