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