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.lethe.data.analysis.analyse;
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.decimal.OceanusMoney;
22  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusPrice;
23  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRate;
24  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRatio;
25  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusUnits;
26  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
27  import io.github.tonywasher.joceanus.oceanus.profile.OceanusProfile;
28  import io.github.tonywasher.joceanus.metis.field.MetisFieldItem;
29  import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
30  import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceManager;
31  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseAssetBase;
32  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseAssetType;
33  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicDataType;
34  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseCash;
35  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDataSet;
36  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDeposit;
37  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseLoan;
38  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePortfolio;
39  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePortfolio.MoneyWisePortfolioList;
40  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity;
41  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityHolding;
42  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityHolding.MoneyWiseSecurityHoldingMap;
43  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityPrice.MoneyWiseSecurityPriceDataMap;
44  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTax.MoneyWiseTaxCredit;
45  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransAsset;
46  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransCategory;
47  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransTag;
48  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransaction;
49  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransaction.MoneyWiseTransactionList;
50  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWisePayeeClass;
51  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWisePortfolioClass;
52  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseSecurityClass;
53  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransCategoryClass;
54  import io.github.tonywasher.joceanus.moneywise.exc.MoneyWiseLogicException;
55  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysis;
56  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisAccountBucket;
57  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisCashBucket.MoneyWiseAnalysisCashBucketList;
58  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisDataResource;
59  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisDepositBucket.MoneyWiseAnalysisDepositBucketList;
60  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisLoanBucket.MoneyWiseAnalysisLoanBucketList;
61  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisPayeeBucket;
62  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisPayeeBucket.MoneyWiseAnalysisPayeeBucketList;
63  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisPortfolioBucket;
64  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisPortfolioBucket.MoneyWiseAnalysisPortfolioBucketList;
65  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisPortfolioCashBucket;
66  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisSecurityBucket;
67  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisTaxBasisBucket.MoneyWiseAnalysisTaxBasisBucketList;
68  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisTransCategoryBucket;
69  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisTransCategoryBucket.MoneyWiseAnalysisTransCategoryBucketList;
70  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisTransTagBucket.MoneyWiseAnalysisTransTagBucketList;
71  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisTransactionHelper;
72  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.values.MoneyWiseAnalysisSecurityAttr;
73  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.values.MoneyWiseAnalysisSecurityValues;
74  import io.github.tonywasher.joceanus.moneywise.tax.MoneyWiseCashType;
75  import io.github.tonywasher.joceanus.prometheus.views.PrometheusEditSet;
76  
77  import java.util.Currency;
78  import java.util.Iterator;
79  import java.util.List;
80  import java.util.Objects;
81  
82  /**
83   * Class to analyse transactions.
84   *
85   * @author Tony Washer
86   */
87  public class MoneyWiseAnalysisTransAnalyser
88          implements MetisFieldItem {
89      /**
90       * Local Report fields.
91       */
92      private static final String ERROR_CATEGORY = "Unexpected Category Type: ";
93  
94      /**
95       * Local Report fields.
96       */
97      private static final MetisFieldSet<MoneyWiseAnalysisTransAnalyser> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseAnalysisTransAnalyser.class);
98  
99      /*
100      * Declare Fields.
101      */
102     static {
103         FIELD_DEFS.declareLocalField(MoneyWiseAnalysisDataResource.ANALYSIS_NAME, MoneyWiseAnalysisTransAnalyser::getAnalysis);
104     }
105 
106     /**
107      * The Amount Tax threshold for "small" transactions (£3000).
108      */
109     private static final OceanusMoney LIMIT_VALUE = OceanusMoney.getWholeUnits(3000);
110 
111     /**
112      * The Rate Tax threshold for "small" transactions (5%).
113      */
114     private static final OceanusRate LIMIT_RATE = OceanusRate.getWholePercentage(5);
115 
116     /**
117      * The security holding map.
118      */
119     private final MoneyWiseSecurityHoldingMap theHoldingMap;
120 
121     /**
122      * The security price map.
123      */
124     private final MoneyWiseSecurityPriceDataMap thePriceMap;
125 
126     /**
127      * The analysis.
128      */
129     private final MoneyWiseAnalysis theAnalysis;
130 
131     /**
132      * The transaction helper.
133      */
134     private final MoneyWiseAnalysisTransactionHelper theHelper;
135 
136     /**
137      * The deposit bucket list.
138      */
139     private final MoneyWiseAnalysisDepositBucketList theDepositBuckets;
140 
141     /**
142      * The cash bucket list.
143      */
144     private final MoneyWiseAnalysisCashBucketList theCashBuckets;
145 
146     /**
147      * The deposit bucket list.
148      */
149     private final MoneyWiseAnalysisLoanBucketList theLoanBuckets;
150 
151     /**
152      * The portfolio bucket list.
153      */
154     private final MoneyWiseAnalysisPortfolioBucketList thePortfolioBuckets;
155 
156     /**
157      * The payee bucket list.
158      */
159     private final MoneyWiseAnalysisPayeeBucketList thePayeeBuckets;
160 
161     /**
162      * The transaction category buckets.
163      */
164     private final MoneyWiseAnalysisTransCategoryBucketList theCategoryBuckets;
165 
166     /**
167      * The transactionTag buckets.
168      */
169     private final MoneyWiseAnalysisTransTagBucketList theTagBuckets;
170 
171     /**
172      * The taxBasis buckets.
173      */
174     private final MoneyWiseAnalysisTaxBasisBucketList theTaxBasisBuckets;
175 
176     /**
177      * The taxMan account.
178      */
179     private final MoneyWiseAnalysisPayeeBucket theTaxMan;
180 
181     /**
182      * The statePension account.
183      */
184     private final MoneyWiseAnalysisSecurityBucket theStatePension;
185 
186     /**
187      * The profile.
188      */
189     private final OceanusProfile theProfile;
190 
191     /**
192      * Constructor for a complete set of accounts.
193      *
194      * @param pTask          the profiled task
195      * @param pEditSet       the EditSet to analyse
196      * @param pPreferenceMgr the preference manager
197      * @throws OceanusException on error
198      */
199     public MoneyWiseAnalysisTransAnalyser(final OceanusProfile pTask,
200                                           final PrometheusEditSet pEditSet,
201                                           final MetisPreferenceManager pPreferenceMgr) throws OceanusException {
202         /* Start a new task */
203         theProfile = pTask;
204         final OceanusProfile myTask = theProfile.startTask("analyseTransactions");
205         final MoneyWiseDataSet myDataSet = (MoneyWiseDataSet) pEditSet.getDataSet();
206 
207         /* Store the parameters */
208         theHoldingMap = pEditSet.getDataList(MoneyWiseBasicDataType.PORTFOLIO, MoneyWisePortfolioList.class).getSecurityHoldingsMap();
209         thePriceMap = myDataSet.getSecurityPriceDataMap();
210 
211         /* Access the lists */
212         final MoneyWiseTransactionList myTrans = pEditSet.getDataList(MoneyWiseBasicDataType.TRANSACTION, MoneyWiseTransactionList.class);
213 
214         /* Create a new analysis */
215         myTask.startTask("Initialise");
216         theAnalysis = new MoneyWiseAnalysis(pEditSet, pPreferenceMgr);
217 
218         /* Create new helper and set opening balances */
219         theHelper = new MoneyWiseAnalysisTransactionHelper(myDataSet);
220         theAnalysis.addOpeningBalances(theHelper);
221 
222         /* Access details from the analysis */
223         theDepositBuckets = theAnalysis.getDeposits();
224         theCashBuckets = theAnalysis.getCash();
225         theLoanBuckets = theAnalysis.getLoans();
226         thePortfolioBuckets = theAnalysis.getPortfolios();
227         thePayeeBuckets = theAnalysis.getPayees();
228         theCategoryBuckets = theAnalysis.getTransCategories();
229         theTagBuckets = theAnalysis.getTransactionTags();
230         theTaxBasisBuckets = theAnalysis.getTaxBasis();
231         theTaxMan = thePayeeBuckets.getBucket(MoneyWisePayeeClass.TAXMAN);
232 
233         /* Access the StatePension security holding */
234         theStatePension = getStatePension(myDataSet);
235 
236         /* Loop through the Transactions extracting relevant elements */
237         myTask.startTask("Transactions");
238         final Iterator<MoneyWiseTransaction> myIterator = myTrans.iterator();
239         while (myIterator.hasNext()) {
240             final MoneyWiseTransaction myCurr = myIterator.next();
241 
242             /* Ignore deleted/header transactions */
243             if (myCurr.isDeleted() || myCurr.isHeader()) {
244                 continue;
245             }
246 
247             /* Touch underlying items */
248             myCurr.touchUnderlyingItems();
249 
250             /* Process the transaction in the report set */
251             processTransaction(myCurr);
252         }
253 
254         /* Complete the task */
255         myTask.end();
256     }
257 
258     /**
259      * Obtain statePension bucket.
260      *
261      * @param pData the dataSet
262      * @return the statePension bucket
263      */
264     private MoneyWiseAnalysisSecurityBucket getStatePension(final MoneyWiseDataSet pData) {
265         /* Access the singular portfolio and security */
266         final MoneyWisePortfolio myPensionPort = pData.getPortfolios().getSingularClass(MoneyWisePortfolioClass.PENSION);
267         final MoneyWiseSecurity myStatePension = pData.getSecurities().getSingularClass(MoneyWiseSecurityClass.STATEPENSION);
268 
269         /* If they exist, access the bucket */
270         if (myPensionPort != null
271                 && myStatePension != null) {
272             final MoneyWiseSecurityHolding myHolding = pData.getPortfolios().getSecurityHoldingsMap().declareHolding(myPensionPort, myStatePension);
273             return thePortfolioBuckets.getBucket(myHolding);
274         }
275 
276         /* Default to no bucket */
277         return null;
278     }
279 
280     @Override
281     public MetisFieldSet<MoneyWiseAnalysisTransAnalyser> getDataFieldSet() {
282         return FIELD_DEFS;
283     }
284 
285     @Override
286     public String formatObject(final OceanusDataFormatter pFormatter) {
287         return FIELD_DEFS.getName();
288     }
289 
290     /**
291      * Obtain the analysis.
292      *
293      * @return the analysis
294      */
295     public MoneyWiseAnalysis getAnalysis() {
296         return theAnalysis;
297     }
298 
299     /**
300      * Mark active accounts.
301      *
302      * @throws OceanusException on error
303      */
304     public void postProcessAnalysis() throws OceanusException {
305         /* Start a new task */
306         final OceanusProfile myTask = theProfile.startTask("postProcessAnalysis");
307         myTask.startTask("markActiveAccounts");
308 
309         /* Mark relevant accounts */
310         theDepositBuckets.markActiveAccounts();
311         theCashBuckets.markActiveAccounts();
312         theLoanBuckets.markActiveAccounts();
313 
314         /* Mark relevant securities */
315         thePortfolioBuckets.markActiveSecurities();
316 
317         /* Complete the task */
318         myTask.end();
319     }
320 
321     /**
322      * Process a transaction.
323      *
324      * @param pTrans the transaction to process
325      * @throws OceanusException on error
326      */
327     private void processTransaction(final MoneyWiseTransaction pTrans) throws OceanusException {
328         /* Declare to helper */
329         theHelper.setTransaction(pTrans);
330 
331         /* Access key details */
332         final MoneyWiseTransAsset myDebitAsset = theHelper.getDebitAsset();
333         final MoneyWiseTransAsset myCreditAsset = theHelper.getCreditAsset();
334         final MoneyWiseTaxCredit myYear = pTrans.getTaxYear();
335 
336         /* Look for tags */
337         final List<MoneyWiseTransTag> myTags = pTrans.getTransactionTags();
338         if (myTags != null) {
339             /* Process the transaction tags */
340             theTagBuckets.processTransaction(pTrans, myTags.iterator());
341         }
342 
343         /* If the event relates to a security item, split out the workings */
344         if (myDebitAsset instanceof MoneyWiseSecurityHolding) {
345             /* Process as a Security transaction */
346             processDebitSecurityTransaction((MoneyWiseSecurityHolding) myDebitAsset, myCreditAsset);
347 
348             /* If the event relates to a security item, split out the workings */
349         } else if (myCreditAsset instanceof MoneyWiseSecurityHolding) {
350             /* Process as a Security transaction */
351             processCreditSecurityTransaction(myDebitAsset, (MoneyWiseSecurityHolding) myCreditAsset);
352 
353             /* Else handle the portfolio transfer */
354         } else if (myDebitAsset instanceof MoneyWisePortfolio
355                 && myCreditAsset instanceof MoneyWisePortfolio
356                 && pTrans.getCategoryClass() == MoneyWiseTransCategoryClass.PORTFOLIOXFER
357                 && !myDebitAsset.equals(myCreditAsset)) {
358             /* Process portfolio transfer */
359             processPortfolioXfer((MoneyWisePortfolio) myDebitAsset, (MoneyWisePortfolio) myCreditAsset);
360 
361             /* Else handle the event normally */
362         } else if (myDebitAsset instanceof MoneyWiseAssetBase
363                 && myCreditAsset instanceof MoneyWiseAssetBase) {
364             /* Access correctly */
365             MoneyWiseAssetBase myDebit = (MoneyWiseAssetBase) myDebitAsset;
366             MoneyWiseAssetBase myCredit = (MoneyWiseAssetBase) myCreditAsset;
367             MoneyWiseAssetBase myChild = null;
368             final OceanusMoney myAmount = pTrans.getAmount();
369             MoneyWiseTransCategory myCat = pTrans.getCategory();
370 
371             /* Switch on category class */
372             switch (myCat.getCategoryTypeClass()) {
373                 case INTEREST:
374                 case LOYALTYBONUS:
375                     /* Obtain detailed category */
376                     myCat = myDebit.getDetailedCategory(myCat, myYear);
377 
378                     /* True debit account is the parent */
379                     myChild = myDebit.equals(myCredit)
380                             ? null
381                             : myDebit;
382                     myDebit = myDebit.getParent();
383                     break;
384                 case LOANINTERESTEARNED:
385                 case CASHBACK:
386                     /* True debit account is the parent of the asset */
387                     myDebit = myDebit.getParent();
388                     break;
389                 case RENTALINCOME:
390                 case ROOMRENTALINCOME:
391                     /* True debit account is the parent of the security */
392                     myChild = myDebit.equals(myCredit)
393                             ? null
394                             : myDebit;
395                     myDebit = myCredit.getParent();
396                     break;
397                 case WRITEOFF:
398                 case LOANINTERESTCHARGED:
399                     /* True credit account is the parent of the loan */
400                     myCredit = myCredit.getParent();
401                     break;
402                 default:
403                     break;
404             }
405 
406             /* If the debit account is auto-Expense */
407             if (myDebit instanceof MoneyWiseCash
408                     && myDebit.getAssetType() == MoneyWiseAssetType.AUTOEXPENSE) {
409                 /* Access debit as cash */
410                 final MoneyWiseCash myCash = (MoneyWiseCash) myDebit;
411                 final MoneyWiseTransCategory myAuto = myCash.getAutoExpense();
412                 myDebit = myCash.getAutoPayee();
413                 myDebit.touchItem(pTrans);
414 
415                 /* Subtract expense from Payee bucket */
416                 final MoneyWiseAnalysisPayeeBucket myPayee = thePayeeBuckets.getBucket(myDebit);
417                 myPayee.subtractExpense(theHelper, myAmount);
418 
419                 /* Subtract expense from Category bucket */
420                 final MoneyWiseAnalysisTransCategoryBucket myCatBucket = theCategoryBuckets.getBucket(myAuto);
421                 myCatBucket.subtractExpense(theHelper, myAmount);
422                 theTaxBasisBuckets.adjustAutoExpense(theHelper, false);
423 
424                 /* handle Payees */
425             } else if (MoneyWiseAssetType.PAYEE.equals(myDebit.getAssetType())) {
426                 final MoneyWiseAnalysisPayeeBucket myPayee = thePayeeBuckets.getBucket(myDebit);
427                 myPayee.adjustForDebit(theHelper);
428 
429                 /* handle valued assets */
430             } else {
431                 final MoneyWiseAnalysisAccountBucket<?> myBucket = getAccountBucket(myDebit);
432                 myBucket.adjustForDebit(theHelper);
433             }
434 
435             /* If the credit account is auto-Expense */
436             if (myCredit instanceof MoneyWiseCash
437                     && myCredit.getAssetType() == MoneyWiseAssetType.AUTOEXPENSE) {
438                 /* Access credit as cash */
439                 final MoneyWiseCash myCash = (MoneyWiseCash) myCredit;
440                 final MoneyWiseTransCategory myAuto = myCash.getAutoExpense();
441                 myCredit = myCash.getAutoPayee();
442                 myCredit.touchItem(pTrans);
443 
444                 /* Add expense to Payee bucket */
445                 final MoneyWiseAnalysisPayeeBucket myPayee = thePayeeBuckets.getBucket(myCredit);
446                 myPayee.addExpense(theHelper, myAmount);
447 
448                 /* Adjust the relevant category bucket */
449                 final MoneyWiseAnalysisTransCategoryBucket myCatBucket = theCategoryBuckets.getBucket(myAuto);
450                 myCatBucket.addExpense(theHelper, myAmount);
451                 theTaxBasisBuckets.adjustAutoExpense(theHelper, true);
452 
453                 /* handle Payees */
454             } else if (MoneyWiseAssetType.PAYEE.equals(myCredit.getAssetType())) {
455                 final MoneyWiseAnalysisPayeeBucket myPayee = thePayeeBuckets.getBucket(myCredit);
456                 myPayee.adjustForCredit(theHelper);
457 
458                 /* handle valued assets */
459             } else {
460                 final MoneyWiseAnalysisAccountBucket<?> myBucket = getAccountBucket(myCredit);
461                 myBucket.adjustForCredit(theHelper);
462             }
463 
464             /* If we should register the event with a child */
465             if (myChild != null) {
466                 /* Access bucket and register it */
467                 final MoneyWiseAnalysisAccountBucket<?> myBucket = getAccountBucket(myChild);
468                 myBucket.registerTransaction(pTrans);
469             }
470 
471             /* Adjust the tax and NI payments */
472             theTaxMan.adjustForTaxPayments(theHelper);
473             theStatePension.adjustForNIPayments(theHelper);
474 
475             /* If the event category is not a transfer */
476             if (!myCat.isTransfer()) {
477                 /* Adjust the relevant category buckets */
478                 theCategoryBuckets.adjustCategories(theHelper, myCat);
479             }
480 
481             /* Unknown combination */
482         } else {
483             throw new MoneyWiseLogicException("Invalid Asset Pair: "
484                     + pTrans.getAccount().getAssetType()
485                     + " "
486                     + pTrans.getDirection()
487                     + " "
488                     + pTrans.getPartner().getAssetType());
489         }
490     }
491 
492     /**
493      * Process a debit security transaction.
494      *
495      * @param pDebit  the debit security
496      * @param pCredit the credit account
497      * @throws OceanusException on error
498      */
499     private void processDebitSecurityTransaction(final MoneyWiseSecurityHolding pDebit,
500                                                  final MoneyWiseTransAsset pCredit) throws OceanusException {
501         /* If credit account is also SecurityHolding */
502         if (pCredit instanceof MoneyWiseSecurityHolding) {
503             /* Split out working */
504             processDebitCreditSecurityTransaction(pDebit, (MoneyWiseSecurityHolding) pCredit);
505             return;
506         }
507 
508         /* Switch on the category */
509         final MoneyWiseTransCategory myCat = theHelper.getCategory();
510         switch (myCat.getCategoryTypeClass()) {
511             /* Process a stock right waived */
512             case STOCKRIGHTSISSUE:
513                 processTransferOut(pDebit, (MoneyWiseAssetBase) pCredit);
514                 break;
515             /* Process a dividend */
516             case DIVIDEND:
517                 processDividend(pDebit, pCredit);
518                 break;
519             case PORTFOLIOXFER:
520                 processPortfolioXfer(pDebit, (MoneyWisePortfolio) pCredit);
521                 break;
522             /* Process standard transfer in/out */
523             case TRANSFER:
524             case SECURITYCLOSURE:
525             case EXPENSE:
526             case INHERITED:
527             case OTHERINCOME:
528                 if (pDebit.getSecurity().isSecurityClass(MoneyWiseSecurityClass.LIFEBOND)) {
529                     processChargeableGain(pDebit, (MoneyWiseAssetBase) pCredit);
530                 } else {
531                     processTransferOut(pDebit, (MoneyWiseAssetBase) pCredit);
532                 }
533                 break;
534             /* Throw an Exception */
535             default:
536                 throw new MoneyWiseLogicException(ERROR_CATEGORY
537                         + myCat.getCategoryTypeClass());
538         }
539     }
540 
541     /**
542      * Process a debit+credit security transaction.
543      *
544      * @param pDebit  the debit security
545      * @param pCredit the credit security
546      * @throws OceanusException on error
547      */
548     private void processDebitCreditSecurityTransaction(final MoneyWiseSecurityHolding pDebit,
549                                                        final MoneyWiseSecurityHolding pCredit) throws OceanusException {
550         /* Switch on the category */
551         final MoneyWiseTransCategory myCat = theHelper.getCategory();
552         switch (myCat.getCategoryTypeClass()) {
553             /* Process a stock split */
554             case STOCKSPLIT:
555             case UNITSADJUST:
556                 processUnitsAdjust(pDebit);
557                 break;
558             /* Process a stock DeMerger */
559             case STOCKDEMERGER:
560                 processStockDeMerger(pDebit, pCredit);
561                 break;
562             /* Process a Stock TakeOver */
563             case SECURITYREPLACE:
564             case STOCKTAKEOVER:
565                 processStockTakeover(pDebit, pCredit);
566                 break;
567             /* Process a dividend */
568             case DIVIDEND:
569                 processDividend(pDebit, pCredit);
570                 break;
571             /* Process standard transfer in/out */
572             case TRANSFER:
573             case EXPENSE:
574             case INHERITED:
575             case OTHERINCOME:
576                 processStockXchange(pDebit, pCredit);
577                 break;
578             /* Throw an Exception */
579             default:
580                 throw new MoneyWiseLogicException(ERROR_CATEGORY
581                         + myCat.getCategoryTypeClass());
582         }
583     }
584 
585     /**
586      * Process a credit security transaction.
587      *
588      * @param pDebit  the debit account
589      * @param pCredit the credit security holding
590      * @throws OceanusException on error
591      */
592     private void processCreditSecurityTransaction(final MoneyWiseTransAsset pDebit,
593                                                   final MoneyWiseSecurityHolding pCredit) throws OceanusException {
594         /* Input asset must be AssetBase */
595         if (!(pDebit instanceof MoneyWiseAssetBase)) {
596             throw new MoneyWiseLogicException("Invalid Debit Asset: "
597                     + pDebit.getAssetType());
598         }
599         final MoneyWiseAssetBase myDebit = (MoneyWiseAssetBase) pDebit;
600 
601         /* Switch on the category */
602         final MoneyWiseTransCategory myCat = theHelper.getCategory();
603         switch (myCat.getCategoryTypeClass()) {
604             /* Process standard transfer in/out */
605             case STOCKRIGHTSISSUE:
606             case TRANSFER:
607             case EXPENSE:
608             case INHERITED:
609             case OTHERINCOME:
610             case PENSIONCONTRIB:
611                 processTransferIn(myDebit, pCredit);
612                 break;
613             /* Throw an Exception */
614             default:
615                 throw new MoneyWiseLogicException(ERROR_CATEGORY
616                         + myCat.getCategoryTypeClass());
617         }
618     }
619 
620     /**
621      * Process a transaction that is a portfolio transfer.
622      * <p>
623      * This capital event relates only to both Debit and credit accounts.
624      *
625      * @param pSource the source portfolio
626      * @param pTarget the target portfolio
627      */
628     private void processPortfolioXfer(final MoneyWisePortfolio pSource,
629                                       final MoneyWisePortfolio pTarget) {
630         /* Access the portfolio buckets */
631         final MoneyWiseAnalysisPortfolioBucket mySource = thePortfolioBuckets.getBucket(pSource);
632         final MoneyWiseAnalysisPortfolioBucket myTarget = thePortfolioBuckets.getBucket(pTarget);
633 
634         /* Access source cash bucket */
635         final MoneyWiseAnalysisPortfolioCashBucket mySourceCash = mySource.getPortfolioCash();
636         if (mySourceCash.isActive()) {
637             /* Transfer any cash element */
638             final MoneyWiseAnalysisPortfolioCashBucket myTargetCash = myTarget.getPortfolioCash();
639             myTargetCash.adjustForXfer(mySourceCash, theHelper);
640         }
641 
642         /* Loop through the source portfolio */
643         final Iterator<MoneyWiseAnalysisSecurityBucket> myIterator = mySource.securityIterator();
644         while (myIterator.hasNext()) {
645             final MoneyWiseAnalysisSecurityBucket myBucket = myIterator.next();
646 
647             /* If the bucket is active */
648             if (myBucket.isActive()) {
649                 /* Adjust the Target Bucket */
650                 final MoneyWiseSecurityHolding myTargetHolding = theHoldingMap.declareHolding(pTarget, myBucket.getSecurity());
651                 final MoneyWiseAnalysisSecurityBucket myTargetBucket = myTarget.getSecurityBucket(myTargetHolding);
652                 theHelper.setSecurity(myBucket.getSecurity());
653 
654                 /* Process the Transfer */
655                 processPortfolioXfer(myBucket, myTargetBucket);
656             }
657         }
658 
659         /* PortfolioXfer is a transfer, so no need to update the categories */
660     }
661 
662     /**
663      * Process a portfolio transfer.
664      *
665      * @param pSource the source holding
666      * @param pTarget the target holding
667      */
668     private void processPortfolioXfer(final MoneyWiseAnalysisSecurityBucket pSource,
669                                       final MoneyWiseAnalysisSecurityBucket pTarget) {
670         /* Access source details */
671         MoneyWiseAnalysisSecurityValues mySourceValues = pSource.getValues();
672         OceanusUnits myUnits = mySourceValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
673         OceanusMoney myCost = mySourceValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);
674         OceanusMoney myGains = mySourceValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS);
675         OceanusMoney myInvested = mySourceValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.INVESTED);
676         OceanusMoney myForeignInvested = mySourceValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.FOREIGNINVESTED);
677         final boolean isForeign = pSource.isForeignCurrency();
678 
679         /* Determine value of the stock being transferred */
680         final OceanusPrice myPrice = thePriceMap.getPriceForDate(pSource.getSecurity(), theHelper.getDate());
681         OceanusMoney myStockValue = myUnits.valueAtPrice(myPrice);
682         OceanusMoney myForeignValue = null;
683         OceanusRatio myRate = null;
684 
685         /* If we are foreign */
686         if (isForeign) {
687             /* Determine foreign and local value */
688             myRate = theHelper.getDebitExchangeRate();
689             myForeignValue = myStockValue;
690             myStockValue = myStockValue.convertCurrency(theAnalysis.getCurrency().getCurrency(), myRate);
691         }
692 
693         /* Allocate current profit between the two stocks */
694         OceanusMoney myProfit = new OceanusMoney(myStockValue);
695         myProfit.subtractAmount(myCost);
696         pSource.adjustCounter(MoneyWiseAnalysisSecurityAttr.GROWTHADJUST, myProfit);
697         myProfit = new OceanusMoney(myProfit);
698         myProfit.negate();
699         pTarget.adjustCounter(MoneyWiseAnalysisSecurityAttr.GROWTHADJUST, myProfit);
700 
701         /* Transfer Units/Cost/Invested to target */
702         pTarget.adjustCounter(MoneyWiseAnalysisSecurityAttr.UNITS, myUnits);
703         pTarget.adjustCounter(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myCost);
704         pTarget.adjustCounter(MoneyWiseAnalysisSecurityAttr.INVESTED, myInvested);
705         pTarget.adjustCounter(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS, myGains);
706         final MoneyWiseAnalysisSecurityValues myTargetValues = pTarget.registerTransaction(theHelper);
707         myTargetValues.setValue(MoneyWiseAnalysisSecurityAttr.PRICE, myPrice);
708         myTargetValues.setValue(MoneyWiseAnalysisSecurityAttr.VALUATION, myStockValue);
709         myTargetValues.setValue(MoneyWiseAnalysisSecurityAttr.XFERREDCOST, myCost);
710         if (isForeign) {
711             myTargetValues.setValue(MoneyWiseAnalysisSecurityAttr.FOREIGNVALUE, myForeignValue);
712             myTargetValues.setValue(MoneyWiseAnalysisSecurityAttr.EXCHANGERATE, myRate);
713             pTarget.adjustCounter(MoneyWiseAnalysisSecurityAttr.FOREIGNINVESTED, myForeignInvested);
714         }
715 
716         /* Adjust the Source Units/Cost/Invested to zero */
717         myUnits = new OceanusUnits(myUnits);
718         myUnits.negate();
719         pSource.adjustCounter(MoneyWiseAnalysisSecurityAttr.UNITS, myUnits);
720         myCost = new OceanusMoney(myCost);
721         myCost.negate();
722         pSource.adjustCounter(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myCost);
723         myCost.negate();
724         myInvested = new OceanusMoney(myInvested);
725         myInvested.negate();
726         pSource.adjustCounter(MoneyWiseAnalysisSecurityAttr.INVESTED, myInvested);
727         myGains = new OceanusMoney(myGains);
728         myGains.negate();
729         pSource.adjustCounter(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS, myGains);
730         mySourceValues = pSource.registerTransaction(theHelper);
731         mySourceValues.setValue(MoneyWiseAnalysisSecurityAttr.PRICE, myPrice);
732         mySourceValues.setValue(MoneyWiseAnalysisSecurityAttr.VALUATION, myStockValue);
733         mySourceValues.setValue(MoneyWiseAnalysisSecurityAttr.XFERREDCOST, myCost);
734         if (isForeign) {
735             mySourceValues.setValue(MoneyWiseAnalysisSecurityAttr.FOREIGNVALUE, myForeignValue);
736             mySourceValues.setValue(MoneyWiseAnalysisSecurityAttr.EXCHANGERATE, myRate);
737             myForeignInvested = new OceanusMoney(myForeignInvested);
738             myForeignInvested.negate();
739             pTarget.adjustCounter(MoneyWiseAnalysisSecurityAttr.FOREIGNINVESTED, myForeignInvested);
740         }
741     }
742 
743     /**
744      * Process a transaction that is a portfolio transfer.
745      * <p>
746      * This capital event relates only to both Debit and credit accounts.
747      *
748      * @param pSource the source holding
749      * @param pTarget the target portfolio
750      */
751     private void processPortfolioXfer(final MoneyWiseSecurityHolding pSource,
752                                       final MoneyWisePortfolio pTarget) {
753         /* Access the portfolio buckets */
754         final MoneyWiseAnalysisPortfolioBucket mySource = thePortfolioBuckets.getBucket(pSource.getPortfolio());
755         final MoneyWiseAnalysisPortfolioBucket myTarget = thePortfolioBuckets.getBucket(pTarget);
756 
757         /* Access source security bucket */
758         final MoneyWiseAnalysisSecurityBucket myBucket = mySource.getSecurityBucket(pSource);
759 
760         /* If the bucket is active */
761         if (myBucket.isActive()) {
762             /* Adjust the Target Bucket */
763             final MoneyWiseSecurityHolding myTargetHolding = theHoldingMap.declareHolding(pTarget, myBucket.getSecurity());
764             final MoneyWiseAnalysisSecurityBucket myTargetBucket = myTarget.getSecurityBucket(myTargetHolding);
765 
766             /* Process the Transfer */
767             processPortfolioXfer(myBucket, myTargetBucket);
768         }
769 
770         /* PortfolioXfer is a transfer, so no need to update the categories */
771     }
772 
773     /**
774      * Process a transaction that is a stock split.
775      * <p>
776      * This capital event relates only to the Debit Account since the credit account is the same.
777      *
778      * @param pHolding the security holding
779      */
780     private void processUnitsAdjust(final MoneyWiseSecurityHolding pHolding) {
781         /* Access the units */
782         final OceanusUnits myDelta = theHelper.getAccountDeltaUnits();
783 
784         /* Adjust the Security Units */
785         final MoneyWiseAnalysisSecurityBucket myAsset = thePortfolioBuckets.getBucket(pHolding);
786         myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.UNITS, myDelta);
787 
788         /* Register the transaction */
789         myAsset.registerTransaction(theHelper);
790 
791         /* StockSplit/Adjust is a transfer, so no need to update the categories */
792     }
793 
794     /**
795      * Process a transaction that is a transfer into capital (also StockRightTaken).
796      * <p>
797      * This capital event relates only to the Credit Account.
798      *
799      * @param pDebit  the debit account
800      * @param pCredit the credit security holding
801      */
802     private void processTransferIn(final MoneyWiseAssetBase pDebit,
803                                    final MoneyWiseSecurityHolding pCredit) {
804         /* Access debit account and category */
805         final MoneyWiseTransCategory myCat = theHelper.getCategory();
806 
807         /* Adjust the credit transfer details */
808         processCreditXferIn(pCredit);
809 
810         /* Adjust the tax payments */
811         theTaxMan.adjustForTaxPayments(theHelper);
812 
813         /* Determine the type of the debit account */
814         switch (pDebit.getAssetType()) {
815             case PAYEE:
816                 final MoneyWiseAnalysisPayeeBucket myPayee = thePayeeBuckets.getBucket(pDebit);
817                 myPayee.adjustForDebit(theHelper);
818                 break;
819             default:
820                 final MoneyWiseAnalysisAccountBucket<?> myAccount = getAccountBucket(pDebit);
821                 myAccount.adjustForDebit(theHelper);
822                 break;
823         }
824 
825         /* If the event category is not a transfer */
826         if (!myCat.isTransfer()) {
827             /* Adjust the relevant category buckets */
828             theCategoryBuckets.adjustCategories(theHelper, myCat);
829         }
830     }
831 
832     /**
833      * Process the credit side of a transfer in transaction.
834      *
835      * @param pHolding the credit holding
836      */
837     private void processCreditXferIn(final MoneyWiseSecurityHolding pHolding) {
838         /* Transfer is to the credit account and may or may not have a change to the units */
839         OceanusMoney myAmount = theHelper.getCreditAmount();
840         final OceanusRatio myExchangeRate = theHelper.getCreditExchangeRate();
841         final MoneyWiseSecurity mySecurity = pHolding.getSecurity();
842 
843         /* Access the Asset Security Bucket */
844         final MoneyWiseAnalysisSecurityBucket myAsset = thePortfolioBuckets.getBucket(pHolding);
845         final boolean isForeign = myAsset.isForeignCurrency();
846 
847         /* If this is a foreign currency asset */
848         if (isForeign) {
849             /* Adjust foreign invested amount */
850             myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.FOREIGNINVESTED, myAmount);
851 
852             /* Switch to local amount */
853             myAmount = theHelper.getLocalAmount();
854         }
855 
856         /* Adjust the cost and investment */
857         myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myAmount);
858         myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.INVESTED, myAmount);
859 
860         /* Determine the delta units */
861         final MoneyWiseSecurityClass mySecClass = mySecurity.getCategoryClass();
862         OceanusUnits myDeltaUnits = theHelper.getCreditUnits();
863         OceanusUnits myUnits = myAsset.getValues().getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
864         if (mySecClass.isAutoUnits() && myUnits.isZero()) {
865             myDeltaUnits = OceanusUnits.getWholeUnits(mySecClass.getAutoUnits());
866         }
867 
868         /* If we have new units */
869         if (myDeltaUnits != null) {
870             /* Record change in units */
871             myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.UNITS, myDeltaUnits);
872         }
873 
874         /* Adjust for National Insurance */
875         myAsset.adjustForNIPayments(theHelper);
876 
877         /* Get the appropriate price for the account */
878         final OceanusPrice myPrice = thePriceMap.getPriceForDate(mySecurity, theHelper.getDate());
879 
880         /* Determine value of this stock after the transaction */
881         myUnits = myAsset.getValues().getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
882         OceanusMoney myValue = myUnits.valueAtPrice(myPrice);
883 
884         /* If we are foreign */
885         if (isForeign) {
886             /* Determine local value */
887             myValue = myValue.convertCurrency(theAnalysis.getCurrency().getCurrency(), myExchangeRate);
888         }
889 
890         /* Register the transaction */
891         final MoneyWiseAnalysisSecurityValues myValues = myAsset.registerTransaction(theHelper);
892         myValues.setValue(MoneyWiseAnalysisSecurityAttr.PRICE, myPrice);
893         myValues.setValue(MoneyWiseAnalysisSecurityAttr.VALUATION, myValue);
894         myValues.setValue(MoneyWiseAnalysisSecurityAttr.CASHINVESTED, myAmount);
895         if (isForeign) {
896             myValues.setValue(MoneyWiseAnalysisSecurityAttr.EXCHANGERATE, myExchangeRate);
897         }
898     }
899 
900     /**
901      * Process a dividend transaction.
902      * <p>
903      * This capital event relates to the only to Debit account, although the Credit account may be
904      * identical to the credit account in which case the dividend is re-invested
905      *
906      * @param pHolding the debit security holding
907      * @param pCredit  the credit account
908      */
909     private void processDividend(final MoneyWiseSecurityHolding pHolding,
910                                  final MoneyWiseTransAsset pCredit) {
911         /* The main security that we are interested in is the debit account */
912         final MoneyWisePortfolio myPortfolio = pHolding.getPortfolio();
913         final MoneyWiseSecurity mySecurity = pHolding.getSecurity();
914         OceanusMoney myAmount = theHelper.getDebitAmount();
915         final OceanusMoney myTaxCredit = theHelper.getTaxCredit();
916         final OceanusUnits myDeltaUnits = theHelper.getAccountDeltaUnits();
917         final MoneyWiseTaxCredit myYear = theHelper.getTransaction().getTaxYear();
918 
919         /* Obtain detailed category */
920         MoneyWiseTransCategory myCat = myPortfolio.getDetailedCategory(theHelper.getCategory(), myYear);
921         myCat = mySecurity.getDetailedCategory(myCat, myYear);
922 
923         /* True debit account is the parent */
924         final MoneyWiseAssetBase myDebit = mySecurity.getParent();
925 
926         /* Adjust the debit payee bucket */
927         final MoneyWiseAnalysisPayeeBucket myPayee = thePayeeBuckets.getBucket(myDebit);
928         myPayee.adjustForDebit(theHelper);
929 
930         /* Access the Asset Account Bucket */
931         final MoneyWiseAnalysisSecurityBucket myAsset = thePortfolioBuckets.getBucket(pHolding);
932         final boolean isForeign = myAsset.isForeignCurrency();
933         final boolean isReInvest = pCredit instanceof MoneyWiseSecurityHolding;
934 
935         /* If this is a foreign dividend */
936         if (isForeign) {
937             /* If this is a reInvestment */
938             if (isReInvest) {
939                 /* Adjust counters */
940                 myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.FOREIGNINVESTED, myAmount);
941                 myAsset.getValues().setValue(MoneyWiseAnalysisSecurityAttr.EXCHANGERATE, theHelper.getCreditExchangeRate());
942             }
943 
944             /* Switch to local amount */
945             myAmount = theHelper.getLocalAmount();
946         }
947 
948         /* If this is a re-investment */
949         if (isReInvest) {
950             /* This amount is added to the cost, so record as the delta cost */
951             myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myAmount);
952 
953             /* Record the investment */
954             myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.INVESTED, myAmount);
955 
956             /* If we have new units */
957             if (myDeltaUnits != null) {
958                 /* Record delta units */
959                 myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.UNITS, myDeltaUnits);
960             }
961 
962             /* If we have a tax credit */
963             if (myTaxCredit != null) {
964                 /* The Tax Credit is viewed as a received dividend from the account */
965                 myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.DIVIDEND, myTaxCredit);
966             }
967 
968             /* else we are paying out to another account */
969         } else {
970             /* Adjust the dividend total for this asset */
971             final OceanusMoney myAdjust = new OceanusMoney(myAmount);
972 
973             /* Any tax credit is viewed as a realised dividend from the account */
974             if (myTaxCredit != null) {
975                 myAdjust.addAmount(myTaxCredit);
976             }
977 
978             /* The Dividend is viewed as a dividend from the account */
979             myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.DIVIDEND, myAdjust);
980 
981             /* Adjust the credit account bucket */
982             final MoneyWiseAnalysisAccountBucket<?> myBucket = getAccountBucket((MoneyWiseAssetBase) pCredit);
983             myBucket.adjustForCredit(theHelper);
984         }
985 
986         /* Register the transaction */
987         myAsset.registerTransaction(theHelper);
988 
989         /* Adjust the tax payments */
990         theTaxMan.adjustForTaxPayments(theHelper);
991 
992         /* Adjust the relevant category buckets */
993         theCategoryBuckets.adjustCategories(theHelper, myCat);
994     }
995 
996     /**
997      * Process a transaction that is a transfer from capital.
998      * <p>
999      * This capital event relates only to the Debit Account
1000      *
1001      * @param pHolding the debit holding
1002      * @param pCredit  the credit account
1003      */
1004     private void processTransferOut(final MoneyWiseSecurityHolding pHolding,
1005                                     final MoneyWiseAssetBase pCredit) {
1006         /* Access credit account and category */
1007         final MoneyWiseTransCategory myCat = theHelper.getCategory();
1008 
1009         /* Adjust the debit transfer details */
1010         processDebitXferOut(pHolding);
1011 
1012         /* Adjust the credit account bucket */
1013         final MoneyWiseAnalysisAccountBucket<?> myBucket = getAccountBucket(pCredit);
1014         myBucket.adjustForCredit(theHelper);
1015 
1016         /* If the event category is not a transfer */
1017         if (!myCat.isTransfer()) {
1018             /* Adjust the relevant category buckets */
1019             theCategoryBuckets.adjustCategories(theHelper, myCat);
1020         }
1021     }
1022 
1023     /**
1024      * Process the debit side of a transfer out transaction.
1025      * <p>
1026      * This capital event relates only to the Debit Account
1027      *
1028      * @param pHolding the debit holding
1029      */
1030     private void processDebitXferOut(final MoneyWiseSecurityHolding pHolding) {
1031         /* Transfer out is from the debit account and may or may not have units */
1032         final MoneyWiseSecurity myDebit = pHolding.getSecurity();
1033         OceanusMoney myAmount = theHelper.getDebitAmount();
1034         boolean isLargeCash = false;
1035 
1036         /* Access the Asset Security Bucket */
1037         final MoneyWiseAnalysisSecurityBucket myAsset = thePortfolioBuckets.getBucket(pHolding);
1038         MoneyWiseAnalysisSecurityValues myValues = myAsset.getValues();
1039         final OceanusRatio myXchangeRate = theHelper.getDebitExchangeRate();
1040         final boolean isForeign = myAsset.isForeignCurrency();
1041 
1042         /* If this is a foreign currency asset */
1043         if (isForeign) {
1044             /* Adjust foreign invested amount */
1045             final OceanusMoney myDelta = new OceanusMoney(myAmount);
1046             myDelta.negate();
1047             myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.FOREIGNINVESTED, myDelta);
1048 
1049             /* Switch to local amount */
1050             myAmount = theHelper.getLocalAmount();
1051         }
1052 
1053         /* Record the delta investment */
1054         final OceanusMoney myDelta = new OceanusMoney(myAmount);
1055         myDelta.negate();
1056         myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.INVESTED, myDelta);
1057 
1058         /* Get the appropriate price for the account */
1059         final OceanusPrice myPrice = thePriceMap.getPriceForDate(myDebit, theHelper.getDate());
1060 
1061         /* Assume that the allowed cost is the full value */
1062         OceanusUnits myUnits = Objects.requireNonNull(myValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS));
1063         OceanusMoney myAllowedCost = new OceanusMoney(myAmount);
1064         final OceanusMoney myCost = myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);
1065         OceanusRatio myCostDilution = null;
1066         OceanusMoney myConsideration = null;
1067 
1068         /* Determine the delta units */
1069         OceanusUnits myDeltaUnits = theHelper.getCategoryClass().isSecurityClosure()
1070                 ? myUnits
1071                 : theHelper.getDebitUnits();
1072         final boolean isCapitalDistribution = myDeltaUnits == null;
1073 
1074         /* If this is not a capital distribution */
1075         if (!isCapitalDistribution) {
1076             /* The allowed cost is the relevant fraction of the cost */
1077             myAllowedCost = myCost.valueAtWeight(myDeltaUnits, myUnits);
1078 
1079             /* Access units as negative value */
1080             myDeltaUnits = new OceanusUnits(myDeltaUnits);
1081             myDeltaUnits.negate();
1082 
1083             /* Record delta to units */
1084             myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.UNITS, myDeltaUnits);
1085             final OceanusUnits myNewUnits = myValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
1086 
1087             /* Determine the cost dilution */
1088             myCostDilution = new OceanusRatio(myNewUnits, myUnits);
1089             myUnits = myNewUnits;
1090         }
1091 
1092         /* Determine value of this stock after the transaction */
1093         OceanusMoney myValue = myUnits.valueAtPrice(myPrice);
1094 
1095         /* If we are foreign */
1096         if (isForeign) {
1097             /* Determine local value */
1098             myValue = myValue.convertCurrency(theAnalysis.getCurrency().getCurrency(), myXchangeRate);
1099         }
1100 
1101         /* If we are performing a capital distribution */
1102         if (isCapitalDistribution) {
1103             /* Determine condition as to whether this is a large cash transaction */
1104             final OceanusMoney myPortion = myValue.valueAtRate(LIMIT_RATE);
1105             isLargeCash = (myAmount.compareTo(LIMIT_VALUE) > 0)
1106                     && (myAmount.compareTo(myPortion) > 0);
1107 
1108             /* If this is large cash */
1109             if (isLargeCash) {
1110                 /* Determine the total value of rights plus share value */
1111                 myConsideration = new OceanusMoney(myAmount);
1112                 myConsideration.addAmount(myValue);
1113 
1114                 /* Determine the allowedCost as a proportion of the total value */
1115                 myAllowedCost = myCost.valueAtWeight(myAmount, myConsideration);
1116 
1117                 /* Determine the cost dilution */
1118                 myCostDilution = new OceanusRatio(myValue, myConsideration);
1119 
1120                 /* else this is viewed as small and is taken out of the cost */
1121             } else {
1122                 /* Set the allowed cost to be the least of the cost or the returned cash */
1123                 myAllowedCost = myAmount.compareTo(myCost) > 0
1124                         ? new OceanusMoney(myCost)
1125                         : new OceanusMoney(myAmount);
1126             }
1127         }
1128 
1129         /* Determine the delta to the cost */
1130         final OceanusMoney myDeltaCost = new OceanusMoney(myAllowedCost);
1131         myDeltaCost.negate();
1132 
1133         /* If we have a delta to the cost */
1134         if (myDeltaCost.isNonZero()) {
1135             /* Adjust the cost */
1136             myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myDeltaCost);
1137         }
1138 
1139         /* Determine the capital gain */
1140         final OceanusMoney myCapitalGain = new OceanusMoney(myAmount);
1141         myCapitalGain.addAmount(myDeltaCost);
1142 
1143         /* If we have a delta to the gains */
1144         if (myCapitalGain.isNonZero()) {
1145             /* Adjust the gains */
1146             myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS, myCapitalGain);
1147 
1148             /* Adjust the capitalGains category bucket */
1149             theCategoryBuckets.adjustStandardGain(theHelper, pHolding, myCapitalGain);
1150         }
1151 
1152         /* Register the transaction */
1153         myValues = myAsset.registerTransaction(theHelper);
1154 
1155         /* record details */
1156         myValues.setValue(MoneyWiseAnalysisSecurityAttr.PRICE, myPrice);
1157         myValues.setValue(MoneyWiseAnalysisSecurityAttr.VALUATION, myValue);
1158         myValues.setValue(MoneyWiseAnalysisSecurityAttr.RETURNEDCASH, myAmount);
1159         myValues.setValue(MoneyWiseAnalysisSecurityAttr.ALLOWEDCOST, myAllowedCost);
1160         if (myCostDilution != null) {
1161             myValues.setValue(MoneyWiseAnalysisSecurityAttr.COSTDILUTION, myCostDilution);
1162         }
1163         if (myConsideration != null) {
1164             myValues.setValue(MoneyWiseAnalysisSecurityAttr.CONSIDERATION, myConsideration);
1165         }
1166         if (myCapitalGain.isNonZero()) {
1167             myValues.setValue(MoneyWiseAnalysisSecurityAttr.CAPITALGAIN, myCapitalGain);
1168         }
1169         if (isForeign) {
1170             myValues.setValue(MoneyWiseAnalysisSecurityAttr.EXCHANGERATE, myXchangeRate);
1171         }
1172         if (isCapitalDistribution) {
1173             myValues.setValue(MoneyWiseAnalysisSecurityAttr.CASHTYPE, isLargeCash
1174                     ? MoneyWiseCashType.LARGECASH
1175                     : MoneyWiseCashType.SMALLCASH);
1176         }
1177     }
1178 
1179     /**
1180      * Process a transaction that is a exchange between two capital accounts.
1181      * <p>
1182      * This represent a transfer out from the debit account and a transfer in to the credit account
1183      *
1184      * @param pDebit  the debit holding
1185      * @param pCredit the credit holding
1186      */
1187     private void processStockXchange(final MoneyWiseSecurityHolding pDebit,
1188                                      final MoneyWiseSecurityHolding pCredit) {
1189         /* Adjust the debit transfer details */
1190         processDebitXferOut(pDebit);
1191 
1192         /* Adjust the credit transfer details */
1193         processCreditXferIn(pCredit);
1194     }
1195 
1196     /**
1197      * Process a transaction that is a chargeable gain.
1198      * <p>
1199      * This capital event relates only to the Debit Asset
1200      *
1201      * @param pHolding the debit security holding
1202      * @param pCredit  the credit account
1203      */
1204     private void processChargeableGain(final MoneyWiseSecurityHolding pHolding,
1205                                        final MoneyWiseAssetBase pCredit) {
1206         /* Chargeable Gain is from the debit account and may or may not have units */
1207         final MoneyWiseSecurity myDebit = pHolding.getSecurity();
1208         OceanusMoney myAmount = theHelper.getDebitAmount();
1209         OceanusUnits myDeltaUnits = theHelper.getDebitUnits();
1210 
1211         /* Access the Asset Security Bucket */
1212         final MoneyWiseAnalysisSecurityBucket myAsset = thePortfolioBuckets.getBucket(pHolding);
1213         final MoneyWiseAnalysisSecurityValues myValues = myAsset.getValues();
1214 
1215         /* If this is a foreign currency asset */
1216         if (Boolean.TRUE.equals(myAsset.isForeignCurrency())) {
1217             /* Adjust foreign invested amount */
1218             final OceanusMoney myDelta = new OceanusMoney(myAmount);
1219             myDelta.negate();
1220             myValues.setValue(MoneyWiseAnalysisSecurityAttr.EXCHANGERATE, theHelper.getDebitExchangeRate());
1221             myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.FOREIGNINVESTED, myDelta);
1222 
1223             /* Switch to local amount */
1224             myAmount = theHelper.getLocalAmount();
1225         }
1226 
1227         /* Record the delta investment */
1228         final OceanusMoney myDelta = new OceanusMoney(myAmount);
1229         myDelta.negate();
1230         myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.INVESTED, myDelta);
1231 
1232         /* Assume the cost reduction is the full value */
1233         OceanusMoney myReduction = new OceanusMoney(myAmount);
1234         final OceanusMoney myCost = myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);
1235 
1236         /* If we are reducing units in the account */
1237         if (myDeltaUnits != null) {
1238             /* The reduction is the relevant fraction of the cost */
1239             final OceanusUnits myUnits = myValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
1240             myReduction = myCost.valueAtWeight(myDeltaUnits, myUnits);
1241 
1242             /* Access units as negative value */
1243             myDeltaUnits = new OceanusUnits(myDeltaUnits);
1244             myDeltaUnits.negate();
1245 
1246             /* Record delta to units */
1247             myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.UNITS, myDeltaUnits);
1248         }
1249 
1250         /* If the reduction is greater than the total cost */
1251         if (myReduction.compareTo(myCost) > 0) {
1252             /* Reduction is the total cost */
1253             myReduction = new OceanusMoney(myCost);
1254         }
1255 
1256         /* Determine the delta to the cost */
1257         final OceanusMoney myDeltaCost = new OceanusMoney(myReduction);
1258         myDeltaCost.negate();
1259 
1260         /* If we have a delta to the cost */
1261         if (myDeltaCost.isNonZero()) {
1262             /* Adjust the cost */
1263             myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myDeltaCost);
1264         }
1265 
1266         /* Determine the delta to the gains */
1267         final OceanusMoney myDeltaGains = new OceanusMoney(myAmount);
1268         myDeltaGains.addAmount(myDeltaCost);
1269 
1270         /* If we have a delta to the gains */
1271         if (myDeltaGains.isNonZero()) {
1272             /* Adjust the gains */
1273             myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS, myDeltaGains);
1274         }
1275 
1276         /* Register the event */
1277         myAsset.registerTransaction(theHelper);
1278 
1279         /* True debit account is the parent */
1280         final MoneyWiseAssetBase myParent = myDebit.getParent();
1281 
1282         /* Adjust the debit account bucket */
1283         final MoneyWiseAnalysisPayeeBucket myPayee = thePayeeBuckets.getBucket(myParent);
1284         myPayee.adjustForTaxCredit(theHelper);
1285 
1286         /* Adjust the credit account bucket */
1287         final MoneyWiseAnalysisAccountBucket<?> myBucket = getAccountBucket(pCredit);
1288         myBucket.adjustForCredit(theHelper);
1289 
1290         /* Adjust the chargeableGains category bucket */
1291         theCategoryBuckets.adjustChargeableGain(theHelper, myReduction);
1292 
1293         /* Adjust the TaxMan account for the tax credit */
1294         theTaxMan.adjustForTaxPayments(theHelper);
1295 
1296         /* Add the chargeable event */
1297         theTaxBasisBuckets.recordChargeableGain(theHelper.getTransaction(), myDeltaGains);
1298     }
1299 
1300     /**
1301      * Process a transaction that is Stock DeMerger.
1302      * <p>
1303      * This capital event relates to both the Credit and Debit accounts
1304      *
1305      * @param pDebit  the debit account
1306      * @param pCredit the credit account
1307      */
1308     private void processStockDeMerger(final MoneyWiseSecurityHolding pDebit,
1309                                       final MoneyWiseSecurityHolding pCredit) {
1310         /* Access the Debit Asset Security Bucket */
1311         MoneyWiseAnalysisSecurityBucket myAsset = thePortfolioBuckets.getBucket(pDebit);
1312         MoneyWiseAnalysisSecurityValues myValues = myAsset.getValues();
1313         final OceanusRatio myDebitRate = theHelper.getDebitExchangeRate();
1314 
1315         /* Obtain current cost */
1316         final OceanusMoney myCost = myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);
1317         final OceanusRatio myDilution = theHelper.getDilution();
1318         final OceanusUnits myDeltaUnits = theHelper.getAccountDeltaUnits();
1319 
1320         /* If we reduced the units */
1321         if (myDeltaUnits != null) {
1322             /* Record the delta units */
1323             myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.UNITS, myDeltaUnits);
1324         }
1325 
1326         /* Calculate the cost dilution */
1327         final OceanusMoney myNewCost = myCost.getDilutedMoney(myDilution);
1328         final OceanusRatio myCostDilution = new OceanusRatio(myNewCost, myCost);
1329 
1330         /* Calculate the delta to the cost */
1331         OceanusMoney myDeltaCost = new OceanusMoney(myNewCost);
1332         myDeltaCost.subtractAmount(myCost);
1333 
1334         /* Record the delta cost/investment */
1335         myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myDeltaCost);
1336         myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.INVESTED, myDeltaCost);
1337         final boolean isForeignDebit = myAsset.isForeignCurrency();
1338         if (isForeignDebit) {
1339             final OceanusMoney myInvested = myDeltaCost.convertCurrency(myAsset.getCurrency().getCurrency(), myDebitRate);
1340             myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.FOREIGNINVESTED, myInvested);
1341         }
1342 
1343         /* Register the event */
1344         myValues = myAsset.registerTransaction(theHelper);
1345 
1346         /* Access the Credit Asset Account Bucket */
1347         myAsset = thePortfolioBuckets.getBucket(pCredit);
1348 
1349         /* The deltaCost is transferred to the credit account */
1350         myDeltaCost = new OceanusMoney(myDeltaCost);
1351         myDeltaCost.negate();
1352 
1353         /* Record details */
1354         myValues.setValue(MoneyWiseAnalysisSecurityAttr.XFERREDCOST, myDeltaCost);
1355         myValues.setValue(MoneyWiseAnalysisSecurityAttr.COSTDILUTION, myCostDilution);
1356         if (isForeignDebit) {
1357             myValues.setValue(MoneyWiseAnalysisSecurityAttr.EXCHANGERATE, myDebitRate);
1358         }
1359 
1360         /* Record the delta cost/investment */
1361         myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myDeltaCost);
1362         myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.INVESTED, myDeltaCost);
1363         final boolean isForeignCredit = myAsset.isForeignCurrency();
1364         final OceanusRatio myCreditRate = theHelper.getCreditExchangeRate();
1365         if (isForeignCredit) {
1366             final OceanusMoney myInvested = myDeltaCost.convertCurrency(myAsset.getCurrency().getCurrency(), myCreditRate);
1367             myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.FOREIGNINVESTED, myInvested);
1368         }
1369 
1370         /* Get the appropriate prices/rates for the stock */
1371         final OceanusDate myDate = theHelper.getDate();
1372         final OceanusPrice myCreditPrice = thePriceMap.getPriceForDate(myAsset.getSecurity(), myDate);
1373         final Currency myCurrency = theAnalysis.getCurrency().getCurrency();
1374 
1375         /* Determine value of the stock being deMerged */
1376         final OceanusUnits myCreditUnits = theHelper.getPartnerDeltaUnits();
1377         OceanusMoney myCreditXferValue = myCreditUnits.valueAtPrice(myCreditPrice);
1378         if (isForeignCredit) {
1379             myCreditXferValue = myCreditXferValue.convertCurrency(myCurrency, myCreditRate);
1380         }
1381 
1382         /* Record the current/delta units */
1383         myAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.UNITS, myCreditUnits);
1384 
1385         /* Register the transaction */
1386         myValues = myAsset.registerTransaction(theHelper);
1387 
1388         /* Record values */
1389         myValues.setValue(MoneyWiseAnalysisSecurityAttr.XFERREDCOST, myDeltaCost);
1390         myValues.setValue(MoneyWiseAnalysisSecurityAttr.PRICE, myCreditPrice);
1391         myValues.setValue(MoneyWiseAnalysisSecurityAttr.XFERREDVALUE, myCreditXferValue);
1392         if (isForeignCredit) {
1393             myValues.setValue(MoneyWiseAnalysisSecurityAttr.EXCHANGERATE, myCreditRate);
1394         }
1395 
1396         /* StockDeMerger is a transfer, so no need to update the categories */
1397     }
1398 
1399     /**
1400      * Process a transaction that is StockTakeover.
1401      * <p>
1402      * This can be accomplished using a cash portion (to a ThirdParty account) and these workings
1403      * are split out.
1404      *
1405      * @param pDebit  the debit holding
1406      * @param pCredit the credit holding
1407      */
1408     private void processStockTakeover(final MoneyWiseSecurityHolding pDebit,
1409                                       final MoneyWiseSecurityHolding pCredit) {
1410         final OceanusMoney myAmount = theHelper.getReturnedCash();
1411         final MoneyWiseTransAsset myReturnedCashAct = theHelper.getReturnedCashAccount();
1412 
1413         /* If we have a returned cash part of the transaction */
1414         if (myReturnedCashAct != null
1415                 && myAmount.isNonZero()) {
1416             /* Process a Stock And Cash TakeOver */
1417             processStockAndCashTakeOver(pDebit, pCredit);
1418         } else {
1419             /* Process a StockOnly TakeOver */
1420             processStockOnlyTakeOver(pDebit, pCredit);
1421         }
1422     }
1423 
1424     /**
1425      * Process a transaction that is a StockOnlyTakeover.
1426      * <p>
1427      * This capital event relates to both the Credit and Debit accounts
1428      *
1429      * @param pDebit  the debit holding
1430      * @param pCredit the credit holding
1431      */
1432     private void processStockOnlyTakeOver(final MoneyWiseSecurityHolding pDebit,
1433                                           final MoneyWiseSecurityHolding pCredit) {
1434         /* Access details */
1435         final MoneyWiseSecurity myCredit = pCredit.getSecurity();
1436         final MoneyWiseSecurity myDebit = pDebit.getSecurity();
1437 
1438         /* Access the Asset Security Buckets */
1439         final MoneyWiseAnalysisSecurityBucket myDebitAsset = thePortfolioBuckets.getBucket(pDebit);
1440         MoneyWiseAnalysisSecurityValues myDebitValues = myDebitAsset.getValues();
1441         final MoneyWiseAnalysisSecurityBucket myCreditAsset = thePortfolioBuckets.getBucket(pCredit);
1442         final OceanusDate myDate = theHelper.getDate();
1443 
1444         /* Get the appropriate prices/rates for the stock */
1445         final OceanusPrice myCreditPrice = thePriceMap.getPriceForDate(myCredit, myDate);
1446         final OceanusPrice myDebitPrice = thePriceMap.getPriceForDate(myDebit, myDate);
1447         final OceanusRatio myDebitRate = theHelper.getDebitExchangeRate();
1448         final OceanusRatio myCreditRate = theHelper.getCreditExchangeRate();
1449         final Currency myCurrency = theAnalysis.getCurrency().getCurrency();
1450 
1451         /* Determine value of the stock in both parts of the takeOver */
1452         OceanusUnits myCreditUnits = theHelper.getPartnerDeltaUnits();
1453         OceanusMoney myCreditXferValue = myCreditUnits.valueAtPrice(myCreditPrice);
1454         OceanusUnits myDebitUnits = myDebitValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
1455         OceanusMoney myDebitValue = myDebitUnits.valueAtPrice(myDebitPrice);
1456         OceanusMoney myInvested = myDebitValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.INVESTED);
1457 
1458         /* Handle foreign debit */
1459         final boolean isForeignDebit = myDebitAsset.isForeignCurrency();
1460         if (isForeignDebit) {
1461             myDebitValue = myDebitValue.convertCurrency(myCurrency, myDebitRate);
1462         }
1463 
1464         /* Handle foreign credit */
1465         final boolean isForeignCredit = myCreditAsset.isForeignCurrency();
1466         if (isForeignCredit) {
1467             myCreditXferValue = myCreditXferValue.convertCurrency(myCurrency, myCreditRate);
1468         }
1469 
1470         /* Determine the residual cost of the old stock */
1471         final OceanusMoney myDebitCost = myDebitValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);
1472 
1473         /* Allocate current profit between the two stocks */
1474         OceanusMoney myProfit = new OceanusMoney(myDebitValue);
1475         myProfit.subtractAmount(myDebitCost);
1476         myDebitAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.GROWTHADJUST, myProfit);
1477         myProfit = new OceanusMoney(myProfit);
1478         myProfit.negate();
1479         myCreditAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.GROWTHADJUST, myProfit);
1480 
1481         /* Adjust cost/units/invested of the credit account */
1482         myCreditAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myDebitCost);
1483         myCreditAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.UNITS, myCreditUnits);
1484         myCreditAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.INVESTED, myInvested);
1485         if (isForeignCredit) {
1486             final OceanusMoney myForeign = myInvested.convertCurrency(myCreditAsset.getCurrency().getCurrency(), myCreditRate);
1487             myCreditAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.FOREIGNINVESTED, myForeign);
1488         }
1489 
1490         /* Determine final value of the credit stock after the takeOver */
1491         myCreditUnits = myCreditAsset.getValues().getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
1492         OceanusMoney myCreditValue = myCreditUnits.valueAtPrice(myCreditPrice);
1493         if (isForeignCredit) {
1494             myCreditValue = myCreditValue.convertCurrency(myCurrency, myCreditRate);
1495         }
1496 
1497         /* Register the transaction */
1498         final MoneyWiseAnalysisSecurityValues myCreditValues = myCreditAsset.registerTransaction(theHelper);
1499         myCreditValues.setValue(MoneyWiseAnalysisSecurityAttr.PRICE, myCreditPrice);
1500         myCreditValues.setValue(MoneyWiseAnalysisSecurityAttr.XFERREDVALUE, myCreditXferValue);
1501         myCreditValues.setValue(MoneyWiseAnalysisSecurityAttr.XFERREDCOST, myDebitCost);
1502         myCreditValues.setValue(MoneyWiseAnalysisSecurityAttr.VALUATION, myCreditValue);
1503         if (isForeignCredit) {
1504             myCreditValues.setValue(MoneyWiseAnalysisSecurityAttr.EXCHANGERATE, myCreditRate);
1505         }
1506 
1507         /* Drive debit cost down to zero */
1508         final OceanusMoney myDeltaCost = new OceanusMoney(myDebitCost);
1509         myDeltaCost.negate();
1510         myDebitAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myDeltaCost);
1511         myDeltaCost.negate();
1512 
1513         /* Drive debit units down to zero */
1514         myDebitUnits = new OceanusUnits(myDebitUnits);
1515         myDebitUnits.negate();
1516         myDebitAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.UNITS, myDebitUnits);
1517 
1518         /* Adjust debit Invested amount */
1519         myInvested = new OceanusMoney(myInvested);
1520         myInvested.negate();
1521         myDebitAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.INVESTED, myInvested);
1522         if (isForeignDebit) {
1523             myInvested = new OceanusMoney(myDebitValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.FOREIGNINVESTED));
1524             myInvested.negate();
1525             myDebitAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.FOREIGNINVESTED, myInvested);
1526         }
1527 
1528         /* Register the transaction */
1529         myDebitValues = myDebitAsset.registerTransaction(theHelper);
1530         myDebitValues.setValue(MoneyWiseAnalysisSecurityAttr.PRICE, myDebitPrice);
1531         myDebitValues.setValue(MoneyWiseAnalysisSecurityAttr.VALUATION, myDebitValue);
1532         myDebitValues.setValue(MoneyWiseAnalysisSecurityAttr.XFERREDVALUE, myCreditXferValue);
1533         myDebitValues.setValue(MoneyWiseAnalysisSecurityAttr.XFERREDCOST, myDeltaCost);
1534         if (isForeignDebit) {
1535             myDebitValues.setValue(MoneyWiseAnalysisSecurityAttr.EXCHANGERATE, myDebitRate);
1536         }
1537     }
1538 
1539     /**
1540      * Process a transaction that is StockAndCashTakeover.
1541      * <p>
1542      * This capital event relates to both the Credit and Debit accounts. In particular it makes
1543      * reference to the CashTakeOver aspect of the debit account
1544      *
1545      * @param pDebit  the debit holding
1546      * @param pCredit the credit holding
1547      */
1548     private void processStockAndCashTakeOver(final MoneyWiseSecurityHolding pDebit,
1549                                              final MoneyWiseSecurityHolding pCredit) {
1550         /* Access details */
1551         final MoneyWiseSecurity myDebit = pDebit.getSecurity();
1552         final MoneyWiseSecurity myCredit = pCredit.getSecurity();
1553         final OceanusDate myDate = theHelper.getDate();
1554         final MoneyWiseTransAsset myReturnedCashAccount = theHelper.getReturnedCashAccount();
1555         final OceanusMoney myAmount = theHelper.getLocalReturnedCash();
1556 
1557         /* Access the Asset Security Buckets */
1558         final MoneyWiseAnalysisSecurityBucket myDebitAsset = thePortfolioBuckets.getBucket(pDebit);
1559         MoneyWiseAnalysisSecurityValues myDebitValues = myDebitAsset.getValues();
1560         final MoneyWiseAnalysisSecurityBucket myCreditAsset = thePortfolioBuckets.getBucket(pCredit);
1561 
1562         /* Get the appropriate prices for the assets */
1563         final OceanusPrice myDebitPrice = thePriceMap.getPriceForDate(myDebit, myDate);
1564         final OceanusPrice myCreditPrice = thePriceMap.getPriceForDate(myCredit, myDate);
1565         final OceanusRatio myDebitRate = theHelper.getDebitExchangeRate();
1566         final OceanusRatio myCreditRate = theHelper.getCreditExchangeRate();
1567         final Currency myCurrency = theAnalysis.getCurrency().getCurrency();
1568 
1569         /* Determine value of the base stock */
1570         OceanusUnits myDebitUnits = myDebitValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
1571         OceanusMoney myDebitValue = myDebitUnits.valueAtPrice(myDebitPrice);
1572 
1573         /* Determine value of the stock part of the takeOver */
1574         OceanusUnits myCreditUnits = theHelper.getPartnerDeltaUnits();
1575         OceanusMoney myCreditXferValue = myCreditUnits.valueAtPrice(myCreditPrice);
1576 
1577         /* Handle foreign debit */
1578         final boolean isForeignDebit = myDebitAsset.isForeignCurrency();
1579         if (isForeignDebit) {
1580             myDebitValue = myDebitValue.convertCurrency(myCurrency, myDebitRate);
1581         }
1582 
1583         /* Handle foreign credit */
1584         final boolean isForeignCredit = myCreditAsset.isForeignCurrency();
1585         if (isForeignCredit) {
1586             myCreditXferValue = myCreditXferValue.convertCurrency(myCurrency, myCreditRate);
1587         }
1588 
1589         /* Calculate the total consideration */
1590         final OceanusMoney myConsideration = new OceanusMoney(myAmount);
1591         myConsideration.addAmount(myCreditXferValue);
1592 
1593         /* Access the current debit cost */
1594         final OceanusMoney myCost = myDebitValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);
1595         OceanusRatio myCostDilution = null;
1596         final OceanusMoney myCostXfer;
1597         final OceanusMoney myAllowedCost;
1598 
1599         /* Determine condition as to whether this is a large cash transaction */
1600         final OceanusMoney myPortion = myDebitValue.valueAtRate(LIMIT_RATE);
1601         final boolean isLargeCash = (myAmount.compareTo(LIMIT_VALUE) > 0)
1602                 && (myAmount.compareTo(myPortion) > 0);
1603 
1604         /* If this is a large cash takeOver */
1605         if (isLargeCash) {
1606             /* Determine the transferable cost */
1607             myCostXfer = myCost.valueAtWeight(myCreditXferValue, myConsideration);
1608 
1609             /* Determine the cost dilution */
1610             myCostDilution = new OceanusRatio(myAmount, myConsideration);
1611 
1612             /* Determine the allowed cost */
1613             myAllowedCost = new OceanusMoney(myCost);
1614             myAllowedCost.subtractAmount(myCostXfer);
1615 
1616             /* else this is viewed as small and is taken out of the cost */
1617         } else {
1618             /* Set the allowed cost to be the least of the cost or the returned cash */
1619             myAllowedCost = myAmount.compareTo(myCost) > 0
1620                     ? new OceanusMoney(myCost)
1621                     : new OceanusMoney(myAmount);
1622 
1623             /* Transferred cost is cost minus the allowed cost */
1624             myCostXfer = new OceanusMoney(myCost);
1625             myCostXfer.subtractAmount(myAllowedCost);
1626         }
1627 
1628         /* Determine the capital gain */
1629         final OceanusMoney myCapitalGain = new OceanusMoney(myAmount);
1630         myCapitalGain.subtractAmount(myAllowedCost);
1631         if (myCapitalGain.isNonZero()) {
1632             /* Record the delta gains */
1633             myDebitAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS, myCapitalGain);
1634 
1635             /* Adjust the capitalGains category bucket */
1636             theCategoryBuckets.adjustStandardGain(theHelper, pDebit, myCapitalGain);
1637         }
1638 
1639         /* Allocate current profit between the two stocks */
1640         OceanusMoney myProfit = new OceanusMoney(myCreditXferValue);
1641         myProfit.subtractAmount(myCostXfer);
1642         myDebitAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.GROWTHADJUST, myProfit);
1643         myProfit = new OceanusMoney(myProfit);
1644         myProfit.negate();
1645         myCreditAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.GROWTHADJUST, myProfit);
1646 
1647         /* Adjust cost/units/invested of the credit account */
1648         myCreditAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myCostXfer);
1649         myCreditAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.UNITS, myCreditUnits);
1650         myCreditAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.INVESTED, myCostXfer);
1651         if (isForeignCredit) {
1652             final OceanusMoney myForeign = myCostXfer.convertCurrency(myCreditAsset.getCurrency().getCurrency(), myCreditRate);
1653             myCreditAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.FOREIGNINVESTED, myForeign);
1654         }
1655 
1656         /* Determine final value of the credit stock after the takeOver */
1657         myCreditUnits = myCreditAsset.getValues().getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
1658         OceanusMoney myCreditValue = myCreditUnits.valueAtPrice(myCreditPrice);
1659         if (isForeignCredit) {
1660             myCreditValue = myCreditValue.convertCurrency(myCurrency, myCreditRate);
1661         }
1662 
1663         /* Register the transaction */
1664         final MoneyWiseAnalysisSecurityValues myCreditValues = myCreditAsset.registerTransaction(theHelper);
1665         myCreditValues.setValue(MoneyWiseAnalysisSecurityAttr.PRICE, myCreditPrice);
1666         myCreditValues.setValue(MoneyWiseAnalysisSecurityAttr.XFERREDVALUE, myCreditXferValue);
1667         myCreditValues.setValue(MoneyWiseAnalysisSecurityAttr.XFERREDCOST, myCostXfer);
1668         myCreditValues.setValue(MoneyWiseAnalysisSecurityAttr.VALUATION, myCreditValue);
1669         if (isForeignCredit) {
1670             myCreditValues.setValue(MoneyWiseAnalysisSecurityAttr.EXCHANGERATE, myCreditRate);
1671         }
1672 
1673         /* Drive debit cost down to zero */
1674         final OceanusMoney myDeltaCost = new OceanusMoney(myCost);
1675         myDeltaCost.negate();
1676         myDebitAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myDeltaCost);
1677 
1678         /* Drive debit units down to zero */
1679         myDebitUnits = new OceanusUnits(myDebitUnits);
1680         myDebitUnits.negate();
1681         myDebitAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.UNITS, myDebitUnits);
1682 
1683         /* Adjust debit Invested amount */
1684         OceanusMoney myInvested = myDebitValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.INVESTED);
1685         myInvested = new OceanusMoney(myInvested);
1686         myInvested.setZero();
1687         myInvested.subtractAmount(myAmount);
1688         myInvested.subtractAmount(myCostXfer);
1689         myDebitAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.INVESTED, myInvested);
1690         if (isForeignDebit) {
1691             myInvested = myInvested.convertCurrency(myDebitAsset.getCurrency().getCurrency(), myDebitRate);
1692             myInvested.negate();
1693             myDebitAsset.adjustCounter(MoneyWiseAnalysisSecurityAttr.FOREIGNINVESTED, myInvested);
1694         }
1695 
1696         /* Register the transaction */
1697         myDebitValues = myDebitAsset.registerTransaction(theHelper);
1698         myDebitValues.setValue(MoneyWiseAnalysisSecurityAttr.PRICE, myDebitPrice);
1699         myDebitValues.setValue(MoneyWiseAnalysisSecurityAttr.VALUATION, myDebitValue);
1700         myDebitValues.setValue(MoneyWiseAnalysisSecurityAttr.CONSIDERATION, myConsideration);
1701         myDebitValues.setValue(MoneyWiseAnalysisSecurityAttr.RETURNEDCASH, myAmount);
1702         myDebitValues.setValue(MoneyWiseAnalysisSecurityAttr.XFERREDVALUE, myCreditXferValue);
1703         myDebitValues.setValue(MoneyWiseAnalysisSecurityAttr.XFERREDCOST, myCostXfer);
1704         myDebitValues.setValue(MoneyWiseAnalysisSecurityAttr.ALLOWEDCOST, myAllowedCost);
1705         if (myCostDilution != null) {
1706             myDebitValues.setValue(MoneyWiseAnalysisSecurityAttr.COSTDILUTION, myCostDilution);
1707         }
1708         if (myCapitalGain.isNonZero()) {
1709             myDebitValues.setValue(MoneyWiseAnalysisSecurityAttr.CAPITALGAIN, myCapitalGain);
1710         }
1711         if (isForeignDebit) {
1712             myDebitValues.setValue(MoneyWiseAnalysisSecurityAttr.EXCHANGERATE, myDebitRate);
1713         }
1714         myDebitValues.setValue(MoneyWiseAnalysisSecurityAttr.CASHTYPE, isLargeCash
1715                 ? MoneyWiseCashType.LARGECASH
1716                 : MoneyWiseCashType.SMALLCASH);
1717 
1718         /* Adjust the ThirdParty account bucket */
1719         final MoneyWiseAnalysisAccountBucket<?> myBucket = getAccountBucket((MoneyWiseAssetBase) myReturnedCashAccount);
1720         myBucket.adjustForReturnedCashCredit(theHelper);
1721     }
1722 
1723     /**
1724      * Obtain Account bucket for asset.
1725      *
1726      * @param pAsset the asset
1727      * @return the bucket
1728      */
1729     private MoneyWiseAnalysisAccountBucket<?> getAccountBucket(final MoneyWiseAssetBase pAsset) {
1730         switch (pAsset.getAssetType()) {
1731             case DEPOSIT:
1732                 return theDepositBuckets.getBucket((MoneyWiseDeposit) pAsset);
1733             case CASH:
1734                 return theCashBuckets.getBucket((MoneyWiseCash) pAsset);
1735             case LOAN:
1736                 return theLoanBuckets.getBucket((MoneyWiseLoan) pAsset);
1737             case PORTFOLIO:
1738                 return thePortfolioBuckets.getCashBucket((MoneyWisePortfolio) pAsset);
1739             default:
1740                 throw new IllegalArgumentException();
1741         }
1742     }
1743 }