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.quicken.file;
18  
19  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDataSet;
20  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDeposit;
21  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePayee;
22  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePortfolio;
23  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity;
24  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityHolding;
25  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityPrice.MoneyWiseSecurityPriceDataMap;
26  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransAsset;
27  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransaction;
28  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransCategoryClass;
29  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysis;
30  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisPortfolioBucket;
31  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisPortfolioBucket.MoneyWiseAnalysisPortfolioBucketList;
32  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisPortfolioCashBucket;
33  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisSecurityBucket;
34  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.values.MoneyWiseAnalysisAccountAttr;
35  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.values.MoneyWiseAnalysisSecurityAttr;
36  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.values.MoneyWiseAnalysisSecurityValues;
37  import io.github.tonywasher.joceanus.moneywise.quicken.definitions.MoneyWiseQActionType;
38  import io.github.tonywasher.joceanus.moneywise.quicken.definitions.MoneyWiseQIFType;
39  import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
40  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusDecimal;
41  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusMoney;
42  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusPrice;
43  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRatio;
44  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusUnits;
45  import io.github.tonywasher.joceanus.oceanus.logger.OceanusLogManager;
46  import io.github.tonywasher.joceanus.oceanus.logger.OceanusLogger;
47  
48  import java.util.Iterator;
49  import java.util.List;
50  
51  /**
52   * Portfolio Builder class for QIF File.
53   */
54  public class MoneyWiseQIFPortfolioBuilder {
55      /**
56       * Logger.
57       */
58      private static final OceanusLogger LOGGER = OceanusLogManager.getLogger(MoneyWiseQIFPortfolioBuilder.class);
59  
60      /**
61       * The QIF register.
62       */
63      private final MoneyWiseQIFRegister theRegister;
64  
65      /**
66       * The QIF File Type.
67       */
68      private final MoneyWiseQIFType theFileType;
69  
70      /**
71       * The Builder.
72       */
73      private final MoneyWiseQIFHelper theHelper;
74  
75      /**
76       * The Data.
77       */
78      private final MoneyWiseDataSet theData;
79  
80      /**
81       * The Analysis.
82       */
83      private final MoneyWiseAnalysis theAnalysis;
84  
85      /**
86       * Constructor.
87       *
88       * @param pHelper   the builder helper
89       * @param pData     the data
90       * @param pAnalysis the analysis
91       */
92      protected MoneyWiseQIFPortfolioBuilder(final MoneyWiseQIFHelper pHelper,
93                                             final MoneyWiseDataSet pData,
94                                             final MoneyWiseAnalysis pAnalysis) {
95          /* Store parameters */
96          theHelper = pHelper;
97          theRegister = theHelper.getRegister();
98          theFileType = theRegister.getFileType();
99          theData = pData;
100         theAnalysis = pAnalysis;
101     }
102 
103     /**
104      * Obtain latest price for a security.
105      *
106      * @param pSecurity the security
107      * @param pDate     the date
108      * @return the price
109      */
110     private OceanusPrice getPriceForDate(final MoneyWiseSecurity pSecurity,
111                                          final OceanusDate pDate) {
112         /* Add the price */
113         final MoneyWiseSecurityPriceDataMap myPriceMap = theData.getSecurityPriceDataMap();
114         return myPriceMap.getPriceForDate(pSecurity, pDate);
115     }
116 
117     /**
118      * Obtain resulting units for a security holding event.
119      *
120      * @param pHolding the security holding
121      * @param pTrans   the transaction
122      * @return the units
123      */
124     private OceanusUnits getUnitsForHoldingEvent(final MoneyWiseSecurityHolding pHolding,
125                                                  final MoneyWiseTransaction pTrans) {
126         /* Access the relevant bucket */
127         final MoneyWiseAnalysisPortfolioBucketList myPortfolios = theAnalysis.getPortfolios();
128         final MoneyWiseAnalysisSecurityBucket myBucket = myPortfolios.getBucket(pHolding);
129 
130         /* Access the resulting values */
131         final MoneyWiseAnalysisSecurityValues myValues = myBucket.getValuesForTransaction(pTrans);
132         return myValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
133     }
134 
135     /**
136      * Obtain base units for a security holding event.
137      *
138      * @param pHolding the security holding
139      * @param pTrans   the transaction
140      * @return the units
141      */
142     protected OceanusUnits getBaseUnitsForHolding(final MoneyWiseSecurityHolding pHolding,
143                                                   final MoneyWiseTransaction pTrans) {
144         /* Access the relevant bucket */
145         final MoneyWiseAnalysisPortfolioBucketList myPortfolios = theAnalysis.getPortfolios();
146         final MoneyWiseAnalysisSecurityBucket myBucket = myPortfolios.getBucket(pHolding);
147 
148         /* Access the base values */
149         final MoneyWiseAnalysisSecurityValues myValues = myBucket.getValuesForTransaction(pTrans);
150         if (myValues != null) {
151             OceanusUnits myUnits = myValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
152             myUnits = new OceanusUnits(myUnits);
153 
154             /* Determine the delta in units */
155             final OceanusUnits myDelta = myBucket.getUnitsDeltaForTransaction(pTrans, MoneyWiseAnalysisSecurityAttr.UNITS);
156             if (myDelta != null) {
157                 myUnits.subtractUnits(myDelta);
158             }
159             return myUnits;
160         } else {
161             return OceanusUnits.getWholeUnits(0);
162         }
163     }
164 
165     /**
166      * Obtain delta cost for a security holding.
167      *
168      * @param pHolding the security holding
169      * @param pTrans   the transaction
170      * @return the delta cost
171      */
172     private OceanusMoney getDeltaCostForHolding(final MoneyWiseSecurityHolding pHolding,
173                                                 final MoneyWiseTransaction pTrans) {
174         /* Access the relevant bucket */
175         final MoneyWiseAnalysisPortfolioBucketList myPortfolios = theAnalysis.getPortfolios();
176         final MoneyWiseAnalysisSecurityBucket myBucket = myPortfolios.getBucket(pHolding);
177 
178         /* Obtain the cost delta for the transaction */
179         return myBucket.getMoneyDeltaForTransaction(pTrans, MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);
180     }
181 
182     /**
183      * Obtain portfolio cash value.
184      *
185      * @param pPortfolio the portfolio
186      * @param pTrans     the transaction
187      * @return the cash value (or null if none)
188      */
189     private OceanusMoney getPortfolioCashValue(final MoneyWisePortfolio pPortfolio,
190                                                final MoneyWiseTransaction pTrans) {
191         /* Access the relevant bucket */
192         final MoneyWiseAnalysisPortfolioBucketList myPortfolios = theAnalysis.getPortfolios();
193         final MoneyWiseAnalysisPortfolioCashBucket myBucket = myPortfolios.getCashBucket(pPortfolio);
194 
195         /* Obtain the value delta for the transaction */
196         OceanusMoney myValue = myBucket.getMoneyDeltaForTransaction(pTrans, MoneyWiseAnalysisAccountAttr.VALUATION);
197         if (myValue != null) {
198             myValue = new OceanusMoney(myValue);
199             myValue.negate();
200         }
201         return myValue;
202     }
203 
204     /**
205      * Process income to a security.
206      *
207      * @param pPayee   the payee
208      * @param pHolding the security holding
209      * @param pTrans   the transaction
210      */
211     protected void processIncomeToSecurity(final MoneyWisePayee pPayee,
212                                            final MoneyWiseSecurityHolding pHolding,
213                                            final MoneyWiseTransaction pTrans) {
214         /* Access Portfolio Account */
215         final MoneyWisePortfolio myPort = pHolding.getPortfolio();
216         final MoneyWiseSecurity mySecurity = pHolding.getSecurity();
217         final MoneyWiseQIFAccountEvents myPortfolio = theRegister.registerAccount(myPort);
218 
219         /* Determine style */
220         final boolean useHoldingAccount = theFileType.useInvestmentHolding4Category();
221 
222         /* Access Transaction details */
223         final MoneyWiseQIFPayee myQPayee = theRegister.registerPayee(pPayee);
224         final MoneyWiseQIFSecurity myQSecurity = theRegister.registerSecurity(mySecurity);
225         final MoneyWiseQIFEventCategory myQCategory = theRegister.registerCategory(pTrans.getCategory());
226 
227         /* Obtain classes */
228         final List<MoneyWiseQIFClass> myList = theHelper.getTransactionClasses(pTrans);
229 
230         /* Access details */
231         final OceanusMoney myAmount = pTrans.getAmount();
232         final OceanusUnits myUnits = pTrans.getAccountDeltaUnits();
233         final OceanusPrice myPrice = getPriceForDate(mySecurity, pTrans.getDate());
234 
235         /* If we are using a holding account */
236         if (useHoldingAccount) {
237             /* Access Holding Account */
238             final MoneyWiseQIFAccountEvents myHolding = theRegister.registerHoldingAccount(myPort);
239 
240             /* Create output amount */
241             final OceanusMoney myOutAmount = new OceanusMoney(myAmount);
242             myOutAmount.negate();
243 
244             /* Create an event */
245             final MoneyWiseQIFEvent myEvent = new MoneyWiseQIFEvent(theRegister, pTrans);
246             myEvent.recordAmount(new OceanusMoney());
247             myEvent.recordPayee(myQPayee);
248 
249             /* record the splits */
250             myEvent.recordSplitRecord(myQCategory, myList, myAmount, myQPayee.getName());
251             myEvent.recordSplitRecord(myPortfolio.getAccount(), myOutAmount, myPort.getName());
252 
253             /* Add to the event list */
254             myHolding.addEvent(myEvent);
255 
256             /* else we can do this properly */
257         } else {
258             /* Create a miscellaneous cash event */
259             final MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.CASH);
260             myEvent.recordAmount(myAmount);
261             myEvent.recordPayee(myQPayee);
262             myEvent.recordCategory(myQCategory, myList);
263 
264             /* Add to event list */
265             myPortfolio.addEvent(myEvent);
266         }
267 
268         /* Create a buy shares event */
269         final MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.BUY);
270         myEvent.recordAmount(myAmount);
271         myEvent.recordSecurity(myQSecurity);
272         myEvent.recordQuantity(myUnits);
273         myEvent.recordPrice(myPrice);
274 
275         /* Add to event list */
276         myPortfolio.addEvent(myEvent);
277     }
278 
279     /**
280      * Process expense from a security.
281      *
282      * @param pPayee   the payee
283      * @param pHolding the security holding
284      * @param pTrans   the transaction
285      */
286     protected void processExpenseFromSecurity(final MoneyWisePayee pPayee,
287                                               final MoneyWiseSecurityHolding pHolding,
288                                               final MoneyWiseTransaction pTrans) {
289         /* Access Portfolio Account */
290         final MoneyWisePortfolio myPort = pHolding.getPortfolio();
291         final MoneyWiseSecurity mySecurity = pHolding.getSecurity();
292         final MoneyWiseQIFAccountEvents myPortfolio = theRegister.registerAccount(myPort);
293 
294         /* Determine style */
295         final boolean useHoldingAccount = theFileType.useInvestmentHolding4Category();
296 
297         /* Access Transaction details */
298         final MoneyWiseQIFPayee myQPayee = theRegister.registerPayee(pPayee);
299         final MoneyWiseQIFSecurity myQSecurity = theRegister.registerSecurity(mySecurity);
300         final MoneyWiseQIFEventCategory myQCategory = theRegister.registerCategory(pTrans.getCategory());
301 
302         /* Obtain classes */
303         final List<MoneyWiseQIFClass> myList = theHelper.getTransactionClasses(pTrans);
304 
305         /* Access details */
306         final OceanusMoney myAmount = pTrans.getAmount();
307         OceanusUnits myUnits = pTrans.getAccountDeltaUnits();
308         myUnits = new OceanusUnits(myUnits);
309         myUnits.negate();
310         final OceanusPrice myPrice = getPriceForDate(mySecurity, pTrans.getDate());
311 
312         /* Create a sell shares event */
313         MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.SELL);
314         myEvent.recordAmount(myAmount);
315         myEvent.recordSecurity(myQSecurity);
316         myEvent.recordQuantity(myUnits);
317         myEvent.recordPrice(myPrice);
318 
319         /* Add to event list */
320         myPortfolio.addEvent(myEvent);
321 
322         /* Create output amount */
323         final OceanusMoney myOutAmount = new OceanusMoney(myAmount);
324         myOutAmount.negate();
325 
326         /* If we are using a holding account */
327         if (useHoldingAccount) {
328             /* Access Holding Account */
329             final MoneyWiseQIFAccountEvents myHolding = theRegister.registerHoldingAccount(myPort);
330 
331             /* Create an event */
332             final MoneyWiseQIFEvent myHoldEvent = new MoneyWiseQIFEvent(theRegister, pTrans);
333             myHoldEvent.recordAmount(new OceanusMoney());
334             myHoldEvent.recordPayee(myQPayee);
335 
336             /* record the splits */
337             myHoldEvent.recordSplitRecord(myPortfolio.getAccount(), myAmount, myPort.getName());
338             myHoldEvent.recordSplitRecord(myQCategory, myList, myOutAmount, myQPayee.getName());
339 
340             /* Add to event list */
341             myHolding.addEvent(myEvent);
342 
343             /* else we can do this properly */
344         } else {
345             /* Create a miscellaneous cash event */
346             myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.CASH);
347             myEvent.recordAmount(myOutAmount);
348             myEvent.recordPayee(myQPayee);
349             myEvent.recordCategory(myQCategory, myList);
350 
351             /* Add to event list */
352             myPortfolio.addEvent(myEvent);
353         }
354     }
355 
356     /**
357      * Process transfer to a security.
358      * <p>
359      * Note that the source cannot be a Security, since that case is handled by
360      * {@link #processTransferFromSecurity}
361      *
362      * @param pHolding the security holding
363      * @param pDebit   the debit account
364      * @param pTrans   the transaction
365      */
366     protected void processTransferToSecurity(final MoneyWiseSecurityHolding pHolding,
367                                              final MoneyWiseTransAsset pDebit,
368                                              final MoneyWiseTransaction pTrans) {
369         /* Handle Loyalty bonus separately */
370         if (MoneyWiseTransCategoryClass.LOYALTYBONUS.equals(pTrans.getCategoryClass())) {
371             processIncomeToSecurity((MoneyWisePayee) pDebit.getParent(), pHolding, pTrans);
372             return;
373         }
374 
375         /* Access Portfolio Account */
376         final MoneyWisePortfolio myPort = pHolding.getPortfolio();
377         final MoneyWiseSecurity mySecurity = pHolding.getSecurity();
378         final MoneyWiseQIFAccountEvents myPortfolio = theRegister.registerAccount(myPort);
379 
380         /* Access Transaction details */
381         final MoneyWiseQIFAccountEvents mySource = theRegister.registerAccount(pDebit);
382         final MoneyWiseQIFSecurity myQSecurity = theRegister.registerSecurity(mySecurity);
383 
384         /* Determine various flags */
385         final boolean canTradeZeroShares = theFileType.canTradeZeroShares();
386         boolean canXferLinked = theFileType.canXferPortfolio();
387         boolean hideBalancingSplitXfer = theFileType.hideBalancingSplitTransfer();
388         hideBalancingSplitXfer &= canXferLinked;
389 
390         /* Check for transfer from portfolio */
391         if (pDebit.equals(myPort)) {
392             /* Make sure we don't try to link account */
393             canXferLinked = false;
394             hideBalancingSplitXfer = true;
395         }
396 
397         /* Obtain classes */
398         final List<MoneyWiseQIFClass> myList = theHelper.getTransactionClasses(pTrans);
399 
400         /* Access details */
401         final OceanusMoney myAmount = pTrans.getAmount();
402         OceanusUnits myUnits = pTrans.getAccountDeltaUnits();
403         if (myUnits == null) {
404             myUnits = pTrans.getPartnerDeltaUnits();
405         }
406         final OceanusPrice myPrice = getPriceForDate(mySecurity, pTrans.getDate());
407 
408         /* Handle zero units */
409         boolean autoCorrectZeroUnits = false;
410         if (myUnits == null) {
411             if (!canTradeZeroShares) {
412                 myUnits = OceanusUnits.getWholeUnits(1);
413                 autoCorrectZeroUnits = true;
414             } else {
415                 myUnits = new OceanusUnits();
416             }
417         }
418 
419         /* Create a buy shares event for the new shares */
420         MoneyWiseQIFPortfolioEvent myPortEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, canXferLinked
421                 ? MoneyWiseQActionType.BUYX
422                 : MoneyWiseQActionType.BUY);
423         myPortEvent.recordAmount(myAmount);
424         myPortEvent.recordSecurity(myQSecurity);
425         myPortEvent.recordQuantity(myUnits);
426         myPortEvent.recordPrice(myPrice);
427         if (canXferLinked) {
428             myPortEvent.recordXfer(mySource.getAccount(), myList, myAmount);
429         }
430 
431         /* Add to event list */
432         myPortfolio.addEvent(myPortEvent);
433 
434         /* If we need to autoCorrect */
435         if (autoCorrectZeroUnits) {
436             /* Create a ShrsOut event to balance */
437             myPortEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.SHRSOUT);
438             myPortEvent.recordSecurity(myQSecurity);
439             myPortEvent.recordQuantity(myUnits);
440 
441             /* Add to event list */
442             myPortfolio.addEvent(myPortEvent);
443         }
444 
445         /* If we are not hiding the balancing transfer */
446         if (!hideBalancingSplitXfer) {
447             /* Build output amount */
448             final OceanusMoney myOutAmount = new OceanusMoney(myAmount);
449             myOutAmount.negate();
450 
451             /* Build the source transfer */
452             final MoneyWiseQIFEvent myEvent = new MoneyWiseQIFEvent(theRegister, pTrans);
453             myEvent.recordAccount(myPortfolio.getAccount(), myList);
454             myEvent.recordAmount(myOutAmount);
455 
456             /* Build payee description */
457             myEvent.recordPayee(theHelper.buildXferToPayee(myPort));
458 
459             /* Add event to event list */
460             mySource.addEvent(myEvent);
461         }
462     }
463 
464     /**
465      * Process transfer between securities.
466      *
467      * @param pSource the source security holding
468      * @param pTarget the target security holding
469      * @param pTrans  the transaction
470      */
471     protected void processTransferBetweenSecurities(final MoneyWiseSecurityHolding pSource,
472                                                     final MoneyWiseSecurityHolding pTarget,
473                                                     final MoneyWiseTransaction pTrans) {
474         /* Switch on transaction type */
475         switch (pTrans.getCategoryClass()) {
476             case STOCKSPLIT:
477                 if (theFileType.useStockSplit()) {
478                     processStockSplit(pSource, pTrans);
479                 } else {
480                     processSecurityAdjust(pSource, pTrans);
481                 }
482                 break;
483             case UNITSADJUST:
484                 processSecurityAdjust(pSource, pTrans);
485                 break;
486             case DIVIDEND:
487                 processReinvestDividend(pSource, pTrans);
488                 break;
489             case STOCKDEMERGER:
490                 processStockDeMerger(pSource, pTarget, pTrans);
491                 break;
492             case STOCKTAKEOVER, SECURITYREPLACE:
493                 processStockTakeOver(pSource, pTarget, pTrans);
494                 break;
495             case TRANSFER:
496                 processSecurityExchange(pSource, pTarget, pTrans);
497                 break;
498             default:
499                 LOGGER.error("Unsupported TransferBetweenSecurities Category: <%s>", pTrans.getCategoryClass());
500                 break;
501         }
502     }
503 
504     /**
505      * Process transfer from a security.
506      *
507      * @param pHolding the security holding
508      * @param pCredit  the credit account
509      * @param pTrans   the transaction
510      */
511     protected void processTransferFromSecurity(final MoneyWiseSecurityHolding pHolding,
512                                                final MoneyWiseTransAsset pCredit,
513                                                final MoneyWiseTransaction pTrans) {
514         /* Switch on transaction type */
515         switch (pTrans.getCategoryClass()) {
516             case DIVIDEND -> processStockDividend(pHolding, pCredit, pTrans);
517             case PORTFOLIOXFER -> processPortfolioXferForHolding(pHolding, (MoneyWisePortfolio) pCredit, pTrans);
518             default -> processTransferOut(pHolding, pCredit, pTrans);
519         }
520     }
521 
522     /**
523      * Process Stock Split.
524      *
525      * @param pHolding the security holding
526      * @param pTrans   the transaction
527      */
528     private void processStockSplit(final MoneyWiseSecurityHolding pHolding,
529                                    final MoneyWiseTransaction pTrans) {
530         /* Access Portfolio Account */
531         final MoneyWisePortfolio myPortfolio = pHolding.getPortfolio();
532         final MoneyWiseSecurity mySecurity = pHolding.getSecurity();
533         final MoneyWiseQIFAccountEvents myQPortfolio = theRegister.registerAccount(myPortfolio);
534 
535         /* Access Transaction details */
536         final MoneyWiseQIFSecurity myQSecurity = theRegister.registerSecurity(mySecurity);
537 
538         /* Obtain number of units after this event */
539         final OceanusUnits myTotalUnits = getUnitsForHoldingEvent(pHolding, pTrans);
540 
541         /* Access the delta units */
542         final OceanusUnits myDeltaUnits = pTrans.getAccountDeltaUnits();
543 
544         /* Obtain number of units before event */
545         final OceanusUnits myBaseUnits = new OceanusUnits(myTotalUnits);
546         myBaseUnits.subtractUnits(myDeltaUnits);
547 
548         /* Obtain split ratio */
549         final OceanusRatio mySplit = new OceanusRatio(myTotalUnits, myBaseUnits);
550         mySplit.multiply(OceanusDecimal.RADIX_TEN);
551 
552         /* Create a stock split event */
553         final MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.STKSPLIT);
554         myEvent.recordSecurity(myQSecurity);
555         myEvent.recordQuantity(mySplit);
556 
557         /* Add to the event list */
558         myQPortfolio.addEvent(myEvent);
559     }
560 
561     /**
562      * Process stock adjustment.
563      *
564      * @param pHolding the security holding
565      * @param pTrans   the transaction
566      */
567     private void processSecurityAdjust(final MoneyWiseSecurityHolding pHolding,
568                                        final MoneyWiseTransaction pTrans) {
569         /* Access Portfolio Account */
570         final MoneyWisePortfolio myPortfolio = pHolding.getPortfolio();
571         final MoneyWiseSecurity mySecurity = pHolding.getSecurity();
572         final MoneyWiseQIFAccountEvents myQPortfolio = theRegister.registerAccount(myPortfolio);
573 
574         /* Access Transaction details */
575         final MoneyWiseQIFSecurity myQSecurity = theRegister.registerSecurity(mySecurity);
576 
577         /* Access the delta units */
578         OceanusUnits myUnits = pTrans.getAccountDeltaUnits();
579         final boolean isCredit = myUnits.isPositive();
580         if (!isCredit) {
581             myUnits = new OceanusUnits(myUnits);
582             myUnits.negate();
583         }
584 
585         /* Create a share movement event */
586         final MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, isCredit
587                 ? MoneyWiseQActionType.SHRSIN
588                 : MoneyWiseQActionType.SHRSOUT);
589         myEvent.recordSecurity(myQSecurity);
590         myEvent.recordQuantity(myUnits);
591 
592         /* Add to event list */
593         myQPortfolio.addEvent(myEvent);
594     }
595 
596     /**
597      * Process stock dividend.
598      *
599      * @param pHolding the security holding
600      * @param pCredit  the credit account
601      * @param pTrans   the transaction
602      */
603     private void processStockDividend(final MoneyWiseSecurityHolding pHolding,
604                                       final MoneyWiseTransAsset pCredit,
605                                       final MoneyWiseTransaction pTrans) {
606         /* Access Portfolio Account */
607         final MoneyWisePortfolio myPortfolio = pHolding.getPortfolio();
608         final MoneyWiseSecurity mySecurity = pHolding.getSecurity();
609         final MoneyWiseQIFAccountEvents myQPortfolio = theRegister.registerAccount(myPortfolio);
610 
611         /* Obtain flags */
612         boolean canXferLinked = theFileType.canXferPortfolio();
613         final boolean isPortfolio = pCredit.equals(myPortfolio);
614 
615         /* Access Transaction details */
616         final MoneyWiseQIFSecurity myQSecurity = theRegister.registerSecurity(mySecurity);
617         final MoneyWiseQIFAccountEvents myTarget = theRegister.registerAccount(pCredit);
618         OceanusMoney myAmount = pTrans.getAmount();
619         final OceanusMoney myTaxCredit = pTrans.getTaxCredit();
620         final OceanusMoney myFullAmount = new OceanusMoney(myAmount);
621         if (myTaxCredit != null) {
622             myFullAmount.addAmount(myTaxCredit);
623         }
624 
625         /* Obtain classes */
626         final List<MoneyWiseQIFClass> myList = theHelper.getTransactionClasses(pTrans);
627 
628         /* Determine whether we should XferLinked */
629         boolean doXferLinked = canXferLinked && myTaxCredit == null;
630 
631         /* Check for dividend held in portfolio */
632         if (isPortfolio) {
633             /* Make sure we don't try to link account */
634             doXferLinked = false;
635             canXferLinked = false;
636         }
637 
638         /* Create a dividend event */
639         MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, doXferLinked
640                 ? MoneyWiseQActionType.DIVX
641                 : MoneyWiseQActionType.DIV);
642         myEvent.recordSecurity(myQSecurity);
643         myEvent.recordAmount(myFullAmount);
644         if (doXferLinked) {
645             myEvent.recordPayee(theHelper.buildXferFromPayee(myPortfolio));
646             myEvent.recordXfer(myTarget.getAccount(), myList, myAmount);
647         }
648 
649         /* Add to event list */
650         myQPortfolio.addEvent(myEvent);
651 
652         /* If we can use XOut records */
653         if (!doXferLinked && canXferLinked) {
654             /* Create a transfer out event */
655             myAmount = pTrans.getAmount();
656             myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.XOUT);
657             myEvent.recordAmount(myAmount);
658             myEvent.recordPayee(theHelper.buildXferFromPayee(myPortfolio));
659             myEvent.recordXfer(myTarget.getAccount(), myList, myAmount);
660 
661             /* Add to event list */
662             myQPortfolio.addEvent(myEvent);
663         }
664 
665         /* Don't do if receiving dividend in portfolio */
666         if (!isPortfolio) {
667             /* If the receiving account is a portfolio */
668             if (pCredit instanceof MoneyWisePortfolio) {
669                 /* Create the receiving transfer event */
670                 final MoneyWiseQIFPortfolioEvent myXferEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.XIN);
671                 myXferEvent.recordAmount(myAmount);
672                 myXferEvent.recordPayee(theHelper.buildXferFromPayee(myPortfolio));
673                 myXferEvent.recordXfer(myQPortfolio.getAccount(), myList, myAmount);
674 
675                 /* Add to event list */
676                 myTarget.addEvent(myXferEvent);
677 
678                 /* else standard account */
679             } else {
680                 /* Create the receiving transfer event */
681                 final MoneyWiseQIFEvent myXferEvent = new MoneyWiseQIFEvent(theRegister, pTrans);
682                 myXferEvent.recordAmount(myAmount);
683                 myXferEvent.recordPayee(theHelper.buildXferFromPayee(myPortfolio));
684                 myXferEvent.recordAccount(myQPortfolio.getAccount(), myList);
685 
686                 /* Add to event list */
687                 myTarget.addEvent(myXferEvent);
688             }
689         }
690 
691         /* If we have a Tax Credit */
692         if (myTaxCredit != null) {
693             /* Determine flags */
694             final boolean useHoldingAccount = theFileType.useInvestmentHolding4Category();
695 
696             /* Access category */
697             final MoneyWiseQIFEventCategory myTaxCategory = theHelper.getTaxCategory();
698             final MoneyWiseQIFPayee myTaxPayee = theHelper.getTaxMan();
699 
700             /* Create output amount */
701             final OceanusMoney myOutAmount = new OceanusMoney(myTaxCredit);
702             myOutAmount.negate();
703 
704             /* If we are using a holding account */
705             if (useHoldingAccount) {
706                 /* Access Holding Account */
707                 final MoneyWiseQIFAccountEvents myHolding = theRegister.registerHoldingAccount(myPortfolio);
708 
709                 /* Create an event */
710                 final MoneyWiseQIFEvent myHoldEvent = new MoneyWiseQIFEvent(theRegister, pTrans);
711                 myHoldEvent.recordAmount(new OceanusMoney());
712                 myHoldEvent.recordPayee(myTaxPayee);
713 
714                 /* record the splits */
715                 myHoldEvent.recordSplitRecord(myQPortfolio.getAccount(), myTaxCredit, myPortfolio.getName());
716                 myHoldEvent.recordSplitRecord(myTaxCategory, myOutAmount, myTaxPayee.getName());
717 
718                 /* Add to event list */
719                 myHolding.addEvent(myHoldEvent);
720 
721                 /* else we can do this properly */
722             } else {
723                 /* Create a tax credit event */
724                 myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.CASH);
725                 myEvent.recordAmount(myOutAmount);
726                 myEvent.recordPayee(myTaxPayee);
727                 myEvent.recordCategory(myTaxCategory);
728 
729                 /* Add to event list */
730                 myQPortfolio.addEvent(myEvent);
731             }
732         }
733     }
734 
735     /**
736      * Process reinvested dividend.
737      *
738      * @param pHolding the security holding
739      * @param pTrans   the transaction
740      */
741     private void processReinvestDividend(final MoneyWiseSecurityHolding pHolding,
742                                          final MoneyWiseTransaction pTrans) {
743         /* Access Portfolio Account */
744         final MoneyWisePortfolio myPortfolio = pHolding.getPortfolio();
745         final MoneyWiseSecurity mySecurity = pHolding.getSecurity();
746         final MoneyWiseQIFAccountEvents myQPortfolio = theRegister.registerAccount(myPortfolio);
747 
748         /* Determine various flags */
749         final boolean canTradeZeroShares = theFileType.canTradeZeroShares();
750 
751         /* Access Transaction details */
752         final MoneyWiseQIFSecurity myQSecurity = theRegister.registerSecurity(mySecurity);
753         OceanusMoney myAmount = pTrans.getAmount();
754         OceanusUnits myUnits = pTrans.getAccountDeltaUnits();
755         final OceanusMoney myTaxCredit = pTrans.getTaxCredit();
756         myAmount = new OceanusMoney(myAmount);
757         if (myTaxCredit != null) {
758             myAmount.addAmount(myTaxCredit);
759         }
760 
761         /* Handle zero units */
762         boolean autoCorrectZeroUnits = false;
763         if (myUnits == null) {
764             if (!canTradeZeroShares) {
765                 myUnits = OceanusUnits.getWholeUnits(1);
766                 autoCorrectZeroUnits = true;
767             } else {
768                 myUnits = new OceanusUnits();
769             }
770         }
771 
772         /* Create a re-invest dividend event */
773         MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.REINVDIV);
774         myEvent.recordSecurity(myQSecurity);
775         myEvent.recordAmount(myAmount);
776         myEvent.recordQuantity(myUnits);
777 
778         /* Add to event list */
779         myQPortfolio.addEvent(myEvent);
780 
781         /* If we need to autoCorrect */
782         if (autoCorrectZeroUnits) {
783             /* Create a ShrsOut event to balance */
784             myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.SHRSOUT);
785             myEvent.recordSecurity(myQSecurity);
786             myEvent.recordQuantity(myUnits);
787 
788             /* Add to event list */
789             myQPortfolio.addEvent(myEvent);
790         }
791 
792         /* If we have a Tax Credit */
793         if (myTaxCredit != null) {
794             /* Determine flags */
795             final boolean useHoldingAccount = theFileType.useInvestmentHolding4Category();
796             final boolean useMiscIncX = theFileType.useMiscIncX4TaxCredit();
797 
798             /* Access category */
799             final MoneyWiseQIFEventCategory myTaxCategory = theHelper.getTaxCategory();
800             final MoneyWiseQIFPayee myTaxPayee = theHelper.getTaxMan();
801 
802             /* Create a tax credit event */
803             myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, useMiscIncX
804                     ? MoneyWiseQActionType.MISCINCX
805                     : MoneyWiseQActionType.MISCINC);
806             myEvent.recordSecurity(myQSecurity);
807             myEvent.recordAmount(myTaxCredit);
808             if (useMiscIncX) {
809                 myEvent.recordPayee(myTaxPayee);
810                 myEvent.recordCategory(myTaxCategory);
811             }
812 
813             /* Add to event list */
814             myQPortfolio.addEvent(myEvent);
815 
816             /* If we need further elements */
817             if (!useMiscIncX) {
818                 /* Create output amount */
819                 final OceanusMoney myOutAmount = new OceanusMoney(myTaxCredit);
820                 myOutAmount.negate();
821 
822                 /* If we are using a holding account */
823                 if (useHoldingAccount) {
824                     /* Access Holding Account */
825                     final MoneyWiseQIFAccountEvents myHolding = theRegister.registerHoldingAccount(myPortfolio);
826 
827                     /* Create an event */
828                     final MoneyWiseQIFEvent myHoldEvent = new MoneyWiseQIFEvent(theRegister, pTrans);
829                     myHoldEvent.recordAmount(new OceanusMoney());
830                     myHoldEvent.recordPayee(myTaxPayee);
831 
832                     /* record the splits */
833                     myHoldEvent.recordSplitRecord(myQPortfolio.getAccount(), myTaxCredit, myPortfolio.getName());
834                     myHoldEvent.recordSplitRecord(myTaxCategory, myOutAmount, myTaxPayee.getName());
835 
836                     /* Add to event list */
837                     myHolding.addEvent(myHoldEvent);
838 
839                     /* else we can do this properly */
840                 } else {
841                     /* Create a tax credit event */
842                     myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.CASH);
843                     myEvent.recordAmount(myOutAmount);
844                     myEvent.recordPayee(myTaxPayee);
845                     myEvent.recordCategory(myTaxCategory);
846 
847                     /* Add to event list */
848                     myQPortfolio.addEvent(myEvent);
849                 }
850             }
851         }
852     }
853 
854     /**
855      * Process stock deMerger.
856      *
857      * @param pHolding the security holding
858      * @param pCredit  the credit account
859      * @param pTrans   the transaction
860      */
861     private void processStockDeMerger(final MoneyWiseSecurityHolding pHolding,
862                                       final MoneyWiseSecurityHolding pCredit,
863                                       final MoneyWiseTransaction pTrans) {
864         /* Access Portfolio Account */
865         final MoneyWisePortfolio myPortfolio = pHolding.getPortfolio();
866         final MoneyWiseSecurity mySecurity = pHolding.getSecurity();
867         final MoneyWiseSecurity myCredit = pCredit.getSecurity();
868         final MoneyWiseQIFAccountEvents myQPortfolio = theRegister.registerAccount(myPortfolio);
869 
870         /* Determine whether we can return capital */
871         final boolean canReturnCapital = theFileType.canReturnCapital();
872         final boolean canTradeZeroShares = theFileType.canTradeZeroShares();
873 
874         /* Access Transaction details */
875         final MoneyWiseQIFSecurity myDebitSecurity = theRegister.registerSecurity(mySecurity);
876         final MoneyWiseQIFSecurity myCreditSecurity = theRegister.registerSecurity(myCredit);
877 
878         /* Access details */
879         final OceanusDate myDate = pTrans.getDate();
880         OceanusUnits myUnits = pTrans.getAccountDeltaUnits();
881         if (myUnits != null) {
882             myUnits = new OceanusUnits(myUnits);
883             myUnits.negate();
884         }
885         final OceanusPrice myDebitPrice = getPriceForDate(mySecurity, myDate);
886         final OceanusPrice myCreditPrice = getPriceForDate(myCredit, myDate);
887 
888         /* Obtain the delta cost (i.e. value transferred) */
889         OceanusMoney myValue = getDeltaCostForHolding(pHolding, pTrans);
890         myValue = new OceanusMoney(myValue);
891         myValue.negate();
892 
893         /* Determine whether we use return capital */
894         final boolean doReturnCapital = canReturnCapital && myUnits == null;
895 
896         /* Handle zero units */
897         boolean autoCorrectZeroUnits = false;
898         if (!canReturnCapital && myUnits == null) {
899             if (!canTradeZeroShares) {
900                 myUnits = OceanusUnits.getWholeUnits(1);
901                 autoCorrectZeroUnits = true;
902             } else {
903                 myUnits = new OceanusUnits();
904             }
905         }
906 
907         /* Create a sellShares/returnCapital event for the share reduction */
908         MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, doReturnCapital
909                 ? MoneyWiseQActionType.RTRNCAP
910                 : MoneyWiseQActionType.SELL);
911         myEvent.recordAmount(myValue);
912         myEvent.recordSecurity(myDebitSecurity);
913         myEvent.recordPrice(myDebitPrice);
914         if (!doReturnCapital) {
915             myEvent.recordQuantity(myUnits);
916         }
917 
918         /* Add to event list */
919         myQPortfolio.addEvent(myEvent);
920 
921         /* If we need to autoCorrect */
922         if (autoCorrectZeroUnits) {
923             /* Create a ShrsIn event to balance */
924             myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.SHRSIN);
925             myEvent.recordSecurity(myDebitSecurity);
926             myEvent.recordQuantity(myUnits);
927 
928             /* Add to event list */
929             myQPortfolio.addEvent(myEvent);
930         }
931 
932         /* Create a buy shares event for the new shares */
933         myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.BUY);
934         myEvent.recordAmount(myValue);
935         myEvent.recordSecurity(myCreditSecurity);
936         myEvent.recordQuantity(pTrans.getPartnerDeltaUnits());
937         myEvent.recordPrice(myCreditPrice);
938 
939         /* Add to event list */
940         myQPortfolio.addEvent(myEvent);
941     }
942 
943     /**
944      * Process security Exchange/TakeOver.
945      *
946      * @param pSource the source security
947      * @param pTarget the target security
948      * @param pTrans  the transaction
949      */
950     private void processStockTakeOver(final MoneyWiseSecurityHolding pSource,
951                                       final MoneyWiseSecurityHolding pTarget,
952                                       final MoneyWiseTransaction pTrans) {
953         /* Access Portfolio Account */
954         final MoneyWisePortfolio myPortfolio = pSource.getPortfolio();
955         final MoneyWiseSecurity mySource = pSource.getSecurity();
956         final MoneyWiseSecurity myTarget = pTarget.getSecurity();
957         final MoneyWiseQIFAccountEvents myQPortfolio = theRegister.registerAccount(myPortfolio);
958 
959         /* Access Transaction details */
960         final MoneyWiseQIFSecurity myDebitSecurity = theRegister.registerSecurity(mySource);
961         final MoneyWiseQIFSecurity myCreditSecurity = theRegister.registerSecurity(myTarget);
962 
963         /* Access details */
964         final OceanusDate myDate = pTrans.getDate();
965         final OceanusUnits myUnits = pTrans.getPartnerDeltaUnits();
966         final OceanusPrice myDebitPrice = getPriceForDate(mySource, myDate);
967         final OceanusPrice myCreditPrice = getPriceForDate(myTarget, myDate);
968         final MoneyWiseDeposit myThirdParty = (MoneyWiseDeposit) pTrans.getReturnedCashAccount();
969         final OceanusMoney myAmount = pTrans.getReturnedCash();
970 
971         /* Obtain the number of units that we are selling */
972         final OceanusUnits myBaseUnits = getBaseUnitsForHolding(pSource, pTrans);
973 
974         /* Obtain the delta cost (i.e. value transferred) */
975         final OceanusMoney myStockCost = getDeltaCostForHolding(pSource, pTrans);
976 
977         /* Determine the total sale value */
978         final OceanusMoney mySaleValue = new OceanusMoney(myStockCost);
979         mySaleValue.addAmount(myAmount);
980 
981         /* Create a sellShares event for the share reduction */
982         MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.SELL);
983         myEvent.recordAmount(mySaleValue);
984         myEvent.recordSecurity(myDebitSecurity);
985         myEvent.recordPrice(myDebitPrice);
986         myEvent.recordQuantity(myBaseUnits);
987 
988         /* Add to event list */
989         myQPortfolio.addEvent(myEvent);
990 
991         /* Create a buy shares event for the new shares */
992         myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.BUY);
993         myEvent.recordAmount(myStockCost);
994         myEvent.recordSecurity(myCreditSecurity);
995         myEvent.recordQuantity(myUnits);
996         myEvent.recordPrice(myCreditPrice);
997 
998         /* Add to event list */
999         myQPortfolio.addEvent(myEvent);
1000 
1001         /* If we have a ThirdParty Account */
1002         if (myThirdParty != null) {
1003             /* determine flags */
1004             final boolean canXferDirect = theFileType.canXferPortfolio();
1005 
1006             /* Access Target account */
1007             final MoneyWiseQIFAccountEvents myQTarget = theRegister.registerAccount(myThirdParty);
1008 
1009             /* If we can transfer direct */
1010             if (canXferDirect) {
1011                 /* Create a transfer out event for the cash payment */
1012                 myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.XOUT);
1013                 myEvent.recordAmount(myAmount);
1014                 myEvent.recordXfer(myQTarget.getAccount(), myAmount);
1015 
1016                 /* Add to event list */
1017                 myQPortfolio.addEvent(myEvent);
1018             } else {
1019                 /* Build the target transfer */
1020                 final MoneyWiseQIFEvent myXferEvent = new MoneyWiseQIFEvent(theRegister, pTrans);
1021                 myXferEvent.recordAccount(myQPortfolio.getAccount());
1022                 myXferEvent.recordAmount(myAmount);
1023 
1024                 /* Build payee description */
1025                 myEvent.recordPayee(theHelper.buildXferFromPayee(myPortfolio));
1026 
1027                 /* Add event to event list */
1028                 myQTarget.addEvent(myEvent);
1029             }
1030         }
1031     }
1032 
1033     /**
1034      * Process standard transfer out from a security.
1035      *
1036      * @param pHolding the security holding
1037      * @param pCredit  the credit account
1038      * @param pTrans   the transaction
1039      */
1040     private void processTransferOut(final MoneyWiseSecurityHolding pHolding,
1041                                     final MoneyWiseTransAsset pCredit,
1042                                     final MoneyWiseTransaction pTrans) {
1043         /* Access Portfolio Account */
1044         final MoneyWisePortfolio myPortfolio = pHolding.getPortfolio();
1045         final MoneyWiseSecurity mySecurity = pHolding.getSecurity();
1046         final MoneyWiseQIFAccountEvents myQPortfolio = theRegister.registerAccount(myPortfolio);
1047 
1048         /* Access Transaction details */
1049         final MoneyWiseQIFAccountEvents myTarget = theRegister.registerAccount(pCredit);
1050         final MoneyWiseQIFSecurity myQSecurity = theRegister.registerSecurity(mySecurity);
1051 
1052         /* Determine various flags */
1053         final boolean canReturnCapital = theFileType.canReturnCapital();
1054         final boolean canTradeZeroShares = theFileType.canTradeZeroShares();
1055         boolean canXferLinked = theFileType.canXferPortfolio();
1056         boolean hideBalancingSplitXfer = theFileType.hideBalancingSplitTransfer();
1057         hideBalancingSplitXfer &= canXferLinked;
1058 
1059         /* Check for transfer to portfolio */
1060         if (pCredit.equals(myPortfolio)) {
1061             /* Make sure we don't try to link account */
1062             canXferLinked = false;
1063             hideBalancingSplitXfer = true;
1064         }
1065 
1066         /* Obtain classes */
1067         final List<MoneyWiseQIFClass> myList = theHelper.getTransactionClasses(pTrans);
1068 
1069         /* Access details */
1070         final OceanusMoney myAmount = pTrans.getAmount();
1071         OceanusUnits myUnits = pTrans.getAccountDeltaUnits();
1072         if (myUnits == null) {
1073             myUnits = pTrans.getPartnerDeltaUnits();
1074         }
1075         if (myUnits != null) {
1076             myUnits = new OceanusUnits(myUnits);
1077             myUnits.negate();
1078         }
1079         final OceanusPrice myPrice = getPriceForDate(mySecurity, pTrans.getDate());
1080 
1081         /* Determine whether we use return capital */
1082         final boolean doReturnCapital = canReturnCapital && myUnits == null;
1083 
1084         /* Handle zero units */
1085         boolean autoCorrectZeroUnits = false;
1086         if (!canReturnCapital && myUnits == null) {
1087             if (!canTradeZeroShares) {
1088                 myUnits = OceanusUnits.getWholeUnits(1);
1089                 autoCorrectZeroUnits = true;
1090             } else {
1091                 myUnits = new OceanusUnits();
1092             }
1093         }
1094 
1095         /* Create a sellShares/returnCapital event */
1096         MoneyWiseQIFPortfolioEvent myPortEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, doReturnCapital
1097                 ? canXferLinked
1098                   ? MoneyWiseQActionType.RTRNCAPX
1099                   : MoneyWiseQActionType.RTRNCAP
1100                 : canXferLinked
1101                   ? MoneyWiseQActionType.SELLX
1102                   : MoneyWiseQActionType.SELL);
1103         myPortEvent.recordAmount(myAmount);
1104         myPortEvent.recordSecurity(myQSecurity);
1105         if (!doReturnCapital) {
1106             myPortEvent.recordQuantity(myUnits);
1107         }
1108         myPortEvent.recordPrice(myPrice);
1109         if (canXferLinked) {
1110             myPortEvent.recordXfer(myTarget.getAccount(), myList, myAmount);
1111         }
1112 
1113         /* Add to event list */
1114         myQPortfolio.addEvent(myPortEvent);
1115 
1116         /* If we need to autoCorrect */
1117         if (autoCorrectZeroUnits) {
1118             /* Create a ShrsIn event to balance */
1119             myPortEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.SHRSIN);
1120             myPortEvent.recordSecurity(myQSecurity);
1121             myPortEvent.recordQuantity(myUnits);
1122 
1123             /* Add to event list */
1124             myQPortfolio.addEvent(myPortEvent);
1125         }
1126 
1127         /* If we are not hiding the balancing transfer */
1128         if (!hideBalancingSplitXfer) {
1129             /* Build the source transfer */
1130             final MoneyWiseQIFEvent myEvent = new MoneyWiseQIFEvent(theRegister, pTrans);
1131             myEvent.recordAccount(myQPortfolio.getAccount(), myList);
1132             myEvent.recordAmount(myAmount);
1133 
1134             /* Build payee description */
1135             myEvent.recordPayee(theHelper.buildXferFromPayee(myPortfolio));
1136 
1137             /* Add event to event list */
1138             myTarget.addEvent(myEvent);
1139         }
1140     }
1141 
1142     /**
1143      * Process exchange between securities.
1144      *
1145      * @param pSource the source security holding
1146      * @param pTarget the target security holding
1147      * @param pTrans  the transaction
1148      */
1149     private void processSecurityExchange(final MoneyWiseSecurityHolding pSource,
1150                                          final MoneyWiseSecurityHolding pTarget,
1151                                          final MoneyWiseTransaction pTrans) {
1152         /* Access Portfolio Account */
1153         final MoneyWisePortfolio myPortfolio = pSource.getPortfolio();
1154         final MoneyWiseSecurity mySource = pSource.getSecurity();
1155         final MoneyWiseSecurity myTarget = pTarget.getSecurity();
1156         final MoneyWiseQIFAccountEvents myQPortfolio = theRegister.registerAccount(myPortfolio);
1157 
1158         /* Access Transaction details */
1159         final MoneyWiseQIFSecurity myQSource = theRegister.registerSecurity(mySource);
1160         final MoneyWiseQIFSecurity myQTarget = theRegister.registerSecurity(myTarget);
1161 
1162         /* Access details */
1163         final OceanusDate myDate = pTrans.getDate();
1164         final OceanusMoney myAmount = pTrans.getAmount();
1165         OceanusUnits mySourceUnits = pTrans.getAccountDeltaUnits();
1166         mySourceUnits = new OceanusUnits(mySourceUnits);
1167         mySourceUnits.negate();
1168         final OceanusUnits myTargetUnits = pTrans.getPartnerDeltaUnits();
1169         final OceanusPrice mySourcePrice = getPriceForDate(mySource, myDate);
1170         final OceanusPrice myTargetPrice = getPriceForDate(myTarget, myDate);
1171 
1172         /* Create a sellShares/returnCapital event */
1173         MoneyWiseQIFPortfolioEvent myPortEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.SELL);
1174         myPortEvent.recordAmount(myAmount);
1175         myPortEvent.recordSecurity(myQSource);
1176         myPortEvent.recordQuantity(mySourceUnits);
1177         myPortEvent.recordPrice(mySourcePrice);
1178 
1179         /* Add to event list */
1180         myQPortfolio.addEvent(myPortEvent);
1181 
1182         /* Create a buyShares event */
1183         myPortEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.BUY);
1184         myPortEvent.recordAmount(myAmount);
1185         myPortEvent.recordSecurity(myQTarget);
1186         myPortEvent.recordQuantity(myTargetUnits);
1187         myPortEvent.recordPrice(myTargetPrice);
1188 
1189         /* Add to event list */
1190         myQPortfolio.addEvent(myPortEvent);
1191     }
1192 
1193     /**
1194      * Process transfer between portfolios.
1195      *
1196      * @param pSource the source portfolio
1197      * @param pTarget the target portfolio
1198      * @param pTrans  the transaction
1199      */
1200     protected void processTransferBetweenPortfolios(final MoneyWisePortfolio pSource,
1201                                                     final MoneyWisePortfolio pTarget,
1202                                                     final MoneyWiseTransaction pTrans) {
1203         /* Switch on transaction type */
1204         switch (pTrans.getCategoryClass()) {
1205             case INTEREST, LOYALTYBONUS:
1206                 processIncomeToPortfolio(pSource.getParent(), pTarget, pTrans);
1207                 break;
1208             case PORTFOLIOXFER:
1209                 processPortfolioXferBetweenPortfolios(pSource, pTarget, pTrans);
1210                 break;
1211             case TRANSFER:
1212                 processCashTransferBetweenPortfolios(pSource, pTarget, pTrans);
1213                 break;
1214             default:
1215                 LOGGER.error("Unsupported TransferBetweenPortfolios Category: <%s>", pTrans.getCategoryClass());
1216                 break;
1217         }
1218     }
1219 
1220     /**
1221      * Process PortfolioXfer between portfolios.
1222      *
1223      * @param pSource the source portfolio
1224      * @param pTarget the target portfolio
1225      * @param pTrans  the transaction
1226      */
1227     protected void processPortfolioXferBetweenPortfolios(final MoneyWisePortfolio pSource,
1228                                                          final MoneyWisePortfolio pTarget,
1229                                                          final MoneyWiseTransaction pTrans) {
1230         /* If there is cash to transfer */
1231         final OceanusMoney myAmount = getPortfolioCashValue(pSource, pTrans);
1232         if (myAmount != null) {
1233             /* Access details */
1234             final MoneyWiseQIFAccountEvents mySource = theRegister.registerAccount(pSource);
1235             final MoneyWiseQIFAccountEvents myTarget = theRegister.registerAccount(pTarget);
1236 
1237             /* Obtain classes */
1238             final List<MoneyWiseQIFClass> myList = theHelper.getTransactionClasses(pTrans);
1239 
1240             /* Create an XOut event */
1241             MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.XOUT);
1242             myEvent.recordAmount(myAmount);
1243             myEvent.recordPayee(theHelper.buildXferToPayee(pTarget));
1244             myEvent.recordXfer(myTarget.getAccount(), myList, myAmount);
1245 
1246             /* Add to event list */
1247             mySource.addEvent(myEvent);
1248 
1249             /* Create an XIn event */
1250             myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.XIN);
1251             myEvent.recordAmount(myAmount);
1252             myEvent.recordPayee(theHelper.buildXferFromPayee(pSource));
1253             myEvent.recordXfer(mySource.getAccount(), myList, myAmount);
1254 
1255             /* Add to event list */
1256             myTarget.addEvent(myEvent);
1257         }
1258 
1259         /* Access the relevant bucket */
1260         final MoneyWiseAnalysisPortfolioBucketList myPortfolios = theAnalysis.getPortfolios();
1261         final MoneyWiseAnalysisPortfolioBucket myBucket = myPortfolios.getBucket(pSource);
1262 
1263         /* Loop through the securities */
1264         final Iterator<MoneyWiseAnalysisSecurityBucket> myIterator = myBucket.securityIterator();
1265         while (myIterator.hasNext()) {
1266             final MoneyWiseAnalysisSecurityBucket mySecurity = myIterator.next();
1267 
1268             /* Process transfer for this bucket */
1269             processPortfolioXferForHolding(mySecurity.getSecurityHolding(), pTarget, pTrans);
1270         }
1271     }
1272 
1273     /**
1274      * Process PortfolioXfer for Holding.
1275      *
1276      * @param pSource the source holding
1277      * @param pTarget the target portfolio
1278      * @param pTrans  the transaction
1279      */
1280     protected void processPortfolioXferForHolding(final MoneyWiseSecurityHolding pSource,
1281                                                   final MoneyWisePortfolio pTarget,
1282                                                   final MoneyWiseTransaction pTrans) {
1283         /* Determine if this holding was transferred */
1284         final OceanusUnits myUnits = getBaseUnitsForHolding(pSource, pTrans);
1285         if (myUnits.isNonZero()) {
1286             /* Access details */
1287             final MoneyWisePortfolio mySourcePortfolio = pSource.getPortfolio();
1288             final MoneyWiseSecurity mySecurity = pSource.getSecurity();
1289             final MoneyWiseQIFAccountEvents mySource = theRegister.registerAccount(mySourcePortfolio);
1290             final MoneyWiseQIFAccountEvents myTarget = theRegister.registerAccount(pTarget);
1291             final MoneyWiseQIFSecurity myQSecurity = theRegister.registerSecurity(mySecurity);
1292             OceanusMoney myCost = getDeltaCostForHolding(pSource, pTrans);
1293 
1294             /* If there is an associated cost */
1295             if (myCost != null) {
1296                 /* Convert cost to positive */
1297                 myCost = new OceanusMoney(myCost);
1298                 myCost.negate();
1299 
1300                 /* Obtain price for the date */
1301                 final OceanusPrice myPrice = getPriceForDate(mySecurity, pTrans.getDate());
1302 
1303                 /* Obtain classes */
1304                 final List<MoneyWiseQIFClass> myList = theHelper.getTransactionClasses(pTrans);
1305 
1306                 /* Create a sell shares event */
1307                 MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.SELL);
1308                 myEvent.recordAmount(myCost);
1309                 myEvent.recordSecurity(myQSecurity);
1310                 myEvent.recordQuantity(myUnits);
1311                 myEvent.recordPrice(myPrice);
1312 
1313                 /* Add to event list */
1314                 mySource.addEvent(myEvent);
1315 
1316                 /* Create an XOut event */
1317                 myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.XOUT);
1318                 myEvent.recordAmount(myCost);
1319                 myEvent.recordPayee(theHelper.buildXferToPayee(pTarget));
1320                 myEvent.recordXfer(myTarget.getAccount(), myList, myCost);
1321 
1322                 /* Add to event list */
1323                 mySource.addEvent(myEvent);
1324 
1325                 /* Create an XIn event */
1326                 myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.XIN);
1327                 myEvent.recordAmount(myCost);
1328                 myEvent.recordPayee(theHelper.buildXferFromPayee(pSource));
1329                 myEvent.recordXfer(mySource.getAccount(), myList, myCost);
1330 
1331                 /* Add to event list */
1332                 myTarget.addEvent(myEvent);
1333 
1334                 /* Create a buy shares event */
1335                 myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.BUY);
1336                 myEvent.recordAmount(myCost);
1337                 myEvent.recordSecurity(myQSecurity);
1338                 myEvent.recordQuantity(myUnits);
1339                 myEvent.recordPrice(myPrice);
1340 
1341                 /* Add to event list */
1342                 myTarget.addEvent(myEvent);
1343 
1344                 /* else just simple transfer of shares */
1345             } else {
1346                 /* Create an SharesOut event */
1347                 MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.SHRSOUT);
1348                 myEvent.recordSecurity(myQSecurity);
1349                 myEvent.recordQuantity(myUnits);
1350 
1351                 /* Add to event list */
1352                 mySource.addEvent(myEvent);
1353 
1354                 /* Create an SharesIn event */
1355                 myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.SHRSIN);
1356                 myEvent.recordSecurity(myQSecurity);
1357                 myEvent.recordQuantity(myUnits);
1358 
1359                 /* Add to event list */
1360                 myTarget.addEvent(myEvent);
1361             }
1362         }
1363     }
1364 
1365     /**
1366      * Process Cash Transfer between portfolios.
1367      *
1368      * @param pSource the source portfolio
1369      * @param pTarget the target portfolio
1370      * @param pTrans  the transaction
1371      */
1372     protected void processCashTransferBetweenPortfolios(final MoneyWisePortfolio pSource,
1373                                                         final MoneyWisePortfolio pTarget,
1374                                                         final MoneyWiseTransaction pTrans) {
1375         /* Access details */
1376         final MoneyWiseQIFAccountEvents mySource = theRegister.registerAccount(pSource);
1377         final MoneyWiseQIFAccountEvents myTarget = theRegister.registerAccount(pTarget);
1378         final OceanusMoney myAmount = pTrans.getAmount();
1379 
1380         /* Obtain classes */
1381         final List<MoneyWiseQIFClass> myList = theHelper.getTransactionClasses(pTrans);
1382 
1383         /* Create an XOut event */
1384         MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.XOUT);
1385         myEvent.recordAmount(myAmount);
1386         myEvent.recordPayee(theHelper.buildXferToPayee(pTarget));
1387         myEvent.recordXfer(myTarget.getAccount(), myList, myAmount);
1388 
1389         /* Add to event list */
1390         mySource.addEvent(myEvent);
1391 
1392         /* Create an XIn event */
1393         myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.XIN);
1394         myEvent.recordAmount(myAmount);
1395         myEvent.recordPayee(theHelper.buildXferFromPayee(pSource));
1396         myEvent.recordXfer(mySource.getAccount(), myList, myAmount);
1397 
1398         /* Add to event list */
1399         myTarget.addEvent(myEvent);
1400     }
1401 
1402     /**
1403      * Process transfer to a portfolio.
1404      *
1405      * @param pPortfolio the portfolio
1406      * @param pDebit     the source account
1407      * @param pTrans     the transaction
1408      */
1409     protected void processTransferToPortfolio(final MoneyWisePortfolio pPortfolio,
1410                                               final MoneyWiseTransAsset pDebit,
1411                                               final MoneyWiseTransaction pTrans) {
1412         /* Switch on transaction type */
1413         switch (pTrans.getCategoryClass()) {
1414             case TRANSFER:
1415                 processCashTransferToPortfolio(pPortfolio, pDebit, pTrans);
1416                 break;
1417             default:
1418                 LOGGER.error("Unsupported TransferToPortfolio Category: <%s>", pTrans.getCategoryClass());
1419                 break;
1420         }
1421     }
1422 
1423     /**
1424      * Process Cash Transfer to portfolio.
1425      *
1426      * @param pPortfolio the target portfolio
1427      * @param pSource    the source account
1428      * @param pTrans     the transaction
1429      */
1430     protected void processCashTransferToPortfolio(final MoneyWisePortfolio pPortfolio,
1431                                                   final MoneyWiseTransAsset pSource,
1432                                                   final MoneyWiseTransaction pTrans) {
1433         /* Access details */
1434         final MoneyWiseQIFAccountEvents myPortfolio = theRegister.registerAccount(pPortfolio);
1435         final MoneyWiseQIFAccountEvents mySource = theRegister.registerAccount(pSource);
1436         OceanusMoney myAmount = pTrans.getAmount();
1437 
1438         /* Obtain classes */
1439         final List<MoneyWiseQIFClass> myList = theHelper.getTransactionClasses(pTrans);
1440 
1441         /* Create an XIn event */
1442         final MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.XIN);
1443         myEvent.recordAmount(myAmount);
1444         myEvent.recordPayee(theHelper.buildXferToPayee(pSource));
1445         myEvent.recordXfer(mySource.getAccount(), myList, myAmount);
1446 
1447         /* Add to event list */
1448         myPortfolio.addEvent(myEvent);
1449 
1450         /* Create the sending transfer event */
1451         final MoneyWiseQIFEvent myXferEvent = new MoneyWiseQIFEvent(theRegister, pTrans);
1452         myAmount = new OceanusMoney(myAmount);
1453         myAmount.negate();
1454         myXferEvent.recordAmount(myAmount);
1455         myXferEvent.recordPayee(theHelper.buildXferFromPayee(pPortfolio));
1456         myXferEvent.recordAccount(myPortfolio.getAccount(), myList);
1457 
1458         /* Add to event list */
1459         mySource.addEvent(myXferEvent);
1460     }
1461 
1462     /**
1463      * Process transfer from a portfolio.
1464      *
1465      * @param pPortfolio the portfolio
1466      * @param pCredit    the target account
1467      * @param pTrans     the transaction
1468      */
1469     protected void processTransferFromPortfolio(final MoneyWisePortfolio pPortfolio,
1470                                                 final MoneyWiseTransAsset pCredit,
1471                                                 final MoneyWiseTransaction pTrans) {
1472         /* Switch on transaction type */
1473         switch (pTrans.getCategoryClass()) {
1474             case TRANSFER:
1475                 processCashTransferFromPortfolio(pPortfolio, pCredit, pTrans);
1476                 break;
1477             default:
1478                 LOGGER.error("Unsupported TransferFromPortfolio Category: <%s>", pTrans.getCategoryClass());
1479                 break;
1480         }
1481     }
1482 
1483     /**
1484      * Process Cash Transfer from portfolio.
1485      *
1486      * @param pPortfolio the source portfolio
1487      * @param pTarget    the target account
1488      * @param pTrans     the transaction
1489      */
1490     protected void processCashTransferFromPortfolio(final MoneyWisePortfolio pPortfolio,
1491                                                     final MoneyWiseTransAsset pTarget,
1492                                                     final MoneyWiseTransaction pTrans) {
1493         /* Access details */
1494         final MoneyWiseQIFAccountEvents myPortfolio = theRegister.registerAccount(pPortfolio);
1495         final MoneyWiseQIFAccountEvents myTarget = theRegister.registerAccount(pTarget);
1496         final OceanusMoney myAmount = pTrans.getAmount();
1497 
1498         /* Obtain classes */
1499         final List<MoneyWiseQIFClass> myList = theHelper.getTransactionClasses(pTrans);
1500 
1501         /* Create an XOut event */
1502         final MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.XOUT);
1503         myEvent.recordAmount(myAmount);
1504         myEvent.recordPayee(theHelper.buildXferToPayee(pTarget));
1505         myEvent.recordXfer(myTarget.getAccount(), myList, myAmount);
1506 
1507         /* Add to event list */
1508         myPortfolio.addEvent(myEvent);
1509 
1510         /* Create the receiving transfer event */
1511         final MoneyWiseQIFEvent myXferEvent = new MoneyWiseQIFEvent(theRegister, pTrans);
1512         myXferEvent.recordAmount(myAmount);
1513         myXferEvent.recordPayee(theHelper.buildXferFromPayee(pPortfolio));
1514         myXferEvent.recordAccount(myPortfolio.getAccount(), myList);
1515 
1516         /* Add to event list */
1517         myTarget.addEvent(myXferEvent);
1518     }
1519 
1520     /**
1521      * Process expense from a portfolio.
1522      *
1523      * @param pCredit    the target payee
1524      * @param pPortfolio the portfolio
1525      * @param pTrans     the transaction
1526      */
1527     protected void processExpenseFromPortfolio(final MoneyWisePayee pCredit,
1528                                                final MoneyWisePortfolio pPortfolio,
1529                                                final MoneyWiseTransaction pTrans) {
1530         /* Access Details */
1531         final MoneyWiseQIFAccountEvents myPortfolio = theRegister.registerAccount(pPortfolio);
1532         final MoneyWiseQIFPayee myPayee = theRegister.registerPayee(pCredit);
1533         final MoneyWiseQIFEventCategory myCategory = theRegister.registerCategory(pTrans.getCategory());
1534         final OceanusMoney myAmount = new OceanusMoney(pTrans.getAmount());
1535         myAmount.negate();
1536 
1537         /* Create an expense event */
1538         final MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.CASH);
1539         myEvent.recordAmount(myAmount);
1540         myEvent.recordPayee(myPayee);
1541         myEvent.recordCategory(myCategory);
1542 
1543         /* Add to event list */
1544         myPortfolio.addEvent(myEvent);
1545     }
1546 
1547     /**
1548      * Process income to a portfolio.
1549      *
1550      * @param pDebit     the source payee
1551      * @param pPortfolio the portfolio
1552      * @param pTrans     the transaction
1553      */
1554     protected void processIncomeToPortfolio(final MoneyWisePayee pDebit,
1555                                             final MoneyWisePortfolio pPortfolio,
1556                                             final MoneyWiseTransaction pTrans) {
1557         /* Access Details */
1558         final MoneyWiseQIFAccountEvents myPortfolio = theRegister.registerAccount(pPortfolio);
1559         final MoneyWiseQIFPayee myPayee = theRegister.registerPayee(pDebit);
1560         final MoneyWiseQIFEventCategory myCategory = theRegister.registerCategory(pTrans.getCategory());
1561         final OceanusMoney myAmount = pTrans.getAmount();
1562 
1563         /* Create an income event */
1564         final MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theRegister, pTrans, MoneyWiseQActionType.CASH);
1565         myEvent.recordAmount(myAmount);
1566         myEvent.recordPayee(myPayee);
1567         myEvent.recordCategory(myCategory);
1568 
1569         /* Add to event list */
1570         myPortfolio.addEvent(myEvent);
1571     }
1572 }