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.atlas.ui.dialog;
18  
19  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
20  import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
21  import io.github.tonywasher.joceanus.oceanus.date.OceanusDateConfig;
22  import io.github.tonywasher.joceanus.oceanus.date.OceanusDateRange;
23  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusMoney;
24  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusPrice;
25  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRatio;
26  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusUnits;
27  import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataFieldId;
28  import io.github.tonywasher.joceanus.metis.field.MetisFieldRequired;
29  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.base.MoneyWiseXAnalysisEvent;
30  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets.MoneyWiseXAnalysis;
31  import io.github.tonywasher.joceanus.moneywise.atlas.ui.controls.MoneyWiseXAnalysisSelect;
32  import io.github.tonywasher.joceanus.moneywise.atlas.views.MoneyWiseXAnalysisFilter;
33  import io.github.tonywasher.joceanus.moneywise.atlas.views.MoneyWiseXTransactionFilters;
34  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseAssetBase;
35  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseAssetBase.MoneyWiseAssetBaseList;
36  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseAssetDirection;
37  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseAssetType;
38  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicDataType;
39  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicResource;
40  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseCash.MoneyWiseCashList;
41  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDataSet;
42  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDataValidator.MoneyWiseDataValidatorTrans;
43  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDeposit.MoneyWiseDepositList;
44  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseLoan.MoneyWiseLoanList;
45  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePayee.MoneyWisePayeeList;
46  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePortfolio;
47  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePortfolio.MoneyWisePortfolioList;
48  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity;
49  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityHolding;
50  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityHolding.MoneyWiseSecurityHoldingMap;
51  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransAsset;
52  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransCategory;
53  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransCategory.MoneyWiseTransCategoryList;
54  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransInfoSet;
55  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransTag;
56  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransTag.MoneyWiseTransTagList;
57  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransaction;
58  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransCategoryClass;
59  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransInfoClass;
60  import io.github.tonywasher.joceanus.moneywise.data.validate.MoneyWiseValidateTransaction;
61  import io.github.tonywasher.joceanus.moneywise.ui.MoneyWiseIcon;
62  import io.github.tonywasher.joceanus.moneywise.ui.MoneyWiseUIResource;
63  import io.github.tonywasher.joceanus.moneywise.ui.base.MoneyWiseBaseTable;
64  import io.github.tonywasher.joceanus.moneywise.ui.base.MoneyWiseItemPanel;
65  import io.github.tonywasher.joceanus.prometheus.ui.fieldset.PrometheusFieldSet;
66  import io.github.tonywasher.joceanus.prometheus.ui.fieldset.PrometheusFieldSetEvent;
67  import io.github.tonywasher.joceanus.prometheus.views.PrometheusEditSet;
68  import io.github.tonywasher.joceanus.tethys.api.control.TethysUIControl.TethysUIIconMapSet;
69  import io.github.tonywasher.joceanus.tethys.api.factory.TethysUIFactory;
70  import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIDateButtonField;
71  import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIIconButtonField;
72  import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIIntegerEditField;
73  import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIListButtonField;
74  import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIMoneyEditField;
75  import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIRatioEditField;
76  import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIScrollButtonField;
77  import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIStringEditField;
78  import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIUnitsEditField;
79  import io.github.tonywasher.joceanus.tethys.api.field.TethysUIFieldFactory;
80  import io.github.tonywasher.joceanus.tethys.api.menu.TethysUIScrollItem;
81  import io.github.tonywasher.joceanus.tethys.api.menu.TethysUIScrollMenu;
82  import io.github.tonywasher.joceanus.tethys.api.menu.TethysUIScrollSubMenu;
83  
84  import java.util.ArrayList;
85  import java.util.HashMap;
86  import java.util.Iterator;
87  import java.util.List;
88  import java.util.Map;
89  
90  /**
91   * Panel to display/edit/create a Transaction.
92   */
93  public class MoneyWiseXTransactionDialog
94          extends MoneyWiseItemPanel<MoneyWiseXAnalysisEvent> {
95      /**
96       * Info Tab Title.
97       */
98      private static final String TAB_INFO = MoneyWiseUIResource.TRANSPANEL_TAB_INFO.getValue();
99  
100     /**
101      * Tax Tab Title.
102      */
103     private static final String TAB_TAXES = MoneyWiseUIResource.TRANSPANEL_TAB_TAXES.getValue();
104 
105     /**
106      * Securities Tab Title.
107      */
108     private static final String TAB_SECURITIES = MoneyWiseUIResource.TRANSPANEL_TAB_SECURITIES.getValue();
109 
110     /**
111      * Returned Tab Title.
112      */
113     private static final String TAB_RETURNED = MoneyWiseUIResource.TRANSPANEL_TAB_RETURNED.getValue();
114 
115     /**
116      * The fieldSet.
117      */
118     private final PrometheusFieldSet<MoneyWiseXAnalysisEvent> theFieldSet;
119 
120     /**
121      * Analysis selection panel.
122      */
123     private final MoneyWiseXAnalysisSelect theAnalysisSelect;
124 
125     /**
126      * dateRange.
127      */
128     private OceanusDateRange theRange;
129 
130     /**
131      * transaction.
132      */
133     private MoneyWiseTransaction theTransaction;
134 
135     /**
136      * reconciledState.
137      */
138     private Boolean theReconciledState = Boolean.FALSE;
139 
140     /**
141      * directionState.
142      */
143     private Boolean theDirectionState = Boolean.FALSE;
144 
145     /**
146      * Constructor.
147      *
148      * @param pFactory        the GUI factory
149      * @param pEditSet        the edit set
150      * @param pAnalysisSelect the analysis selection panel
151      * @param pOwner          the owning table
152      */
153     public MoneyWiseXTransactionDialog(final TethysUIFactory<?> pFactory,
154                                        final PrometheusEditSet pEditSet,
155                                        final MoneyWiseXAnalysisSelect pAnalysisSelect,
156                                        final MoneyWiseBaseTable<MoneyWiseXAnalysisEvent> pOwner) {
157         /* Initialise the panel */
158         super(pFactory, pEditSet, pOwner);
159         theAnalysisSelect = pAnalysisSelect;
160 
161         /* Access the fieldSet */
162         theFieldSet = getFieldSet();
163 
164         /* Build the main panel */
165         buildMainPanel(pFactory);
166 
167         /* Build the info panel */
168         buildInfoPanel(pFactory);
169 
170         /* Build the tax panel */
171         buildTaxPanel(pFactory);
172 
173         /* Build the securities panel */
174         buildSecuritiesPanel(pFactory);
175 
176         /* Build the returned panel */
177         buildReturnedPanel(pFactory);
178     }
179 
180     /**
181      * Build Main subPanel.
182      *
183      * @param pFactory the GUI factory
184      */
185     private void buildMainPanel(final TethysUIFactory<?> pFactory) {
186         /* Allocate fields */
187         final TethysUIFieldFactory myFields = pFactory.fieldFactory();
188         final TethysUIMoneyEditField myAmount = myFields.newMoneyField();
189 
190         /* Create the buttons */
191         final TethysUIDateButtonField myDateButton = myFields.newDateField();
192         final TethysUIScrollButtonField<MoneyWiseTransAsset> myAccountButton = myFields.newScrollField(MoneyWiseTransAsset.class);
193         final TethysUIScrollButtonField<MoneyWiseTransAsset> myPartnerButton = myFields.newScrollField(MoneyWiseTransAsset.class);
194         final TethysUIScrollButtonField<MoneyWiseTransCategory> myCategoryButton = myFields.newScrollField(MoneyWiseTransCategory.class);
195         final TethysUIIconButtonField<Boolean> myReconciledButton = myFields.newIconField(Boolean.class);
196         final TethysUIIconButtonField<MoneyWiseAssetDirection> myDirectionButton = myFields.newIconField(MoneyWiseAssetDirection.class);
197 
198         /* Assign the fields to the panel */
199         theFieldSet.addField(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_DATE, myDateButton, MoneyWiseXAnalysisEvent::getDate);
200         theFieldSet.addField(MoneyWiseBasicResource.TRANSACTION_ACCOUNT, myAccountButton, MoneyWiseXAnalysisEvent::getAccount);
201         theFieldSet.addField(MoneyWiseBasicDataType.TRANSCATEGORY, myCategoryButton, MoneyWiseXAnalysisEvent::getCategory);
202         theFieldSet.addField(MoneyWiseBasicResource.TRANSACTION_DIRECTION, myDirectionButton, MoneyWiseXAnalysisEvent::getDirection);
203         theFieldSet.addField(MoneyWiseBasicResource.TRANSACTION_PARTNER, myPartnerButton, MoneyWiseXAnalysisEvent::getPartner);
204         theFieldSet.addField(MoneyWiseBasicResource.TRANSACTION_AMOUNT, myAmount, MoneyWiseXAnalysisEvent::getAmount);
205         theFieldSet.addField(MoneyWiseBasicResource.TRANSACTION_RECONCILED, myReconciledButton, MoneyWiseXAnalysisEvent::isReconciled);
206 
207         /* Configure the menuBuilders */
208         myDateButton.setDateConfigurator(this::handleDateConfig);
209         myAccountButton.setMenuConfigurator(c -> buildAccountMenu(c, getItem()));
210         myCategoryButton.setMenuConfigurator(c -> buildCategoryMenu(c, getItem()));
211         myPartnerButton.setMenuConfigurator(c -> buildPartnerMenu(c, getItem()));
212         final Map<Boolean, TethysUIIconMapSet<Boolean>> myRecMapSets = MoneyWiseIcon.configureReconciledIconButton(pFactory);
213         myReconciledButton.setIconMapSet(() -> myRecMapSets.get(theReconciledState));
214         final Map<Boolean, TethysUIIconMapSet<MoneyWiseAssetDirection>> myDirMapSets = MoneyWiseIcon.configureDirectionIconButton(pFactory);
215         myDirectionButton.setIconMapSet(() -> myDirMapSets.get(theDirectionState));
216         myAmount.setDeemedCurrency(() -> getItem().getAccount().getCurrency());
217     }
218 
219     /**
220      * Build info subPanel.
221      *
222      * @param pFactory the GUI factory
223      */
224     private void buildInfoPanel(final TethysUIFactory<?> pFactory) {
225         /* Create a new panel */
226         theFieldSet.newPanel(TAB_INFO);
227 
228         /* Allocate fields */
229         final TethysUIFieldFactory myFields = pFactory.fieldFactory();
230         final TethysUIMoneyEditField myAmount = myFields.newMoneyField();
231         final TethysUIStringEditField myComments = myFields.newStringField();
232         final TethysUIStringEditField myReference = myFields.newStringField();
233 
234         /* Create the buttons */
235         final TethysUIListButtonField<MoneyWiseTransTag> myTagButton = myFields.newListField();
236 
237         /* Assign the fields to the panel */
238         theFieldSet.addField(MoneyWiseTransInfoClass.PARTNERAMOUNT, myAmount, MoneyWiseXAnalysisEvent::getPartnerAmount);
239         theFieldSet.addField(MoneyWiseTransInfoClass.COMMENTS, myComments, MoneyWiseXAnalysisEvent::getComments);
240         theFieldSet.addField(MoneyWiseTransInfoClass.REFERENCE, myReference, MoneyWiseXAnalysisEvent::getReference);
241         theFieldSet.addField(MoneyWiseTransInfoClass.TRANSTAG, myTagButton, MoneyWiseXAnalysisEvent::getTransactionTags);
242 
243         /* Configure the tag button */
244         myTagButton.setSelectables(this::buildTransactionTags);
245 
246         /* Set currency */
247         myAmount.setDeemedCurrency(() -> getItem().getPartner().getCurrency());
248     }
249 
250     /**
251      * Build tax subPanel.
252      *
253      * @param pFactory the GUI factory
254      */
255     private void buildTaxPanel(final TethysUIFactory<?> pFactory) {
256         /* Create a new panel */
257         theFieldSet.newPanel(TAB_TAXES);
258 
259         /* Allocate fields */
260         final TethysUIFieldFactory myFields = pFactory.fieldFactory();
261         final TethysUIMoneyEditField myTaxCredit = myFields.newMoneyField();
262         final TethysUIMoneyEditField myEeNatIns = myFields.newMoneyField();
263         final TethysUIMoneyEditField myErNatIns = myFields.newMoneyField();
264         final TethysUIMoneyEditField myBenefit = myFields.newMoneyField();
265         final TethysUIMoneyEditField myWithheld = myFields.newMoneyField();
266         final TethysUIIntegerEditField myYears = myFields.newIntegerField();
267 
268         /* Assign the fields to the panel */
269         theFieldSet.addField(MoneyWiseTransInfoClass.TAXCREDIT, myTaxCredit, MoneyWiseXAnalysisEvent::getTaxCredit);
270         theFieldSet.addField(MoneyWiseTransInfoClass.EMPLOYEENATINS, myEeNatIns, MoneyWiseXAnalysisEvent::getEmployeeNatIns);
271         theFieldSet.addField(MoneyWiseTransInfoClass.EMPLOYERNATINS, myErNatIns, MoneyWiseXAnalysisEvent::getEmployerNatIns);
272         theFieldSet.addField(MoneyWiseTransInfoClass.DEEMEDBENEFIT, myBenefit, MoneyWiseXAnalysisEvent::getDeemedBenefit);
273         theFieldSet.addField(MoneyWiseTransInfoClass.WITHHELD, myWithheld, MoneyWiseXAnalysisEvent::getWithheld);
274 
275         /* Set currency */
276         myTaxCredit.setDeemedCurrency(() -> getItem().getAccount().getCurrency());
277         myEeNatIns.setDeemedCurrency(() -> getItem().getAccount().getCurrency());
278         myErNatIns.setDeemedCurrency(() -> getItem().getAccount().getCurrency());
279         myBenefit.setDeemedCurrency(() -> getItem().getAccount().getCurrency());
280         myWithheld.setDeemedCurrency(() -> getItem().getAccount().getCurrency());
281     }
282 
283     /**
284      * Build securities subPanel.
285      *
286      * @param pFactory the GUI factory
287      */
288     private void buildSecuritiesPanel(final TethysUIFactory<?> pFactory) {
289         /* Create a new panel */
290         theFieldSet.newPanel(TAB_SECURITIES);
291 
292         /* Allocate fields */
293         final TethysUIFieldFactory myFields = pFactory.fieldFactory();
294         final TethysUIUnitsEditField myAccountUnits = myFields.newUnitsField();
295         final TethysUIUnitsEditField myPartnerUnits = myFields.newUnitsField();
296         final TethysUIRatioEditField myDilution = myFields.newRatioField();
297 
298         /* Assign the fields to the panel */
299         theFieldSet.addField(MoneyWiseTransInfoClass.ACCOUNTDELTAUNITS, myAccountUnits, MoneyWiseXAnalysisEvent::getAccountDeltaUnits);
300         theFieldSet.addField(MoneyWiseTransInfoClass.PARTNERDELTAUNITS, myPartnerUnits, MoneyWiseXAnalysisEvent::getPartnerDeltaUnits);
301         theFieldSet.addField(MoneyWiseTransInfoClass.DILUTION, myDilution, MoneyWiseXAnalysisEvent::getDilution);
302     }
303 
304     /**
305      * Build returned subPanel.
306      *
307      * @param pFactory the GUI factory
308      */
309     private void buildReturnedPanel(final TethysUIFactory<?> pFactory) {
310         /* Create a new panel */
311         theFieldSet.newPanel(TAB_RETURNED);
312 
313         /* Allocate fields */
314         final TethysUIFieldFactory myFields = pFactory.fieldFactory();
315         final TethysUIMoneyEditField myReturnedCash = myFields.newMoneyField();
316 
317         /* Create the buttons */
318         final TethysUIScrollButtonField<MoneyWiseTransAsset> myReturnedAccountButton = myFields.newScrollField(MoneyWiseTransAsset.class);
319 
320         /* Assign the fields to the panel */
321         theFieldSet.addField(MoneyWiseTransInfoClass.RETURNEDCASHACCOUNT, myReturnedAccountButton, MoneyWiseXAnalysisEvent::getReturnedCashAccount);
322         theFieldSet.addField(MoneyWiseTransInfoClass.RETURNEDCASH, myReturnedCash, MoneyWiseXAnalysisEvent::getReturnedCash);
323 
324         /* Configure the menuBuilders */
325         myReturnedAccountButton.setMenuConfigurator(c -> buildReturnedAccountMenu(c, getItem()));
326 
327         /* Set currency */
328         myReturnedCash.setDeemedCurrency(() -> getItem().getReturnedCashAccount().getCurrency());
329     }
330 
331     @Override
332     public void refreshData() {
333         /* If we have an item */
334         final MoneyWiseXAnalysisEvent myItem = getItem();
335         //if (myItem != null) {
336         /* TODO Reselect from list of Events - Where is this list? */
337         //setItem(myTrans.findItemById(myItem.getIndexedId()));
338         //}
339 
340         /* Make sure that the item is not editable */
341         setEditable(false);
342     }
343 
344     /**
345      * Update editors.
346      *
347      * @param pRange the date range.
348      */
349     public void updateEditors(final OceanusDateRange pRange) {
350         /* Update the range */
351         theRange = pRange;
352     }
353 
354     /**
355      * Handle dateConfig.
356      *
357      * @param pConfig the dateConfig
358      */
359     private void handleDateConfig(final OceanusDateConfig pConfig) {
360         /* Update Date button */
361         pConfig.setEarliestDate(theRange != null
362                 ? theRange.getStart()
363                 : null);
364         pConfig.setLatestDate(theRange != null
365                 ? theRange.getEnd()
366                 : null);
367     }
368 
369     @Override
370     public boolean isDeletable() {
371         return getItem() != null && !getItem().isReconciled();
372     }
373 
374     @Override
375     protected void adjustFields(final boolean isEditable) {
376         /* Access the item */
377         final MoneyWiseTransaction myTrans = getItem().getTransaction();
378         final boolean bIsReconciled = myTrans.isReconciled();
379         final boolean bIsLocked = myTrans.isLocked();
380 
381         /* Determine whether the comments field should be visible */
382         boolean bShowField = isEditable || myTrans.getComments() != null;
383         theFieldSet.setFieldVisible(MoneyWiseTransInfoClass.COMMENTS, bShowField);
384 
385         /* Determine whether the reference field should be visible */
386         bShowField = isEditable || myTrans.getReference() != null;
387         theFieldSet.setFieldVisible(MoneyWiseTransInfoClass.REFERENCE, bShowField);
388 
389         /* Determine whether the tags field should be visible */
390         bShowField = isEditable || myTrans.getTransactionTags() != null;
391         theFieldSet.setFieldVisible(MoneyWiseTransInfoClass.TRANSTAG, bShowField);
392 
393         /* Determine whether the partnerAmount field should be visible */
394         boolean bEditField = isEditable && isEditableField(myTrans, MoneyWiseTransInfoClass.PARTNERAMOUNT);
395         bShowField = bEditField || myTrans.getPartnerAmount() != null;
396         theFieldSet.setFieldVisible(MoneyWiseTransInfoClass.PARTNERAMOUNT, bShowField);
397         theFieldSet.setFieldEditable(MoneyWiseTransInfoClass.PARTNERAMOUNT, bEditField);
398 
399         /* Determine whether the taxCredit field should be visible */
400         bEditField = isEditable && isEditableField(myTrans, MoneyWiseTransInfoClass.TAXCREDIT);
401         bShowField = bEditField || myTrans.getTaxCredit() != null;
402         theFieldSet.setFieldVisible(MoneyWiseTransInfoClass.TAXCREDIT, bShowField);
403         theFieldSet.setFieldEditable(MoneyWiseTransInfoClass.TAXCREDIT, bEditField);
404 
405         /* Determine whether the EeNatIns field should be visible */
406         bEditField = isEditable && isEditableField(myTrans, MoneyWiseTransInfoClass.EMPLOYEENATINS);
407         bShowField = bEditField || myTrans.getEmployeeNatIns() != null;
408         theFieldSet.setFieldVisible(MoneyWiseTransInfoClass.EMPLOYEENATINS, bShowField);
409         theFieldSet.setFieldEditable(MoneyWiseTransInfoClass.EMPLOYEENATINS, bEditField);
410 
411         /* Determine whether the ErnatIns field should be visible */
412         bEditField = isEditable && isEditableField(myTrans, MoneyWiseTransInfoClass.EMPLOYERNATINS);
413         bShowField = bEditField || myTrans.getEmployerNatIns() != null;
414         theFieldSet.setFieldVisible(MoneyWiseTransInfoClass.EMPLOYERNATINS, bShowField);
415         theFieldSet.setFieldEditable(MoneyWiseTransInfoClass.EMPLOYERNATINS, bEditField);
416 
417         /* Determine whether the benefit field should be visible */
418         bEditField = isEditable && isEditableField(myTrans, MoneyWiseTransInfoClass.DEEMEDBENEFIT);
419         bShowField = bEditField || myTrans.getDeemedBenefit() != null;
420         theFieldSet.setFieldVisible(MoneyWiseTransInfoClass.DEEMEDBENEFIT, bShowField);
421         theFieldSet.setFieldEditable(MoneyWiseTransInfoClass.DEEMEDBENEFIT, bEditField);
422 
423         /* Determine whether the donation field should be visible */
424         bEditField = isEditable && isEditableField(myTrans, MoneyWiseTransInfoClass.WITHHELD);
425         bShowField = bEditField || myTrans.getWithheld() != null;
426         theFieldSet.setFieldVisible(MoneyWiseTransInfoClass.WITHHELD, bShowField);
427         theFieldSet.setFieldEditable(MoneyWiseTransInfoClass.WITHHELD, bEditField);
428 
429         /* Determine whether the account units field should be visible */
430         bEditField = isEditable && isEditableField(myTrans, MoneyWiseTransInfoClass.ACCOUNTDELTAUNITS);
431         bShowField = bEditField || myTrans.getAccountDeltaUnits() != null;
432         theFieldSet.setFieldVisible(MoneyWiseTransInfoClass.ACCOUNTDELTAUNITS, bShowField);
433         theFieldSet.setFieldEditable(MoneyWiseTransInfoClass.ACCOUNTDELTAUNITS, bEditField);
434 
435         /* Determine whether the partnerDeltaUnits field should be visible */
436         bEditField = isEditable && isEditableField(myTrans, MoneyWiseTransInfoClass.PARTNERDELTAUNITS);
437         bShowField = bEditField || myTrans.getPartnerDeltaUnits() != null;
438         theFieldSet.setFieldVisible(MoneyWiseTransInfoClass.PARTNERDELTAUNITS, bShowField);
439         theFieldSet.setFieldEditable(MoneyWiseTransInfoClass.PARTNERDELTAUNITS, bEditField);
440 
441         /* Determine whether the dilution field should be visible */
442         bEditField = isEditable && isEditableField(myTrans, MoneyWiseTransInfoClass.DILUTION);
443         bShowField = bEditField || myTrans.getDilution() != null;
444         theFieldSet.setFieldVisible(MoneyWiseTransInfoClass.DILUTION, bShowField);
445         theFieldSet.setFieldEditable(MoneyWiseTransInfoClass.DILUTION, bEditField);
446 
447         /* Determine whether the returnedAccount field should be visible */
448         bEditField = isEditable && isEditableField(myTrans, MoneyWiseTransInfoClass.RETURNEDCASHACCOUNT);
449         bShowField = bEditField || myTrans.getReturnedCashAccount() != null;
450         theFieldSet.setFieldVisible(MoneyWiseTransInfoClass.RETURNEDCASHACCOUNT, bShowField);
451         theFieldSet.setFieldEditable(MoneyWiseTransInfoClass.RETURNEDCASHACCOUNT, bEditField);
452 
453         /* Determine whether the returnedCash field should be visible */
454         bEditField = isEditable && isEditableField(myTrans, MoneyWiseTransInfoClass.RETURNEDCASH);
455         bShowField = bEditField || myTrans.getReturnedCash() != null;
456         theFieldSet.setFieldVisible(MoneyWiseTransInfoClass.RETURNEDCASH, bShowField);
457         theFieldSet.setFieldEditable(MoneyWiseTransInfoClass.RETURNEDCASH, bEditField);
458 
459         /* Determine whether the reconciled field should be visible */
460         final boolean bShowReconciled = isEditable || bIsReconciled;
461         theReconciledState = bIsLocked;
462         theDirectionState = bIsReconciled;
463         theFieldSet.setFieldVisible(MoneyWiseBasicResource.TRANSACTION_RECONCILED, bShowReconciled);
464         theFieldSet.setFieldEditable(MoneyWiseBasicResource.TRANSACTION_RECONCILED, isEditable && !bIsLocked);
465 
466         /* Determine basic editing */
467         final boolean canEdit = isEditable && !bIsReconciled;
468         final boolean needsNullAmount = myTrans.needsNullAmount();
469         theFieldSet.setFieldEditable(MoneyWiseBasicResource.TRANSACTION_DIRECTION, canEdit && myTrans.canSwitchDirection());
470         theFieldSet.setFieldEditable(MoneyWiseBasicResource.TRANSACTION_ACCOUNT, canEdit);
471         theFieldSet.setFieldEditable(MoneyWiseBasicResource.TRANSACTION_PARTNER, canEdit);
472         theFieldSet.setFieldEditable(MoneyWiseBasicDataType.TRANSCATEGORY, canEdit);
473         theFieldSet.setFieldEditable(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_DATE, canEdit);
474         theFieldSet.setFieldEditable(MoneyWiseBasicResource.TRANSACTION_AMOUNT, canEdit && !needsNullAmount);
475         theFieldSet.setFieldVisible(MoneyWiseBasicResource.TRANSACTION_AMOUNT, !needsNullAmount);
476 
477         /* Set the range for the dateButton */
478         final MoneyWiseValidateTransaction myBuilder = (MoneyWiseValidateTransaction) myTrans.getList().getValidator();
479         theRange = myBuilder.getRange();
480     }
481 
482     /**
483      * Is the field editable?
484      *
485      * @param pTrans the transaction
486      * @param pField the field class
487      * @return true/false
488      */
489     public static boolean isEditableField(final MoneyWiseTransaction pTrans,
490                                           final MoneyWiseTransInfoClass pField) {
491         /* Access the infoSet */
492         final MoneyWiseTransInfoSet myInfoSet = pTrans.getInfoSet();
493 
494         /* If the transaction is reconciled */
495         if (Boolean.TRUE.equals(pTrans.isReconciled())) {
496             /* Only allow editing of metaData */
497             return myInfoSet.isMetaData(pField);
498         }
499 
500         /* Check whether the field is available */
501         final MoneyWiseValidateTransaction myValidator = (MoneyWiseValidateTransaction) pTrans.getList().getValidator();
502         final MetisFieldRequired isRequired = myValidator.isClassRequired(pTrans, pField);
503         return !isRequired.equals(MetisFieldRequired.NOTALLOWED);
504     }
505 
506     @SuppressWarnings("unchecked")
507     @Override
508     protected void updateField(final PrometheusFieldSetEvent pUpdate) throws OceanusException {
509         /* Access the field */
510         final MetisDataFieldId myField = pUpdate.getFieldId();
511         final MoneyWiseTransaction myTrans = getItem().getTransaction();
512         final MoneyWiseValidateTransaction myBuilder = (MoneyWiseValidateTransaction) myTrans.getList().getValidator();
513 
514         /* Process updates */
515         if (MoneyWiseBasicResource.MONEYWISEDATA_FIELD_DATE.equals(myField)) {
516             /* Update the Date */
517             myTrans.setDate(pUpdate.getValue(OceanusDate.class));
518         } else if (MoneyWiseBasicResource.TRANSACTION_AMOUNT.equals(myField)) {
519             /* Update the Amount */
520             myTrans.setAmount(pUpdate.getValue(OceanusMoney.class));
521             myBuilder.autoCorrect(myTrans);
522         } else if (MoneyWiseBasicResource.TRANSACTION_ACCOUNT.equals(myField)) {
523             /* Update the Account */
524             myTrans.setAccount(resolveAsset(pUpdate.getValue(MoneyWiseTransAsset.class)));
525             myBuilder.autoCorrect(myTrans);
526         } else if (MoneyWiseBasicResource.TRANSACTION_DIRECTION.equals(myField)) {
527             /* Update the Direction */
528             myTrans.switchDirection();
529             myBuilder.autoCorrect(myTrans);
530         } else if (MoneyWiseBasicResource.TRANSACTION_PARTNER.equals(myField)) {
531             /* Update the Partner */
532             myTrans.setPartner(resolveAsset(pUpdate.getValue(MoneyWiseTransAsset.class)));
533             myBuilder.autoCorrect(myTrans);
534         } else if (MoneyWiseBasicDataType.TRANSCATEGORY.equals(myField)) {
535             /* Update the Category */
536             myTrans.setCategory(pUpdate.getValue(MoneyWiseTransCategory.class));
537             myBuilder.autoCorrect(myTrans);
538         } else if (MoneyWiseBasicResource.TRANSACTION_RECONCILED.equals(myField)) {
539             /* Update the Reconciled indication */
540             myTrans.setReconciled(pUpdate.getValue(Boolean.class));
541         } else if (MoneyWiseTransInfoClass.COMMENTS.equals(myField)) {
542             /* Update the Comments */
543             myTrans.setComments(pUpdate.getValue(String.class));
544         } else if (MoneyWiseTransInfoClass.REFERENCE.equals(myField)) {
545             /* Update the Reference */
546             myTrans.setReference(pUpdate.getValue(String.class));
547         } else if (MoneyWiseTransInfoClass.TRANSTAG.equals(myField)) {
548             /* Update the Tag indication */
549             myTrans.setTransactionTags(pUpdate.getValue(List.class));
550         } else if (MoneyWiseTransInfoClass.PARTNERAMOUNT.equals(myField)) {
551             /* Update the PartnerAmount */
552             myTrans.setPartnerAmount(pUpdate.getValue(OceanusMoney.class));
553         } else if (MoneyWiseTransInfoClass.XCHANGERATE.equals(myField)) {
554             /* Update the ExchangeRate */
555             myTrans.setExchangeRate(pUpdate.getValue(OceanusRatio.class));
556         } else if (MoneyWiseTransInfoClass.ACCOUNTDELTAUNITS.equals(myField)) {
557             /* Update the AccountDeltaUnits */
558             myTrans.setAccountDeltaUnits(pUpdate.getValue(OceanusUnits.class));
559         } else if (MoneyWiseTransInfoClass.PARTNERDELTAUNITS.equals(myField)) {
560             /* Update the PartnerDeltaUnits */
561             myTrans.setPartnerDeltaUnits(pUpdate.getValue(OceanusUnits.class));
562         } else if (MoneyWiseTransInfoClass.PRICE.equals(myField)) {
563             /* Update the Price */
564             myTrans.setPrice(pUpdate.getValue(OceanusPrice.class));
565         } else if (MoneyWiseTransInfoClass.COMMISSION.equals(myField)) {
566             /* Update the Commission */
567             myTrans.setCommission(pUpdate.getValue(OceanusMoney.class));
568         } else if (MoneyWiseTransInfoClass.DILUTION.equals(myField)) {
569             /* Update the Dilution */
570             myTrans.setDilution(pUpdate.getValue(OceanusRatio.class));
571         } else if (MoneyWiseTransInfoClass.QUALIFYYEARS.equals(myField)) {
572             /* Update the QualifyYears */
573             myTrans.setYears(pUpdate.getValue(Integer.class));
574         } else if (MoneyWiseTransInfoClass.RETURNEDCASHACCOUNT.equals(myField)) {
575             /* Update the ReturnedCashAccount */
576             myTrans.setReturnedCashAccount(pUpdate.getValue(MoneyWiseTransAsset.class));
577             myBuilder.autoCorrect(myTrans);
578         } else if (MoneyWiseTransInfoClass.RETURNEDCASH.equals(myField)) {
579             /* Update the ReturnedCash */
580             myTrans.setReturnedCash(pUpdate.getValue(OceanusMoney.class));
581         } else if (MoneyWiseTransInfoClass.TAXCREDIT.equals(myField)) {
582             /* Update the TaxCredit */
583             myTrans.setTaxCredit(pUpdate.getValue(OceanusMoney.class));
584         } else if (MoneyWiseTransInfoClass.EMPLOYEENATINS.equals(myField)) {
585             /* Update the EmployeeNatIns */
586             myTrans.setEmployeeNatIns(pUpdate.getValue(OceanusMoney.class));
587         } else if (MoneyWiseTransInfoClass.EMPLOYERNATINS.equals(myField)) {
588             /* Update the EmployerNayIns */
589             myTrans.setEmployerNatIns(pUpdate.getValue(OceanusMoney.class));
590         } else if (MoneyWiseTransInfoClass.DEEMEDBENEFIT.equals(myField)) {
591             /* Update the Benefit */
592             myTrans.setDeemedBenefit(pUpdate.getValue(OceanusMoney.class));
593         } else if (MoneyWiseTransInfoClass.WITHHELD.equals(myField)) {
594             /* Update the Withheld */
595             myTrans.setWithheld(pUpdate.getValue(OceanusMoney.class));
596         }
597     }
598 
599     @Override
600     protected void declareGoToItems(final boolean pUpdates) {
601         /* Access the item */
602         final MoneyWiseXAnalysisEvent myItem = getItem();
603 
604         /* Access the analysis and the relevant filters */
605         final MoneyWiseXAnalysis myAnalysis = theAnalysisSelect.getAnalysis();
606         final OceanusDateRange myDateRange = theAnalysisSelect.getRange();
607         final MoneyWiseXTransactionFilters myFilters = new MoneyWiseXTransactionFilters(myAnalysis, myDateRange, myItem);
608 
609         /* Remove the current filter */
610         final MoneyWiseXAnalysisFilter<?, ?> myCurrent = theAnalysisSelect.getFilter();
611         myFilters.remove(myCurrent);
612 
613         /* Loop through the filters */
614         final Iterator<MoneyWiseXAnalysisFilter<?, ?>> myIterator = myFilters.iterator();
615         while (myIterator.hasNext()) {
616             final MoneyWiseXAnalysisFilter<?, ?> myFilter = myIterator.next();
617 
618             /* declare it */
619             declareGoToFilter(myFilter);
620         }
621 
622         /* If we have not had updates */
623         if (!pUpdates) {
624             /* Allow GoTo different panels */
625             buildAssetGoTo(myItem.getAccount());
626             buildAssetGoTo(myItem.getPartner());
627             declareGoToItem(myItem.getCategory());
628             buildAssetGoTo(myItem.getReturnedCashAccount());
629         }
630     }
631 
632     /**
633      * Handle goto declarations for TransactionAssets.
634      *
635      * @param pAsset the asset
636      */
637     private void buildAssetGoTo(final MoneyWiseTransAsset pAsset) {
638         if (pAsset instanceof MoneyWiseSecurityHolding myHolding) {
639             /* Build menu Items for Portfolio and Security */
640             declareGoToItem(myHolding.getPortfolio());
641             declareGoToItem(myHolding.getSecurity());
642         } else if (pAsset instanceof MoneyWiseAssetBase myAsset) {
643             declareGoToItem(myAsset);
644         }
645     }
646 
647     /**
648      * Resolve Asset.
649      *
650      * @param pAsset the asset to resolve
651      * @return the resolved asset
652      */
653     public static MoneyWiseTransAsset resolveAsset(final MoneyWiseTransAsset pAsset) {
654         /* If this is a security holding */
655         if (pAsset instanceof MoneyWiseSecurityHolding myHolding) {
656             /* declare holding via map */
657             final MoneyWisePortfolio myPortfolio = myHolding.getPortfolio();
658             final MoneyWiseSecurity mySecurity = myHolding.getSecurity();
659             final MoneyWiseDataSet myData = myPortfolio.getDataSet();
660             final MoneyWiseSecurityHoldingMap myMap = myData.getPortfolios().getSecurityHoldingsMap();
661             return myMap.declareHolding(myPortfolio, mySecurity);
662         }
663 
664         /* Just return the asset */
665         return pAsset;
666     }
667 
668     /**
669      * Build the account menu for an item.
670      *
671      * @param pMenu  the menu
672      * @param pEvent the event to build for
673      */
674     public void buildAccountMenu(final TethysUIScrollMenu<MoneyWiseTransAsset> pMenu,
675                                  final MoneyWiseXAnalysisEvent pEvent) {
676         /* Clear the menu */
677         pMenu.removeAllItems();
678 
679         /* Add possible items */
680         final MoneyWiseTransaction myTrans = pEvent.getTransaction();
681         buildAssetMenu(pMenu, getDataList(MoneyWiseBasicDataType.DEPOSIT, MoneyWiseDepositList.class), true, myTrans);
682         buildAssetMenu(pMenu, getDataList(MoneyWiseBasicDataType.CASH, MoneyWiseCashList.class), true, myTrans);
683         buildAssetMenu(pMenu, getDataList(MoneyWiseBasicDataType.LOAN, MoneyWiseLoanList.class), true, myTrans);
684         buildHoldingMenu(pMenu, true, myTrans);
685         buildAssetMenu(pMenu, getDataList(MoneyWiseBasicDataType.PORTFOLIO, MoneyWisePortfolioList.class), true, myTrans);
686     }
687 
688     /**
689      * Build the partner menu for an item.
690      *
691      * @param pMenu  the menu
692      * @param pEvent the event to build for
693      */
694     public void buildPartnerMenu(final TethysUIScrollMenu<MoneyWiseTransAsset> pMenu,
695                                  final MoneyWiseXAnalysisEvent pEvent) {
696         /* Clear the menu */
697         pMenu.removeAllItems();
698 
699         /* Add possible items */
700         final MoneyWiseTransaction myTrans = pEvent.getTransaction();
701         buildAssetMenu(pMenu, getDataList(MoneyWiseBasicDataType.DEPOSIT, MoneyWiseDepositList.class), false, myTrans);
702         buildAssetMenu(pMenu, getDataList(MoneyWiseBasicDataType.CASH, MoneyWiseCashList.class), false, myTrans);
703         buildAssetMenu(pMenu, getDataList(MoneyWiseBasicDataType.LOAN, MoneyWiseLoanList.class), false, myTrans);
704         buildHoldingMenu(pMenu, false, myTrans);
705         buildAssetMenu(pMenu, getDataList(MoneyWiseBasicDataType.PORTFOLIO, MoneyWisePortfolioList.class), false, myTrans);
706         buildAssetMenu(pMenu, getDataList(MoneyWiseBasicDataType.PAYEE, MoneyWisePayeeList.class), false, myTrans);
707     }
708 
709     /**
710      * Build the asset menu for an item.
711      *
712      * @param <T>        the Asset type
713      * @param pMenu      the menu
714      * @param pIsAccount is this item the account rather than partner
715      * @param pList      the asset list
716      * @param pTrans     the transaction to build for
717      */
718     private static <T extends MoneyWiseAssetBase> void buildAssetMenu(final TethysUIScrollMenu<MoneyWiseTransAsset> pMenu,
719                                                                       final MoneyWiseAssetBaseList<T> pList,
720                                                                       final boolean pIsAccount,
721                                                                       final MoneyWiseTransaction pTrans) {
722         /* Record active item */
723         final MoneyWiseTransAsset myAccount = pTrans.getAccount();
724         final MoneyWiseTransCategory myCategory = pTrans.getCategory();
725         final MoneyWiseDataValidatorTrans<?> myValidator = pTrans.getList().getValidator();
726         final MoneyWiseTransAsset myCurr = pIsAccount
727                 ? myAccount
728                 : pTrans.getPartner();
729         TethysUIScrollItem<MoneyWiseTransAsset> myActive = null;
730         TethysUIScrollSubMenu<MoneyWiseTransAsset> myMenu = null;
731 
732         /* Loop through the available values */
733         final Iterator<T> myIterator = pList.iterator();
734         while (myIterator.hasNext()) {
735             final T myAsset = myIterator.next();
736 
737             /* Only process non-deleted/non-closed items */
738             boolean bIgnore = myAsset.isDeleted() || myAsset.isClosed();
739 
740             /* Check whether the asset is allowable for the owner */
741             bIgnore |= !(pIsAccount
742                     ? myValidator.isValidAccount(myAsset)
743                     : myValidator.isValidPartner(myAccount, myCategory, myAsset));
744             if (bIgnore) {
745                 continue;
746             }
747 
748             /* If this the first item */
749             if (myMenu == null) {
750                 /* Create a new subMenu and add it to the popUp */
751                 myMenu = pMenu.addSubMenu(pList.getItemType().getItemName());
752             }
753 
754             /* Create a new MenuItem and add it to the popUp */
755             final TethysUIScrollItem<MoneyWiseTransAsset> myItem = myMenu.getSubMenu().addItem(myAsset);
756 
757             /* If this is the active category */
758             if (myAsset.equals(myCurr)) {
759                 /* Record it */
760                 myActive = myItem;
761             }
762         }
763 
764         /* Ensure active item is visible */
765         if (myActive != null) {
766             myActive.scrollToItem();
767         }
768     }
769 
770     /**
771      * Build the holding asset menu for an item.
772      *
773      * @param pMenu      the menu
774      * @param pIsAccount is this item the account rather than partner
775      * @param pTrans     the transaction to build for
776      */
777     private static void buildHoldingMenu(final TethysUIScrollMenu<MoneyWiseTransAsset> pMenu,
778                                          final boolean pIsAccount,
779                                          final MoneyWiseTransaction pTrans) {
780         /* Record active item */
781         final MoneyWiseTransAsset myAccount = pTrans.getAccount();
782         final MoneyWiseTransCategory myCategory = pTrans.getCategory();
783         final MoneyWiseDataValidatorTrans<?> myValidator = pTrans.getList().getValidator();
784         final MoneyWiseTransAsset myCurr = pIsAccount
785                 ? myAccount
786                 : pTrans.getPartner();
787         TethysUIScrollItem<MoneyWiseTransAsset> myActive = null;
788         TethysUIScrollSubMenu<MoneyWiseTransAsset> myMenu = null;
789 
790         /* Access Portfolios and Holdings Map */
791         final MoneyWiseDataSet myData = pTrans.getDataSet();
792         final MoneyWisePortfolioList myPortfolios = myData.getPortfolios();
793         final MoneyWiseSecurityHoldingMap myMap = myPortfolios.getSecurityHoldingsMap();
794 
795         /* Loop through the Portfolios */
796         final Iterator<MoneyWisePortfolio> myPortIterator = myPortfolios.iterator();
797         while (myPortIterator.hasNext()) {
798             final MoneyWisePortfolio myPortfolio = myPortIterator.next();
799             TethysUIScrollSubMenu<MoneyWiseTransAsset> myCoreMenu = null;
800 
801             /* Ignore deleted or closed */
802             if (myPortfolio.isDeleted()
803                     || Boolean.TRUE.equals(myPortfolio.isClosed())) {
804                 continue;
805             }
806 
807             /* Look for existing and new holdings */
808             final Iterator<MoneyWiseSecurityHolding> myExistIterator = myMap.existingIterator(myPortfolio);
809             final Iterator<MoneyWiseSecurityHolding> myNewIterator = myMap.newIterator(myPortfolio);
810             if ((myExistIterator != null) || (myNewIterator != null)) {
811                 /* If there are existing elements */
812                 if (myExistIterator != null) {
813                     /* Loop through them */
814                     while (myExistIterator.hasNext()) {
815                         final MoneyWiseSecurityHolding myHolding = myExistIterator.next();
816                         final MoneyWiseSecurity mySecurity = myHolding.getSecurity();
817 
818                         /* Check whether the asset is allowable for the owner */
819                         final boolean bIgnore = !(pIsAccount
820                                 ? myValidator.isValidAccount(myHolding)
821                                 : myValidator.isValidPartner(myAccount, myCategory, myHolding));
822                         if (bIgnore) {
823                             continue;
824                         }
825 
826                         /* Ensure that hierarchy is created */
827                         if (myMenu == null) {
828                             /* Create a new JMenu and add it to the popUp */
829                             myMenu = pMenu.addSubMenu(MoneyWiseAssetType.SECURITYHOLDING.toString());
830                         }
831                         if (myCoreMenu == null) {
832                             /* Create a new Menu and add it to the popUp */
833                             myCoreMenu = myMenu.getSubMenu().addSubMenu(myPortfolio.getName());
834                         }
835 
836                         /* Add the item to the menu */
837                         final TethysUIScrollItem<MoneyWiseTransAsset> myItem = myCoreMenu.getSubMenu().addItem(myHolding, mySecurity.getName());
838 
839                         /* If this is the active holding */
840                         if (mySecurity.equals(myCurr)) {
841                             /* Record it */
842                             myActive = myItem;
843                         }
844                     }
845                 }
846 
847                 /* If there are new elements */
848                 if (myNewIterator != null) {
849                     /* Loop through them */
850                     TethysUIScrollSubMenu<MoneyWiseTransAsset> mySubMenu = null;
851                     while (myNewIterator.hasNext()) {
852                         final MoneyWiseSecurityHolding myHolding = myNewIterator.next();
853                         final MoneyWiseSecurity mySecurity = myHolding.getSecurity();
854 
855                         /* Check whether the asset is allowable for the owner */
856                         final boolean bIgnore = !(pIsAccount
857                                 ? myValidator.isValidAccount(myHolding)
858                                 : myValidator.isValidPartner(myAccount, myCategory, myHolding));
859                         if (bIgnore) {
860                             continue;
861                         }
862 
863                         /* Ensure that hierarchy is created */
864                         if (myMenu == null) {
865                             /* Create a new subMenu and add it to the popUp */
866                             myMenu = pMenu.addSubMenu(MoneyWiseAssetType.SECURITYHOLDING.toString());
867                         }
868                         if (myCoreMenu == null) {
869                             /* Create a new subMenu and add it to the popUp */
870                             myCoreMenu = myMenu.getSubMenu().addSubMenu(myPortfolio.getName());
871                         }
872                         if (mySubMenu == null) {
873                             /* Create a new subMenu */
874                             mySubMenu = myCoreMenu.getSubMenu().addSubMenu(MoneyWiseSecurityHolding.SECURITYHOLDING_NEW);
875                         }
876 
877                         /* Add the item to the menu */
878                         mySubMenu.getSubMenu().addItem(myHolding, mySecurity.getName());
879                     }
880                 }
881             }
882         }
883 
884         /* Ensure active item is visible */
885         if (myActive != null) {
886             myActive.scrollToItem();
887         }
888     }
889 
890     /**
891      * Build the category menu for an item.
892      *
893      * @param pMenu  the menu
894      * @param pEvent the event to build for
895      */
896     public void buildCategoryMenu(final TethysUIScrollMenu<MoneyWiseTransCategory> pMenu,
897                                   final MoneyWiseXAnalysisEvent pEvent) {
898         /* Clear the menu */
899         pMenu.removeAllItems();
900 
901         /* Record active item */
902         final MoneyWiseTransAsset myAccount = pEvent.getAccount();
903         final MoneyWiseTransCategory myCurr = pEvent.getCategory();
904         TethysUIScrollItem<MoneyWiseTransCategory> myActive = null;
905         TethysUIScrollItem<MoneyWiseTransCategory> myItem;
906 
907         /* Create a simple map for top-level categories */
908         final Map<String, TethysUIScrollSubMenu<MoneyWiseTransCategory>> myMap = new HashMap<>();
909 
910         /* Access Categories */
911         final MoneyWiseTransCategoryList myCategories = getDataList(MoneyWiseBasicDataType.TRANSCATEGORY, MoneyWiseTransCategoryList.class);
912         final MoneyWiseDataValidatorTrans<?> myValidator = pEvent.getTransaction().getList().getValidator();
913 
914         /* Loop through the available category values */
915         final Iterator<MoneyWiseTransCategory> myIterator = myCategories.iterator();
916         while (myIterator.hasNext()) {
917             final MoneyWiseTransCategory myCategory = myIterator.next();
918 
919             /* Only process non-deleted low-level items */
920             final MoneyWiseTransCategoryClass myClass = myCategory.getCategoryTypeClass();
921             boolean bIgnore = myCategory.isDeleted() || myClass.canParentCategory();
922 
923             /* Check whether the category is allowable for the owner */
924             bIgnore |= !myValidator.isValidCategory(myAccount, myCategory);
925             if (bIgnore) {
926                 continue;
927             }
928 
929             /* Determine parent */
930             final MoneyWiseTransCategory myParent = myCategory.getParentCategory();
931 
932             /* If we have a parent */
933             if (myParent != null) {
934                 final String myParentName = myParent.getName();
935                 final TethysUIScrollSubMenu<MoneyWiseTransCategory> myMenu = myMap.computeIfAbsent(myParentName, pMenu::addSubMenu);
936 
937                 /* Create a new MenuItem and add it to the subMenu */
938                 myItem = myMenu.getSubMenu().addItem(myCategory, myCategory.getSubCategory());
939 
940             } else {
941                 /* Create a new MenuItem and add it to the popUp */
942                 myItem = pMenu.addItem(myCategory);
943             }
944 
945             /* If this is the active category */
946             if (myCategory.equals(myCurr)) {
947                 /* Record it */
948                 myActive = myItem;
949             }
950         }
951 
952         /* Ensure active item is visible */
953         if (myActive != null) {
954             myActive.scrollToItem();
955         }
956     }
957 
958     /**
959      * Build the ReturnedAccount menu for an item.
960      *
961      * @param pMenu  the menu
962      * @param pEvent the event to build for
963      */
964     public void buildReturnedAccountMenu(final TethysUIScrollMenu<MoneyWiseTransAsset> pMenu,
965                                          final MoneyWiseXAnalysisEvent pEvent) {
966         /* Clear the menu */
967         pMenu.removeAllItems();
968 
969         /* Add possible items */
970         final MoneyWiseTransaction myTrans = pEvent.getTransaction();
971         buildAssetMenu(pMenu, getDataList(MoneyWiseBasicDataType.DEPOSIT, MoneyWiseDepositList.class), false, myTrans);
972         buildAssetMenu(pMenu, getDataList(MoneyWiseBasicDataType.PORTFOLIO, MoneyWisePortfolioList.class), false, myTrans);
973     }
974 
975     /**
976      * Build the possible TransactionTag list.
977      *
978      * @return the transaction tag iterator
979      */
980     public Iterator<MoneyWiseTransTag> buildTransactionTags() {
981         /* Create a list */
982         final List<MoneyWiseTransTag> myList = new ArrayList<>();
983 
984         /* Loop through the TransactionTags */
985         final Iterator<MoneyWiseTransTag> myIterator = getEditSet()
986                 .getDataList(MoneyWiseBasicDataType.TRANSTAG, MoneyWiseTransTagList.class).iterator();
987         while (myIterator.hasNext()) {
988             final MoneyWiseTransTag myTag = myIterator.next();
989 
990             /* Add to list if available */
991             if (!myTag.isDeleted()) {
992                 myList.add(myTag);
993             }
994         }
995 
996         /* Return the iterator */
997         return myList.iterator();
998     }
999 }