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.validate;
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.OceanusDateRange;
22  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusMoney;
23  import io.github.tonywasher.joceanus.oceanus.logger.OceanusLogManager;
24  import io.github.tonywasher.joceanus.oceanus.logger.OceanusLogger;
25  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseAssetBase;
26  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseAssetBase.MoneyWiseAssetBaseList;
27  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseAssetDirection;
28  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicDataType;
29  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseCash.MoneyWiseCashList;
30  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDeposit.MoneyWiseDepositList;
31  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseLoan.MoneyWiseLoanList;
32  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePayee;
33  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePayee.MoneyWisePayeeList;
34  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePortfolio;
35  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePortfolio.MoneyWisePortfolioList;
36  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityHolding;
37  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityHolding.MoneyWiseSecurityHoldingMap;
38  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransAsset;
39  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransCategory;
40  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransCategory.MoneyWiseTransCategoryList;
41  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransaction;
42  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransaction.MoneyWiseTransactionList;
43  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransCategoryClass;
44  import io.github.tonywasher.joceanus.prometheus.views.PrometheusEditSet;
45  
46  import java.util.Currency;
47  import java.util.Iterator;
48  
49  /**
50   * Transaction builder.
51   *
52   * @author Tony Washer
53   */
54  public class MoneyWiseValidateTransDefaults {
55      /**
56       * Logger.
57       */
58      private static final OceanusLogger LOGGER = OceanusLogManager.getLogger(MoneyWiseValidateTransDefaults.class);
59  
60      /**
61       * The transaction validator.
62       */
63      private final MoneyWiseValidateTransaction theValidator;
64  
65      /**
66       * The EditSet.
67       */
68      private PrometheusEditSet theEditSet;
69  
70      /**
71       * The Date Range.
72       */
73      private OceanusDateRange theRange;
74  
75      /**
76       * Constructor.
77       *
78       * @param pValidator the validator
79       */
80      public MoneyWiseValidateTransDefaults(final MoneyWiseValidateTransaction pValidator) {
81          theValidator = pValidator;
82      }
83  
84      /**
85       * Obtain range.
86       *
87       * @return the date range
88       */
89      public OceanusDateRange getRange() {
90          return theRange;
91      }
92  
93      /**
94       * Set range.
95       *
96       * @param pRange the date range
97       */
98      public void setRange(final OceanusDateRange pRange) {
99          theRange = pRange;
100     }
101 
102     /**
103      * autoCorrect transaction after change.
104      *
105      * @param pTrans the transaction
106      * @throws OceanusException on error
107      */
108     public void autoCorrect(final MoneyWiseTransaction pTrans) throws OceanusException {
109         /* Access details */
110         final MoneyWiseTransAsset myAccount = pTrans.getAccount();
111         MoneyWiseTransAsset myPartner = pTrans.getPartner();
112         MoneyWiseTransCategory myCategory = pTrans.getCategory();
113         final MoneyWiseAssetDirection myDir = pTrans.getDirection();
114         final OceanusMoney myAmount = pTrans.getAmount();
115         final Currency myCurrency = myAccount.getCurrency();
116         theEditSet = theValidator.getEditSet();
117 
118         /* Check that category is valid */
119         if (!theValidator.isValidCategory(myAccount, myCategory)) {
120             /* Determine valid category */
121             myCategory = getDefaultCategoryForAccount(myAccount);
122             pTrans.setCategory(myCategory);
123         }
124 
125         /* Check that direction is valid */
126         if (!theValidator.isValidDirection(myAccount, myCategory, myDir)) {
127             /* Reverse direction */
128             pTrans.switchDirection();
129         }
130 
131         /* Check that partner is valid */
132         if (!theValidator.isValidPartner(myAccount, myCategory, myPartner)) {
133             /* Determine valid partner */
134             myPartner = getDefaultPartnerForAccountAndCategory(myAccount, myCategory);
135             pTrans.setPartner(myPartner);
136         }
137 
138         /* If we need to null money */
139         if (myCategory.getCategoryTypeClass().needsNullAmount()) {
140             if (myAmount != null) {
141                 /* Create a zero amount */
142                 pTrans.setAmount(null);
143             }
144 
145             /* Else money is required */
146         } else {
147             if (myAmount == null) {
148                 /* Create a zero amount */
149                 pTrans.setAmount(new OceanusMoney(myCurrency));
150 
151                 /* If we need to change currency */
152             } else if (!myCurrency.equals(myAmount.getCurrency())) {
153                 /* Convert the currency */
154                 pTrans.setAmount(myAmount.changeCurrency(myCurrency));
155             }
156         }
157 
158         /* AutoCorrect the InfoSet */
159         final MoneyWiseValidateTransInfoSet myInfoSet = theValidator.getInfoSetValidator();
160         myInfoSet.autoCorrect(pTrans.getInfoSet());
161     }
162 
163     /**
164      * Build empty transaction.
165      *
166      * @return the new transaction
167      */
168     private MoneyWiseTransaction newTransaction() {
169         /* Obtain a new transaction */
170         return new MoneyWiseTransaction(theEditSet.getDataList(MoneyWiseBasicDataType.TRANSACTION, MoneyWiseTransactionList.class));
171     }
172 
173     /**
174      * Build default transaction.
175      *
176      * @param pKey the key to base the new transaction around (or null)
177      * @return the new transaction (or null if no possible transaction)
178      */
179     public MoneyWiseTransaction buildTransaction(final Object pKey) {
180         /* Protect against exceptions */
181         try {
182             theEditSet = theValidator.getEditSet();
183             if (pKey == null) {
184                 /* Build default transaction */
185                 return buildDefaultTransaction();
186             }
187             if (pKey instanceof MoneyWisePayee myPayee) {
188                 /* Build default payee transaction */
189                 return buildDefaultTransactionForPayee(myPayee);
190             }
191             if (pKey instanceof MoneyWiseSecurityHolding myHolding) {
192                 /* Build default holding transaction */
193                 return buildDefaultTransactionForHolding(myHolding);
194             }
195             if (pKey instanceof MoneyWiseTransAsset myAsset) {
196                 /* Build default account transaction */
197                 return buildDefaultTransactionForAccount(myAsset);
198             }
199             if (pKey instanceof MoneyWiseTransCategory myCategory) {
200                 /* Build default category transaction */
201                 return buildDefaultTransactionForCategory(myCategory);
202             }
203         } catch (OceanusException e) {
204             LOGGER.error("Unable to build transaction", e);
205         }
206 
207         /* Unrecognised key */
208         return null;
209     }
210 
211     /**
212      * Build standard details.
213      *
214      * @param pTrans the transaction to build
215      * @throws OceanusException on error
216      */
217     private void buildStandardDetails(final MoneyWiseTransaction pTrans) throws OceanusException {
218         /* Access standard range */
219         final OceanusDateRange myRange = theRange;
220 
221         /* Set default direction */
222         pTrans.setDirection(MoneyWiseAssetDirection.TO);
223 
224         /* Determine date */
225         OceanusDate myDate = new OceanusDate();
226         final int iResult = myRange.compareToDate(myDate);
227         if (iResult < 0) {
228             myDate = myRange.getEnd();
229         } else if (iResult > 0) {
230             myDate = myRange.getStart();
231         }
232         pTrans.setDate(myDate);
233 
234         /* Create a zero amount */
235         final MoneyWiseTransAsset myAccount = pTrans.getAccount();
236         final Currency myCurrency = myAccount.getCurrency();
237         pTrans.setAmount(new OceanusMoney(myCurrency));
238     }
239 
240     /**
241      * Build default transaction.
242      *
243      * @return the valid transaction (or null)
244      * @throws OceanusException on error
245      */
246     private MoneyWiseTransaction buildDefaultTransaction() throws OceanusException {
247         final MoneyWiseTransCategory myCategory = getDefaultCategory();
248         if (myCategory == null) {
249             return null;
250         }
251 
252         /* Look for transaction for this category */
253         return buildDefaultTransactionForCategory(myCategory);
254     }
255 
256     /**
257      * Build default transaction for payee.
258      *
259      * @param pPayee the payee to build for
260      * @return the valid transaction (or null)
261      * @throws OceanusException on error
262      */
263     private MoneyWiseTransaction buildDefaultTransactionForPayee(final MoneyWisePayee pPayee) throws OceanusException {
264         /* Check for closed/hidden payee */
265         if (pPayee.isClosed() || pPayee.isHidden()) {
266             return null;
267         }
268 
269         /* Build an empty transaction */
270         final MoneyWiseTransaction myTrans = newTransaction();
271 
272         /* Record the payee */
273         myTrans.setPartner(pPayee);
274 
275         /* Build default category */
276         final MoneyWiseTransCategory myCategory = getDefaultCategory();
277         if (myCategory == null) {
278             return null;
279         }
280         myTrans.setCategory(myCategory);
281 
282         /* Build default account */
283         final MoneyWiseTransAsset myAccount = getDefaultAccountForCategory(myCategory);
284         if (myAccount == null) {
285             return null;
286         }
287         myTrans.setAccount(myAccount);
288 
289         /* Check that we are valid after all this */
290         if (!theValidator.isValidPartner(myAccount, myCategory, pPayee)) {
291             return null;
292         }
293 
294         /* build standard details */
295         buildStandardDetails(myTrans);
296 
297         /* AutoCorrect the transaction */
298         autoCorrect(myTrans);
299 
300         /* Return the new transaction */
301         return myTrans;
302     }
303 
304     /**
305      * Build default transaction for category.
306      *
307      * @param pCategory the category to build for
308      * @return the valid transaction (or null)
309      * @throws OceanusException on error
310      */
311     private MoneyWiseTransaction buildDefaultTransactionForCategory(final MoneyWiseTransCategory pCategory) throws OceanusException {
312         /* Check for hidden category */
313         if (pCategory.isHidden()) {
314             return null;
315         }
316 
317         /* Build an empty transaction */
318         final MoneyWiseTransaction myTrans = newTransaction();
319 
320         /* Record the category */
321         myTrans.setCategory(pCategory);
322 
323         /* Build default account category */
324         final MoneyWiseTransAsset myAccount = getDefaultAccountForCategory(pCategory);
325         if (myAccount == null) {
326             return null;
327         }
328         myTrans.setAccount(myAccount);
329 
330         /* Build default partner */
331         final MoneyWiseTransAsset myPartner = getDefaultPartnerForAccountAndCategory(myAccount, pCategory);
332         if (myPartner == null) {
333             return null;
334         }
335         myTrans.setPartner(myPartner);
336 
337         /* build standard details */
338         buildStandardDetails(myTrans);
339 
340         /* AutoCorrect the transaction */
341         autoCorrect(myTrans);
342 
343         /* Return the new transaction */
344         return myTrans;
345     }
346 
347     /**
348      * Build default transaction for Deposit/Loan/Cash.
349      *
350      * @param pAccount the Deposit/Loan/Cash to build for
351      * @return the valid transaction (or null)
352      * @throws OceanusException on error
353      */
354     private MoneyWiseTransaction buildDefaultTransactionForAccount(final MoneyWiseTransAsset pAccount) throws OceanusException {
355         /* Check for closed account */
356         if (pAccount.isClosed()) {
357             return null;
358         }
359 
360         /* Build an empty transaction */
361         final MoneyWiseTransaction myTrans = newTransaction();
362 
363         /* Record the account */
364         myTrans.setAccount(pAccount);
365 
366         /* Build default expense category */
367         final MoneyWiseTransCategory myCategory = getDefaultCategory();
368         if (myCategory == null) {
369             return null;
370         }
371         myTrans.setCategory(myCategory);
372 
373         /* Build default partner */
374         final MoneyWiseTransAsset myPartner = getDefaultPartnerForAccountAndCategory(pAccount, myCategory);
375         if (myPartner == null) {
376             return null;
377         }
378         myTrans.setPartner(myPartner);
379 
380         /* build standard details */
381         buildStandardDetails(myTrans);
382 
383         /* AutoCorrect the transaction */
384         autoCorrect(myTrans);
385 
386         /* Return the new transaction */
387         return myTrans;
388     }
389 
390     /**
391      * Build default transaction for securityHolding.
392      *
393      * @param pHolding the SecurityHolding to build for
394      * @return the valid transaction (or null)
395      * @throws OceanusException on error
396      */
397     private MoneyWiseTransaction buildDefaultTransactionForHolding(final MoneyWiseSecurityHolding pHolding) throws OceanusException {
398         /* Check for closed holding */
399         if (pHolding.isClosed()) {
400             return null;
401         }
402 
403         /* Build an empty transaction */
404         final MoneyWiseTransaction myTrans = newTransaction();
405 
406         /* Record the account */
407         myTrans.setAccount(pHolding);
408 
409         /* Build default category */
410         final MoneyWiseTransCategory myCategory = getDefaultCategoryForAccount(pHolding);
411         myTrans.setCategory(myCategory);
412 
413         /* Build default partner */
414         final MoneyWiseTransAsset myPartner = getDefaultPartnerForAccountAndCategory(pHolding, myCategory);
415         if (myPartner == null) {
416             return null;
417         }
418         myTrans.setPartner(myPartner);
419 
420         /* build standard details */
421         buildStandardDetails(myTrans);
422 
423         /* AutoCorrect the transaction */
424         autoCorrect(myTrans);
425 
426         /* Return the new transaction */
427         return myTrans;
428     }
429 
430     /**
431      * Obtain default account for category.
432      *
433      * @param pCategory the category
434      * @return the default account
435      */
436     private MoneyWiseTransAsset getDefaultAccountForCategory(final MoneyWiseTransCategory pCategory) {
437         /* Try deposits/cash/loans */
438         MoneyWiseTransAsset myAccount = getDefaultAssetForCategory(theEditSet.getDataList(MoneyWiseBasicDataType.DEPOSIT, MoneyWiseDepositList.class), pCategory);
439         if (myAccount == null) {
440             myAccount = getDefaultAssetForCategory(theEditSet.getDataList(MoneyWiseBasicDataType.CASH, MoneyWiseCashList.class), pCategory);
441         }
442         if (myAccount == null) {
443             myAccount = getDefaultAssetForCategory(theEditSet.getDataList(MoneyWiseBasicDataType.LOAN, MoneyWiseLoanList.class), pCategory);
444         }
445 
446         /* Try holdings */
447         if (myAccount == null) {
448             myAccount = getDefaultHolding(pCategory);
449         }
450 
451         /* Try portfolios */
452         if (myAccount == null) {
453             myAccount = getDefaultAssetForCategory(theEditSet.getDataList(MoneyWiseBasicDataType.PORTFOLIO, MoneyWisePortfolioList.class), pCategory);
454         }
455 
456         /* Return the account */
457         return myAccount;
458     }
459 
460     /**
461      * Obtain default category.
462      *
463      * @return the default category
464      */
465     private MoneyWiseTransCategory getDefaultCategory() {
466         /* Look for category in order of expense, income, transfer */
467         MoneyWiseTransCategory myCategory = getDefaultCategory(CategoryType.EXPENSE);
468         if (myCategory == null) {
469             myCategory = getDefaultCategory(CategoryType.INCOME);
470         }
471         if (myCategory == null) {
472             myCategory = getDefaultCategory(CategoryType.TRANSFER);
473         }
474 
475         /* Return category */
476         return myCategory;
477     }
478 
479     /**
480      * Obtain default category.
481      *
482      * @param pType the category type
483      * @return the default category
484      */
485     private MoneyWiseTransCategory getDefaultCategory(final CategoryType pType) {
486         /* Access Categories */
487         final MoneyWiseTransCategoryList myCategories = theEditSet.getDataList(MoneyWiseBasicDataType.TRANSCATEGORY, MoneyWiseTransCategoryList.class);
488 
489         /* Loop through the available category values */
490         final Iterator<MoneyWiseTransCategory> myIterator = myCategories.iterator();
491         while (myIterator.hasNext()) {
492             final MoneyWiseTransCategory myCategory = myIterator.next();
493 
494             /* Only process non-deleted low-level items */
495             final MoneyWiseTransCategoryClass myClass = myCategory.getCategoryTypeClass();
496             if (myCategory.isDeleted() || myClass.canParentCategory()) {
497                 continue;
498             }
499 
500             /* Switch on type */
501             switch (pType) {
502                 case EXPENSE:
503                     if (myClass.isExpense()) {
504                         return myCategory;
505                     }
506                     break;
507                 case INCOME:
508                     if (myClass.isIncome()) {
509                         return myCategory;
510                     }
511                     break;
512                 case TRANSFER:
513                 default:
514                     if (myClass.isTransfer()) {
515                         return myCategory;
516                     }
517                     break;
518             }
519         }
520 
521         /* No category available */
522         return null;
523     }
524 
525     /**
526      * Obtain default category for account.
527      *
528      * @param pAccount the account
529      * @return the default category
530      */
531     private MoneyWiseTransCategory getDefaultCategoryForAccount(final MoneyWiseTransAsset pAccount) {
532         /* Access Categories */
533         final MoneyWiseTransCategoryList myCategories = theEditSet.getDataList(MoneyWiseBasicDataType.TRANSCATEGORY, MoneyWiseTransCategoryList.class);
534 
535         /* Loop through the available category values */
536         final Iterator<MoneyWiseTransCategory> myIterator = myCategories.iterator();
537         while (myIterator.hasNext()) {
538             final MoneyWiseTransCategory myCategory = myIterator.next();
539 
540             /* Only process non-deleted low-level items */
541             final MoneyWiseTransCategoryClass myClass = myCategory.getCategoryTypeClass();
542             if (myCategory.isDeleted() || myClass.canParentCategory()) {
543                 continue;
544             }
545 
546             /* Check whether the category is allowable for the owner */
547             if (theValidator.isValidCategory(pAccount, myCategory)) {
548                 return myCategory;
549             }
550         }
551 
552         /* No category available */
553         throw new IllegalArgumentException();
554     }
555 
556     /**
557      * Obtain default partner for account and category.
558      *
559      * @param pAccount  the account
560      * @param pCategory the category
561      * @return the default partner
562      */
563     private MoneyWiseTransAsset getDefaultPartnerForAccountAndCategory(final MoneyWiseTransAsset pAccount,
564                                                                        final MoneyWiseTransCategory pCategory) {
565         /* Try Payees */
566         MoneyWiseTransAsset myPartner = getDefaultPartnerAsset(theEditSet.getDataList(MoneyWiseBasicDataType.PAYEE, MoneyWisePayeeList.class), pAccount, pCategory);
567 
568         /* Try deposits/cash/loans */
569         if (myPartner == null) {
570             myPartner = getDefaultPartnerAsset(theEditSet.getDataList(MoneyWiseBasicDataType.DEPOSIT, MoneyWiseDepositList.class), pAccount, pCategory);
571         }
572         if (myPartner == null) {
573             myPartner = getDefaultPartnerAsset(theEditSet.getDataList(MoneyWiseBasicDataType.CASH, MoneyWiseCashList.class), pAccount, pCategory);
574         }
575         if (myPartner == null) {
576             myPartner = getDefaultPartnerAsset(theEditSet.getDataList(MoneyWiseBasicDataType.LOAN, MoneyWiseLoanList.class), pAccount, pCategory);
577         }
578 
579         /* Try portfolios */
580         if (myPartner == null) {
581             myPartner = getDefaultPartnerAsset(theEditSet.getDataList(MoneyWiseBasicDataType.PORTFOLIO, MoneyWisePortfolioList.class), pAccount, pCategory);
582         }
583 
584         /* Try holdings */
585         if (myPartner == null) {
586             myPartner = getDefaultPartnerHolding(pAccount, pCategory);
587         }
588 
589         /* Return the partner */
590         return myPartner;
591     }
592 
593     /**
594      * Obtain the default account from an asset list.
595      *
596      * @param <X>       the Asset type
597      * @param pList     the list to select from
598      * @param pCategory the category
599      * @return the default partner or null
600      */
601     private <X extends MoneyWiseAssetBase> MoneyWiseTransAsset getDefaultAssetForCategory(final MoneyWiseAssetBaseList<X> pList,
602                                                                                           final MoneyWiseTransCategory pCategory) {
603         /* Loop through the available values */
604         final Iterator<X> myIterator = pList.iterator();
605         while (myIterator.hasNext()) {
606             final X myAsset = myIterator.next();
607 
608             /* Only process non-deleted, non-closed items */
609             if (myAsset.isDeleted() || myAsset.isClosed()) {
610                 continue;
611             }
612 
613             /* Check whether the asset is allowable for the owner */
614             if (theValidator.isValidCategory(myAsset, pCategory)) {
615                 return myAsset;
616             }
617         }
618 
619         /* No asset available */
620         return null;
621     }
622 
623     /**
624      * Obtain the default partner from an asset list.
625      *
626      * @param <X>       the Asset type
627      * @param pList     the list to select from
628      * @param pAccount  the account
629      * @param pCategory the category
630      * @return the default partner or null
631      */
632     private <X extends MoneyWiseAssetBase> MoneyWiseTransAsset getDefaultPartnerAsset(final MoneyWiseAssetBaseList<X> pList,
633                                                                                       final MoneyWiseTransAsset pAccount,
634                                                                                       final MoneyWiseTransCategory pCategory) {
635         /* Loop through the available values */
636         final Iterator<X> myIterator = pList.iterator();
637         while (myIterator.hasNext()) {
638             final X myAsset = myIterator.next();
639 
640             /* Only process non-deleted, non-closed items */
641             if (myAsset.isDeleted() || myAsset.isClosed()) {
642                 continue;
643             }
644 
645             /* Check whether the asset is allowable for the owner */
646             if (theValidator.isValidPartner(pAccount, pCategory, myAsset)) {
647                 return myAsset;
648             }
649         }
650 
651         /* No asset available */
652         return null;
653     }
654 
655     /**
656      * Obtain the default security holding from the security map.
657      *
658      * @param pCategory the category
659      * @return the default partner
660      */
661     private MoneyWiseSecurityHolding getDefaultHolding(final MoneyWiseTransCategory pCategory) {
662         /* Access Portfolios and Holdings Map */
663         final MoneyWisePortfolioList myPortfolios = theEditSet.getDataList(MoneyWiseBasicDataType.PORTFOLIO, MoneyWisePortfolioList.class);
664         final MoneyWiseSecurityHoldingMap myMap = myPortfolios.getSecurityHoldingsMap();
665 
666         /* Loop through the Portfolios */
667         final Iterator<MoneyWisePortfolio> myPortIterator = myPortfolios.iterator();
668         while (myPortIterator.hasNext()) {
669             final MoneyWisePortfolio myPortfolio = myPortIterator.next();
670 
671             /* Ignore deleted or closed */
672             if (myPortfolio.isDeleted() || myPortfolio.isClosed()) {
673                 continue;
674             }
675 
676             /* Look for existing holdings */
677             final Iterator<MoneyWiseSecurityHolding> myExistIterator = myMap.existingIterator(myPortfolio);
678             if (myExistIterator != null) {
679                 /* Loop through them */
680                 while (myExistIterator.hasNext()) {
681                     final MoneyWiseSecurityHolding myHolding = myExistIterator.next();
682 
683                     /* Check whether the asset is allowable for the combination */
684                     if (theValidator.isValidCategory(myHolding, pCategory)) {
685                         return myHolding;
686                     }
687                 }
688             }
689         }
690 
691         /* No holding available */
692         return null;
693     }
694 
695     /**
696      * Obtain the default partner security holding from the security map.
697      *
698      * @param pAccount  the account
699      * @param pCategory the category
700      * @return the default partner
701      */
702     private MoneyWiseSecurityHolding getDefaultPartnerHolding(final MoneyWiseTransAsset pAccount,
703                                                               final MoneyWiseTransCategory pCategory) {
704         /* Access Portfolios and Holdings Map */
705         final MoneyWisePortfolioList myPortfolios = theEditSet.getDataList(MoneyWiseBasicDataType.PORTFOLIO, MoneyWisePortfolioList.class);
706         final MoneyWiseSecurityHoldingMap myMap = myPortfolios.getSecurityHoldingsMap();
707 
708         /* Loop through the Portfolios */
709         final Iterator<MoneyWisePortfolio> myPortIterator = myPortfolios.iterator();
710         while (myPortIterator.hasNext()) {
711             final MoneyWisePortfolio myPortfolio = myPortIterator.next();
712 
713             /* Ignore deleted or closed */
714             if (myPortfolio.isDeleted() || myPortfolio.isClosed()) {
715                 continue;
716             }
717 
718             /* Look for existing holdings */
719             final Iterator<MoneyWiseSecurityHolding> myExistIterator = myMap.existingIterator(myPortfolio);
720             if (myExistIterator != null) {
721                 /* Loop through them */
722                 while (myExistIterator.hasNext()) {
723                     final MoneyWiseSecurityHolding myHolding = myExistIterator.next();
724 
725                     /* Check whether the asset is allowable for the combination */
726                     if (theValidator.isValidPartner(pAccount, pCategory, myHolding)) {
727                         return myHolding;
728                     }
729                 }
730             }
731         }
732 
733         /* No holding available */
734         return null;
735     }
736 
737     /**
738      * Category types.
739      */
740     private enum CategoryType {
741         /**
742          * Expense.
743          */
744         EXPENSE,
745 
746         /**
747          * Income.
748          */
749         INCOME,
750 
751         /**
752          * Transfer.
753          */
754         TRANSFER;
755     }
756 }