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.oceanus.date.OceanusDate;
20  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusDecimal;
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.OceanusRatio;
24  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusUnits;
25  import io.github.tonywasher.joceanus.oceanus.logger.OceanusLogManager;
26  import io.github.tonywasher.joceanus.oceanus.logger.OceanusLogger;
27  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDataSet;
28  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDeposit;
29  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePayee;
30  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePortfolio;
31  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity;
32  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityHolding;
33  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityPrice.MoneyWiseSecurityPriceDataMap;
34  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransAsset;
35  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransaction;
36  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransCategoryClass;
37  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysis;
38  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisPortfolioBucket;
39  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisPortfolioBucket.MoneyWiseAnalysisPortfolioBucketList;
40  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisPortfolioCashBucket;
41  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisSecurityBucket;
42  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.values.MoneyWiseAnalysisAccountAttr;
43  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.values.MoneyWiseAnalysisSecurityAttr;
44  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.values.MoneyWiseAnalysisSecurityValues;
45  import io.github.tonywasher.joceanus.moneywise.quicken.definitions.MoneyWiseQActionType;
46  import io.github.tonywasher.joceanus.moneywise.quicken.definitions.MoneyWiseQIFType;
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 File.
62       */
63      private final MoneyWiseQIFFile theFile;
64  
65      /**
66       * The QIF File Type.
67       */
68      private final MoneyWiseQIFType theFileType;
69  
70      /**
71       * The Builder.
72       */
73      private final MoneyWiseQIFBuilder theBuilder;
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 pBuilder  the builder
89       * @param pData     the data
90       * @param pAnalysis the analysis
91       */
92      protected MoneyWiseQIFPortfolioBuilder(final MoneyWiseQIFBuilder pBuilder,
93                                             final MoneyWiseDataSet pData,
94                                             final MoneyWiseAnalysis pAnalysis) {
95          /* Store parameters */
96          theBuilder = pBuilder;
97          theFile = theBuilder.getFile();
98          theFileType = theFile.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 = theFile.registerAccount(myPort);
218 
219         /* Determine style */
220         final boolean useHoldingAccount = theFileType.useInvestmentHolding4Category();
221 
222         /* Access Transaction details */
223         final MoneyWiseQIFPayee myQPayee = theFile.registerPayee(pPayee);
224         final MoneyWiseQIFSecurity myQSecurity = theFile.registerSecurity(mySecurity);
225         final MoneyWiseQIFEventCategory myQCategory = theFile.registerCategory(pTrans.getCategory());
226 
227         /* Obtain classes */
228         final List<MoneyWiseQIFClass> myList = theBuilder.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 = theFile.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(theFile, 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 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(theFile, 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(theFile, 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 = theFile.registerAccount(myPort);
293 
294         /* Determine style */
295         final boolean useHoldingAccount = theFileType.useInvestmentHolding4Category();
296 
297         /* Access Transaction details */
298         final MoneyWiseQIFPayee myQPayee = theFile.registerPayee(pPayee);
299         final MoneyWiseQIFSecurity myQSecurity = theFile.registerSecurity(mySecurity);
300         final MoneyWiseQIFEventCategory myQCategory = theFile.registerCategory(pTrans.getCategory());
301 
302         /* Obtain classes */
303         final List<MoneyWiseQIFClass> myList = theBuilder.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(theFile, 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 = theFile.registerHoldingAccount(myPort);
330 
331             /* Create an event */
332             final MoneyWiseQIFEvent myHoldEvent = new MoneyWiseQIFEvent(theFile, 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(theFile, 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 = theFile.registerAccount(myPort);
379 
380         /* Access Transaction details */
381         final MoneyWiseQIFAccountEvents mySource = theFile.registerAccount(pDebit);
382         final MoneyWiseQIFSecurity myQSecurity = theFile.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 = theBuilder.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(theFile, 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(theFile, 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(theFile, pTrans);
453             myEvent.recordAccount(myPortfolio.getAccount(), myList);
454             myEvent.recordAmount(myOutAmount);
455 
456             /* Build payee description */
457             myEvent.recordPayee(theBuilder.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:
493             case SECURITYREPLACE:
494                 processStockTakeOver(pSource, pTarget, pTrans);
495                 break;
496             case TRANSFER:
497                 processSecurityExchange(pSource, pTarget, pTrans);
498                 break;
499             default:
500                 LOGGER.error("Unsupported TransferBetweenSecurities Category: <%s>", pTrans.getCategoryClass());
501                 break;
502         }
503     }
504 
505     /**
506      * Process transfer from a security.
507      *
508      * @param pHolding the security holding
509      * @param pCredit  the credit account
510      * @param pTrans   the transaction
511      */
512     protected void processTransferFromSecurity(final MoneyWiseSecurityHolding pHolding,
513                                                final MoneyWiseTransAsset pCredit,
514                                                final MoneyWiseTransaction pTrans) {
515         /* Switch on transaction type */
516         switch (pTrans.getCategoryClass()) {
517             case DIVIDEND:
518                 processStockDividend(pHolding, pCredit, pTrans);
519                 break;
520             case PORTFOLIOXFER:
521                 processPortfolioXferForHolding(pHolding, (MoneyWisePortfolio) pCredit, pTrans);
522                 break;
523             case TRANSFER:
524             case STOCKRIGHTSISSUE:
525             default:
526                 processTransferOut(pHolding, pCredit, pTrans);
527                 break;
528         }
529     }
530 
531     /**
532      * Process Stock Split.
533      *
534      * @param pHolding the security holding
535      * @param pTrans   the transaction
536      */
537     private void processStockSplit(final MoneyWiseSecurityHolding pHolding,
538                                    final MoneyWiseTransaction pTrans) {
539         /* Access Portfolio Account */
540         final MoneyWisePortfolio myPortfolio = pHolding.getPortfolio();
541         final MoneyWiseSecurity mySecurity = pHolding.getSecurity();
542         final MoneyWiseQIFAccountEvents myQPortfolio = theFile.registerAccount(myPortfolio);
543 
544         /* Access Transaction details */
545         final MoneyWiseQIFSecurity myQSecurity = theFile.registerSecurity(mySecurity);
546 
547         /* Obtain number of units after this event */
548         final OceanusUnits myTotalUnits = getUnitsForHoldingEvent(pHolding, pTrans);
549 
550         /* Access the delta units */
551         final OceanusUnits myDeltaUnits = pTrans.getAccountDeltaUnits();
552 
553         /* Obtain number of units before event */
554         final OceanusUnits myBaseUnits = new OceanusUnits(myTotalUnits);
555         myBaseUnits.subtractUnits(myDeltaUnits);
556 
557         /* Obtain split ratio */
558         final OceanusRatio mySplit = new OceanusRatio(myTotalUnits, myBaseUnits);
559         mySplit.multiply(OceanusDecimal.RADIX_TEN);
560 
561         /* Create a stock split event */
562         final MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.STKSPLIT);
563         myEvent.recordSecurity(myQSecurity);
564         myEvent.recordQuantity(mySplit);
565 
566         /* Add to event list */
567         myQPortfolio.addEvent(myEvent);
568     }
569 
570     /**
571      * Process stock adjustment.
572      *
573      * @param pHolding the security holding
574      * @param pTrans   the transaction
575      */
576     private void processSecurityAdjust(final MoneyWiseSecurityHolding pHolding,
577                                        final MoneyWiseTransaction pTrans) {
578         /* Access Portfolio Account */
579         final MoneyWisePortfolio myPortfolio = pHolding.getPortfolio();
580         final MoneyWiseSecurity mySecurity = pHolding.getSecurity();
581         final MoneyWiseQIFAccountEvents myQPortfolio = theFile.registerAccount(myPortfolio);
582 
583         /* Access Transaction details */
584         final MoneyWiseQIFSecurity myQSecurity = theFile.registerSecurity(mySecurity);
585 
586         /* Access the delta units */
587         OceanusUnits myUnits = pTrans.getAccountDeltaUnits();
588         final boolean isCredit = myUnits.isPositive();
589         if (!isCredit) {
590             myUnits = new OceanusUnits(myUnits);
591             myUnits.negate();
592         }
593 
594         /* Create a share movement event */
595         final MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, isCredit
596                 ? MoneyWiseQActionType.SHRSIN
597                 : MoneyWiseQActionType.SHRSOUT);
598         myEvent.recordSecurity(myQSecurity);
599         myEvent.recordQuantity(myUnits);
600 
601         /* Add to event list */
602         myQPortfolio.addEvent(myEvent);
603     }
604 
605     /**
606      * Process stock dividend.
607      *
608      * @param pHolding the security holding
609      * @param pCredit  the credit account
610      * @param pTrans   the transaction
611      */
612     private void processStockDividend(final MoneyWiseSecurityHolding pHolding,
613                                       final MoneyWiseTransAsset pCredit,
614                                       final MoneyWiseTransaction pTrans) {
615         /* Access Portfolio Account */
616         final MoneyWisePortfolio myPortfolio = pHolding.getPortfolio();
617         final MoneyWiseSecurity mySecurity = pHolding.getSecurity();
618         final MoneyWiseQIFAccountEvents myQPortfolio = theFile.registerAccount(myPortfolio);
619 
620         /* Obtain flags */
621         boolean canXferLinked = theFileType.canXferPortfolio();
622         final boolean isPortfolio = pCredit.equals(myPortfolio);
623 
624         /* Access Transaction details */
625         final MoneyWiseQIFSecurity myQSecurity = theFile.registerSecurity(mySecurity);
626         final MoneyWiseQIFAccountEvents myTarget = theFile.registerAccount(pCredit);
627         OceanusMoney myAmount = pTrans.getAmount();
628         final OceanusMoney myTaxCredit = pTrans.getTaxCredit();
629         final OceanusMoney myFullAmount = new OceanusMoney(myAmount);
630         if (myTaxCredit != null) {
631             myFullAmount.addAmount(myTaxCredit);
632         }
633 
634         /* Obtain classes */
635         final List<MoneyWiseQIFClass> myList = theBuilder.getTransactionClasses(pTrans);
636 
637         /* Determine whether we should XferLinked */
638         boolean doXferLinked = canXferLinked && myTaxCredit == null;
639 
640         /* Check for dividend held in portfolio */
641         if (isPortfolio) {
642             /* Make sure we don't try to link account */
643             doXferLinked = false;
644             canXferLinked = false;
645         }
646 
647         /* Create a dividend event */
648         MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, doXferLinked
649                 ? MoneyWiseQActionType.DIVX
650                 : MoneyWiseQActionType.DIV);
651         myEvent.recordSecurity(myQSecurity);
652         myEvent.recordAmount(myFullAmount);
653         if (doXferLinked) {
654             myEvent.recordPayee(theBuilder.buildXferFromPayee(myPortfolio));
655             myEvent.recordXfer(myTarget.getAccount(), myList, myAmount);
656         }
657 
658         /* Add to event list */
659         myQPortfolio.addEvent(myEvent);
660 
661         /* If we can use XOut records */
662         if (!doXferLinked && canXferLinked) {
663             /* Create a transfer out event */
664             myAmount = pTrans.getAmount();
665             myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.XOUT);
666             myEvent.recordAmount(myAmount);
667             myEvent.recordPayee(theBuilder.buildXferFromPayee(myPortfolio));
668             myEvent.recordXfer(myTarget.getAccount(), myList, myAmount);
669 
670             /* Add to event list */
671             myQPortfolio.addEvent(myEvent);
672         }
673 
674         /* Don't do if receiving dividend in portfolio */
675         if (!isPortfolio) {
676             /* If the receiving account is a portfolio */
677             if (pCredit instanceof MoneyWisePortfolio) {
678                 /* Create the receiving transfer event */
679                 final MoneyWiseQIFPortfolioEvent myXferEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.XIN);
680                 myXferEvent.recordAmount(myAmount);
681                 myXferEvent.recordPayee(theBuilder.buildXferFromPayee(myPortfolio));
682                 myXferEvent.recordXfer(myQPortfolio.getAccount(), myList, myAmount);
683 
684                 /* Add to event list */
685                 myTarget.addEvent(myXferEvent);
686 
687                 /* else standard account */
688             } else {
689                 /* Create the receiving transfer event */
690                 final MoneyWiseQIFEvent myXferEvent = new MoneyWiseQIFEvent(theFile, pTrans);
691                 myXferEvent.recordAmount(myAmount);
692                 myXferEvent.recordPayee(theBuilder.buildXferFromPayee(myPortfolio));
693                 myXferEvent.recordAccount(myQPortfolio.getAccount(), myList);
694 
695                 /* Add to event list */
696                 myTarget.addEvent(myXferEvent);
697             }
698         }
699 
700         /* If we have a Tax Credit */
701         if (myTaxCredit != null) {
702             /* Determine flags */
703             final boolean useHoldingAccount = theFileType.useInvestmentHolding4Category();
704 
705             /* Access category */
706             final MoneyWiseQIFEventCategory myTaxCategory = theBuilder.getTaxCategory();
707             final MoneyWiseQIFPayee myTaxPayee = theBuilder.getTaxMan();
708 
709             /* Create output amount */
710             final OceanusMoney myOutAmount = new OceanusMoney(myTaxCredit);
711             myOutAmount.negate();
712 
713             /* If we are using a holding account */
714             if (useHoldingAccount) {
715                 /* Access Holding Account */
716                 final MoneyWiseQIFAccountEvents myHolding = theFile.registerHoldingAccount(myPortfolio);
717 
718                 /* Create an event */
719                 final MoneyWiseQIFEvent myHoldEvent = new MoneyWiseQIFEvent(theFile, pTrans);
720                 myHoldEvent.recordAmount(new OceanusMoney());
721                 myHoldEvent.recordPayee(myTaxPayee);
722 
723                 /* record the splits */
724                 myHoldEvent.recordSplitRecord(myQPortfolio.getAccount(), myTaxCredit, myPortfolio.getName());
725                 myHoldEvent.recordSplitRecord(myTaxCategory, myOutAmount, myTaxPayee.getName());
726 
727                 /* Add to event list */
728                 myHolding.addEvent(myHoldEvent);
729 
730                 /* else we can do this properly */
731             } else {
732                 /* Create a tax credit event */
733                 myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.CASH);
734                 myEvent.recordAmount(myOutAmount);
735                 myEvent.recordPayee(myTaxPayee);
736                 myEvent.recordCategory(myTaxCategory);
737 
738                 /* Add to event list */
739                 myQPortfolio.addEvent(myEvent);
740             }
741         }
742     }
743 
744     /**
745      * Process reinvested dividend.
746      *
747      * @param pHolding the security holding
748      * @param pTrans   the transaction
749      */
750     private void processReinvestDividend(final MoneyWiseSecurityHolding pHolding,
751                                          final MoneyWiseTransaction pTrans) {
752         /* Access Portfolio Account */
753         final MoneyWisePortfolio myPortfolio = pHolding.getPortfolio();
754         final MoneyWiseSecurity mySecurity = pHolding.getSecurity();
755         final MoneyWiseQIFAccountEvents myQPortfolio = theFile.registerAccount(myPortfolio);
756 
757         /* Determine various flags */
758         final boolean canTradeZeroShares = theFileType.canTradeZeroShares();
759 
760         /* Access Transaction details */
761         final MoneyWiseQIFSecurity myQSecurity = theFile.registerSecurity(mySecurity);
762         OceanusMoney myAmount = pTrans.getAmount();
763         OceanusUnits myUnits = pTrans.getAccountDeltaUnits();
764         final OceanusMoney myTaxCredit = pTrans.getTaxCredit();
765         myAmount = new OceanusMoney(myAmount);
766         if (myTaxCredit != null) {
767             myAmount.addAmount(myTaxCredit);
768         }
769 
770         /* Handle zero units */
771         boolean autoCorrectZeroUnits = false;
772         if (myUnits == null) {
773             if (!canTradeZeroShares) {
774                 myUnits = OceanusUnits.getWholeUnits(1);
775                 autoCorrectZeroUnits = true;
776             } else {
777                 myUnits = new OceanusUnits();
778             }
779         }
780 
781         /* Create a re-invest dividend event */
782         MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.REINVDIV);
783         myEvent.recordSecurity(myQSecurity);
784         myEvent.recordAmount(myAmount);
785         myEvent.recordQuantity(myUnits);
786 
787         /* Add to event list */
788         myQPortfolio.addEvent(myEvent);
789 
790         /* If we need to autoCorrect */
791         if (autoCorrectZeroUnits) {
792             /* Create a ShrsOut event to balance */
793             myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.SHRSOUT);
794             myEvent.recordSecurity(myQSecurity);
795             myEvent.recordQuantity(myUnits);
796 
797             /* Add to event list */
798             myQPortfolio.addEvent(myEvent);
799         }
800 
801         /* If we have a Tax Credit */
802         if (myTaxCredit != null) {
803             /* Determine flags */
804             final boolean useHoldingAccount = theFileType.useInvestmentHolding4Category();
805             final boolean useMiscIncX = theFileType.useMiscIncX4TaxCredit();
806 
807             /* Access category */
808             final MoneyWiseQIFEventCategory myTaxCategory = theBuilder.getTaxCategory();
809             final MoneyWiseQIFPayee myTaxPayee = theBuilder.getTaxMan();
810 
811             /* Create a tax credit event */
812             myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, useMiscIncX
813                     ? MoneyWiseQActionType.MISCINCX
814                     : MoneyWiseQActionType.MISCINC);
815             myEvent.recordSecurity(myQSecurity);
816             myEvent.recordAmount(myTaxCredit);
817             if (useMiscIncX) {
818                 myEvent.recordPayee(myTaxPayee);
819                 myEvent.recordCategory(myTaxCategory);
820             }
821 
822             /* Add to event list */
823             myQPortfolio.addEvent(myEvent);
824 
825             /* If we need further elements */
826             if (!useMiscIncX) {
827                 /* Create output amount */
828                 final OceanusMoney myOutAmount = new OceanusMoney(myTaxCredit);
829                 myOutAmount.negate();
830 
831                 /* If we are using a holding account */
832                 if (useHoldingAccount) {
833                     /* Access Holding Account */
834                     final MoneyWiseQIFAccountEvents myHolding = theFile.registerHoldingAccount(myPortfolio);
835 
836                     /* Create an event */
837                     final MoneyWiseQIFEvent myHoldEvent = new MoneyWiseQIFEvent(theFile, pTrans);
838                     myHoldEvent.recordAmount(new OceanusMoney());
839                     myHoldEvent.recordPayee(myTaxPayee);
840 
841                     /* record the splits */
842                     myHoldEvent.recordSplitRecord(myQPortfolio.getAccount(), myTaxCredit, myPortfolio.getName());
843                     myHoldEvent.recordSplitRecord(myTaxCategory, myOutAmount, myTaxPayee.getName());
844 
845                     /* Add to event list */
846                     myHolding.addEvent(myHoldEvent);
847 
848                     /* else we can do this properly */
849                 } else {
850                     /* Create a tax credit event */
851                     myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.CASH);
852                     myEvent.recordAmount(myOutAmount);
853                     myEvent.recordPayee(myTaxPayee);
854                     myEvent.recordCategory(myTaxCategory);
855 
856                     /* Add to event list */
857                     myQPortfolio.addEvent(myEvent);
858                 }
859             }
860         }
861     }
862 
863     /**
864      * Process stock deMerger.
865      *
866      * @param pHolding the security holding
867      * @param pCredit  the credit account
868      * @param pTrans   the transaction
869      */
870     private void processStockDeMerger(final MoneyWiseSecurityHolding pHolding,
871                                       final MoneyWiseSecurityHolding pCredit,
872                                       final MoneyWiseTransaction pTrans) {
873         /* Access Portfolio Account */
874         final MoneyWisePortfolio myPortfolio = pHolding.getPortfolio();
875         final MoneyWiseSecurity mySecurity = pHolding.getSecurity();
876         final MoneyWiseSecurity myCredit = pCredit.getSecurity();
877         final MoneyWiseQIFAccountEvents myQPortfolio = theFile.registerAccount(myPortfolio);
878 
879         /* Determine whether we can return capital */
880         final boolean canReturnCapital = theFileType.canReturnCapital();
881         final boolean canTradeZeroShares = theFileType.canTradeZeroShares();
882 
883         /* Access Transaction details */
884         final MoneyWiseQIFSecurity myDebitSecurity = theFile.registerSecurity(mySecurity);
885         final MoneyWiseQIFSecurity myCreditSecurity = theFile.registerSecurity(myCredit);
886 
887         /* Access details */
888         final OceanusDate myDate = pTrans.getDate();
889         OceanusUnits myUnits = pTrans.getAccountDeltaUnits();
890         if (myUnits != null) {
891             myUnits = new OceanusUnits(myUnits);
892             myUnits.negate();
893         }
894         final OceanusPrice myDebitPrice = getPriceForDate(mySecurity, myDate);
895         final OceanusPrice myCreditPrice = getPriceForDate(myCredit, myDate);
896 
897         /* Obtain the delta cost (i.e. value transferred) */
898         OceanusMoney myValue = getDeltaCostForHolding(pHolding, pTrans);
899         myValue = new OceanusMoney(myValue);
900         myValue.negate();
901 
902         /* Determine whether we use return capital */
903         final boolean doReturnCapital = canReturnCapital && myUnits == null;
904 
905         /* Handle zero units */
906         boolean autoCorrectZeroUnits = false;
907         if (!canReturnCapital && myUnits == null) {
908             if (!canTradeZeroShares) {
909                 myUnits = OceanusUnits.getWholeUnits(1);
910                 autoCorrectZeroUnits = true;
911             } else {
912                 myUnits = new OceanusUnits();
913             }
914         }
915 
916         /* Create a sellShares/returnCapital event for the share reduction */
917         MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, doReturnCapital
918                 ? MoneyWiseQActionType.RTRNCAP
919                 : MoneyWiseQActionType.SELL);
920         myEvent.recordAmount(myValue);
921         myEvent.recordSecurity(myDebitSecurity);
922         myEvent.recordPrice(myDebitPrice);
923         if (!doReturnCapital) {
924             myEvent.recordQuantity(myUnits);
925         }
926 
927         /* Add to event list */
928         myQPortfolio.addEvent(myEvent);
929 
930         /* If we need to autoCorrect */
931         if (autoCorrectZeroUnits) {
932             /* Create a ShrsIn event to balance */
933             myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.SHRSIN);
934             myEvent.recordSecurity(myDebitSecurity);
935             myEvent.recordQuantity(myUnits);
936 
937             /* Add to event list */
938             myQPortfolio.addEvent(myEvent);
939         }
940 
941         /* Create a buy shares event for the new shares */
942         myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.BUY);
943         myEvent.recordAmount(myValue);
944         myEvent.recordSecurity(myCreditSecurity);
945         myEvent.recordQuantity(pTrans.getPartnerDeltaUnits());
946         myEvent.recordPrice(myCreditPrice);
947 
948         /* Add to event list */
949         myQPortfolio.addEvent(myEvent);
950     }
951 
952     /**
953      * Process security Exchange/TakeOver.
954      *
955      * @param pSource the source security
956      * @param pTarget the target security
957      * @param pTrans  the transaction
958      */
959     private void processStockTakeOver(final MoneyWiseSecurityHolding pSource,
960                                       final MoneyWiseSecurityHolding pTarget,
961                                       final MoneyWiseTransaction pTrans) {
962         /* Access Portfolio Account */
963         final MoneyWisePortfolio myPortfolio = pSource.getPortfolio();
964         final MoneyWiseSecurity mySource = pSource.getSecurity();
965         final MoneyWiseSecurity myTarget = pTarget.getSecurity();
966         final MoneyWiseQIFAccountEvents myQPortfolio = theFile.registerAccount(myPortfolio);
967 
968         /* Access Transaction details */
969         final MoneyWiseQIFSecurity myDebitSecurity = theFile.registerSecurity(mySource);
970         final MoneyWiseQIFSecurity myCreditSecurity = theFile.registerSecurity(myTarget);
971 
972         /* Access details */
973         final OceanusDate myDate = pTrans.getDate();
974         final OceanusUnits myUnits = pTrans.getPartnerDeltaUnits();
975         final OceanusPrice myDebitPrice = getPriceForDate(mySource, myDate);
976         final OceanusPrice myCreditPrice = getPriceForDate(myTarget, myDate);
977         final MoneyWiseDeposit myThirdParty = (MoneyWiseDeposit) pTrans.getReturnedCashAccount();
978         final OceanusMoney myAmount = pTrans.getReturnedCash();
979 
980         /* Obtain the number of units that we are selling */
981         final OceanusUnits myBaseUnits = getBaseUnitsForHolding(pSource, pTrans);
982 
983         /* Obtain the delta cost (i.e. value transferred) */
984         final OceanusMoney myStockCost = getDeltaCostForHolding(pSource, pTrans);
985 
986         /* Determine the total sale value */
987         final OceanusMoney mySaleValue = new OceanusMoney(myStockCost);
988         mySaleValue.addAmount(myAmount);
989 
990         /* Create a sellShares event for the share reduction */
991         MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.SELL);
992         myEvent.recordAmount(mySaleValue);
993         myEvent.recordSecurity(myDebitSecurity);
994         myEvent.recordPrice(myDebitPrice);
995         myEvent.recordQuantity(myBaseUnits);
996 
997         /* Add to event list */
998         myQPortfolio.addEvent(myEvent);
999 
1000         /* Create a buy shares event for the new shares */
1001         myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.BUY);
1002         myEvent.recordAmount(myStockCost);
1003         myEvent.recordSecurity(myCreditSecurity);
1004         myEvent.recordQuantity(myUnits);
1005         myEvent.recordPrice(myCreditPrice);
1006 
1007         /* Add to event list */
1008         myQPortfolio.addEvent(myEvent);
1009 
1010         /* If we have a ThirdParty Account */
1011         if (myThirdParty != null) {
1012             /* determine flags */
1013             final boolean canXferDirect = theFileType.canXferPortfolio();
1014 
1015             /* Access Target account */
1016             final MoneyWiseQIFAccountEvents myQTarget = theFile.registerAccount(myThirdParty);
1017 
1018             /* If we can transfer direct */
1019             if (canXferDirect) {
1020                 /* Create a transfer out event for the cash payment */
1021                 myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.XOUT);
1022                 myEvent.recordAmount(myAmount);
1023                 myEvent.recordXfer(myQTarget.getAccount(), myAmount);
1024 
1025                 /* Add to event list */
1026                 myQPortfolio.addEvent(myEvent);
1027             } else {
1028                 /* Build the target transfer */
1029                 final MoneyWiseQIFEvent myXferEvent = new MoneyWiseQIFEvent(theFile, pTrans);
1030                 myXferEvent.recordAccount(myQPortfolio.getAccount());
1031                 myXferEvent.recordAmount(myAmount);
1032 
1033                 /* Build payee description */
1034                 myEvent.recordPayee(theBuilder.buildXferFromPayee(myPortfolio));
1035 
1036                 /* Add event to event list */
1037                 myQTarget.addEvent(myEvent);
1038             }
1039         }
1040     }
1041 
1042     /**
1043      * Process standard transfer out from a security.
1044      *
1045      * @param pHolding the security holding
1046      * @param pCredit  the credit account
1047      * @param pTrans   the transaction
1048      */
1049     private void processTransferOut(final MoneyWiseSecurityHolding pHolding,
1050                                     final MoneyWiseTransAsset pCredit,
1051                                     final MoneyWiseTransaction pTrans) {
1052         /* Access Portfolio Account */
1053         final MoneyWisePortfolio myPortfolio = pHolding.getPortfolio();
1054         final MoneyWiseSecurity mySecurity = pHolding.getSecurity();
1055         final MoneyWiseQIFAccountEvents myQPortfolio = theFile.registerAccount(myPortfolio);
1056 
1057         /* Access Transaction details */
1058         final MoneyWiseQIFAccountEvents myTarget = theFile.registerAccount(pCredit);
1059         final MoneyWiseQIFSecurity myQSecurity = theFile.registerSecurity(mySecurity);
1060 
1061         /* Determine various flags */
1062         final boolean canReturnCapital = theFileType.canReturnCapital();
1063         final boolean canTradeZeroShares = theFileType.canTradeZeroShares();
1064         boolean canXferLinked = theFileType.canXferPortfolio();
1065         boolean hideBalancingSplitXfer = theFileType.hideBalancingSplitTransfer();
1066         hideBalancingSplitXfer &= canXferLinked;
1067 
1068         /* Check for transfer to portfolio */
1069         if (pCredit.equals(myPortfolio)) {
1070             /* Make sure we don't try to link account */
1071             canXferLinked = false;
1072             hideBalancingSplitXfer = true;
1073         }
1074 
1075         /* Obtain classes */
1076         final List<MoneyWiseQIFClass> myList = theBuilder.getTransactionClasses(pTrans);
1077 
1078         /* Access details */
1079         final OceanusMoney myAmount = pTrans.getAmount();
1080         OceanusUnits myUnits = pTrans.getAccountDeltaUnits();
1081         if (myUnits == null) {
1082             myUnits = pTrans.getPartnerDeltaUnits();
1083         }
1084         if (myUnits != null) {
1085             myUnits = new OceanusUnits(myUnits);
1086             myUnits.negate();
1087         }
1088         final OceanusPrice myPrice = getPriceForDate(mySecurity, pTrans.getDate());
1089 
1090         /* Determine whether we use return capital */
1091         final boolean doReturnCapital = canReturnCapital && myUnits == null;
1092 
1093         /* Handle zero units */
1094         boolean autoCorrectZeroUnits = false;
1095         if (!canReturnCapital && myUnits == null) {
1096             if (!canTradeZeroShares) {
1097                 myUnits = OceanusUnits.getWholeUnits(1);
1098                 autoCorrectZeroUnits = true;
1099             } else {
1100                 myUnits = new OceanusUnits();
1101             }
1102         }
1103 
1104         /* Create a sellShares/returnCapital event */
1105         MoneyWiseQIFPortfolioEvent myPortEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, doReturnCapital
1106                 ? canXferLinked
1107                 ? MoneyWiseQActionType.RTRNCAPX
1108                 : MoneyWiseQActionType.RTRNCAP
1109                 : canXferLinked
1110                 ? MoneyWiseQActionType.SELLX
1111                 : MoneyWiseQActionType.SELL);
1112         myPortEvent.recordAmount(myAmount);
1113         myPortEvent.recordSecurity(myQSecurity);
1114         if (!doReturnCapital) {
1115             myPortEvent.recordQuantity(myUnits);
1116         }
1117         myPortEvent.recordPrice(myPrice);
1118         if (canXferLinked) {
1119             myPortEvent.recordXfer(myTarget.getAccount(), myList, myAmount);
1120         }
1121 
1122         /* Add to event list */
1123         myQPortfolio.addEvent(myPortEvent);
1124 
1125         /* If we need to autoCorrect */
1126         if (autoCorrectZeroUnits) {
1127             /* Create a ShrsIn event to balance */
1128             myPortEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.SHRSIN);
1129             myPortEvent.recordSecurity(myQSecurity);
1130             myPortEvent.recordQuantity(myUnits);
1131 
1132             /* Add to event list */
1133             myQPortfolio.addEvent(myPortEvent);
1134         }
1135 
1136         /* If we are not hiding the balancing transfer */
1137         if (!hideBalancingSplitXfer) {
1138             /* Build the source transfer */
1139             final MoneyWiseQIFEvent myEvent = new MoneyWiseQIFEvent(theFile, pTrans);
1140             myEvent.recordAccount(myQPortfolio.getAccount(), myList);
1141             myEvent.recordAmount(myAmount);
1142 
1143             /* Build payee description */
1144             myEvent.recordPayee(theBuilder.buildXferFromPayee(myPortfolio));
1145 
1146             /* Add event to event list */
1147             myTarget.addEvent(myEvent);
1148         }
1149     }
1150 
1151     /**
1152      * Process exchange between securities.
1153      *
1154      * @param pSource the source security holding
1155      * @param pTarget the target security holding
1156      * @param pTrans  the transaction
1157      */
1158     private void processSecurityExchange(final MoneyWiseSecurityHolding pSource,
1159                                          final MoneyWiseSecurityHolding pTarget,
1160                                          final MoneyWiseTransaction pTrans) {
1161         /* Access Portfolio Account */
1162         final MoneyWisePortfolio myPortfolio = pSource.getPortfolio();
1163         final MoneyWiseSecurity mySource = pSource.getSecurity();
1164         final MoneyWiseSecurity myTarget = pTarget.getSecurity();
1165         final MoneyWiseQIFAccountEvents myQPortfolio = theFile.registerAccount(myPortfolio);
1166 
1167         /* Access Transaction details */
1168         final MoneyWiseQIFSecurity myQSource = theFile.registerSecurity(mySource);
1169         final MoneyWiseQIFSecurity myQTarget = theFile.registerSecurity(myTarget);
1170 
1171         /* Access details */
1172         final OceanusDate myDate = pTrans.getDate();
1173         final OceanusMoney myAmount = pTrans.getAmount();
1174         OceanusUnits mySourceUnits = pTrans.getAccountDeltaUnits();
1175         mySourceUnits = new OceanusUnits(mySourceUnits);
1176         mySourceUnits.negate();
1177         final OceanusUnits myTargetUnits = pTrans.getPartnerDeltaUnits();
1178         final OceanusPrice mySourcePrice = getPriceForDate(mySource, myDate);
1179         final OceanusPrice myTargetPrice = getPriceForDate(myTarget, myDate);
1180 
1181         /* Create a sellShares/returnCapital event */
1182         MoneyWiseQIFPortfolioEvent myPortEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.SELL);
1183         myPortEvent.recordAmount(myAmount);
1184         myPortEvent.recordSecurity(myQSource);
1185         myPortEvent.recordQuantity(mySourceUnits);
1186         myPortEvent.recordPrice(mySourcePrice);
1187 
1188         /* Add to event list */
1189         myQPortfolio.addEvent(myPortEvent);
1190 
1191         /* Create a buyShares event */
1192         myPortEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.BUY);
1193         myPortEvent.recordAmount(myAmount);
1194         myPortEvent.recordSecurity(myQTarget);
1195         myPortEvent.recordQuantity(myTargetUnits);
1196         myPortEvent.recordPrice(myTargetPrice);
1197 
1198         /* Add to event list */
1199         myQPortfolio.addEvent(myPortEvent);
1200     }
1201 
1202     /**
1203      * Process transfer between portfolios.
1204      *
1205      * @param pSource the source portfolio
1206      * @param pTarget the target portfolio
1207      * @param pTrans  the transaction
1208      */
1209     protected void processTransferBetweenPortfolios(final MoneyWisePortfolio pSource,
1210                                                     final MoneyWisePortfolio pTarget,
1211                                                     final MoneyWiseTransaction pTrans) {
1212         /* Switch on transaction type */
1213         switch (pTrans.getCategoryClass()) {
1214             case INTEREST:
1215             case LOYALTYBONUS:
1216                 processIncomeToPortfolio(pSource.getParent(), pTarget, pTrans);
1217                 break;
1218             case PORTFOLIOXFER:
1219                 processPortfolioXferBetweenPortfolios(pSource, pTarget, pTrans);
1220                 break;
1221             case TRANSFER:
1222                 processCashTransferBetweenPortfolios(pSource, pTarget, pTrans);
1223                 break;
1224             default:
1225                 LOGGER.error("Unsupported TransferBetweenPortfolios Category: <%s>", pTrans.getCategoryClass());
1226                 break;
1227         }
1228     }
1229 
1230     /**
1231      * Process PortfolioXfer between portfolios.
1232      *
1233      * @param pSource the source portfolio
1234      * @param pTarget the target portfolio
1235      * @param pTrans  the transaction
1236      */
1237     protected void processPortfolioXferBetweenPortfolios(final MoneyWisePortfolio pSource,
1238                                                          final MoneyWisePortfolio pTarget,
1239                                                          final MoneyWiseTransaction pTrans) {
1240         /* If there is cash to transfer */
1241         final OceanusMoney myAmount = getPortfolioCashValue(pSource, pTrans);
1242         if (myAmount != null) {
1243             /* Access details */
1244             final MoneyWiseQIFAccountEvents mySource = theFile.registerAccount(pSource);
1245             final MoneyWiseQIFAccountEvents myTarget = theFile.registerAccount(pTarget);
1246 
1247             /* Obtain classes */
1248             final List<MoneyWiseQIFClass> myList = theBuilder.getTransactionClasses(pTrans);
1249 
1250             /* Create an XOut event */
1251             MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.XOUT);
1252             myEvent.recordAmount(myAmount);
1253             myEvent.recordPayee(theBuilder.buildXferToPayee(pTarget));
1254             myEvent.recordXfer(myTarget.getAccount(), myList, myAmount);
1255 
1256             /* Add to event list */
1257             mySource.addEvent(myEvent);
1258 
1259             /* Create an XIn event */
1260             myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.XIN);
1261             myEvent.recordAmount(myAmount);
1262             myEvent.recordPayee(theBuilder.buildXferFromPayee(pSource));
1263             myEvent.recordXfer(mySource.getAccount(), myList, myAmount);
1264 
1265             /* Add to event list */
1266             myTarget.addEvent(myEvent);
1267         }
1268 
1269         /* Access the relevant bucket */
1270         final MoneyWiseAnalysisPortfolioBucketList myPortfolios = theAnalysis.getPortfolios();
1271         final MoneyWiseAnalysisPortfolioBucket myBucket = myPortfolios.getBucket(pSource);
1272 
1273         /* Loop through the securities */
1274         final Iterator<MoneyWiseAnalysisSecurityBucket> myIterator = myBucket.securityIterator();
1275         while (myIterator.hasNext()) {
1276             final MoneyWiseAnalysisSecurityBucket mySecurity = myIterator.next();
1277 
1278             /* Process transfer for this bucket */
1279             processPortfolioXferForHolding(mySecurity.getSecurityHolding(), pTarget, pTrans);
1280         }
1281     }
1282 
1283     /**
1284      * Process PortfolioXfer for Holding.
1285      *
1286      * @param pSource the source holding
1287      * @param pTarget the target portfolio
1288      * @param pTrans  the transaction
1289      */
1290     protected void processPortfolioXferForHolding(final MoneyWiseSecurityHolding pSource,
1291                                                   final MoneyWisePortfolio pTarget,
1292                                                   final MoneyWiseTransaction pTrans) {
1293         /* Determine if this holding was transferred */
1294         final OceanusUnits myUnits = getBaseUnitsForHolding(pSource, pTrans);
1295         if (myUnits.isNonZero()) {
1296             /* Access details */
1297             final MoneyWisePortfolio mySourcePortfolio = pSource.getPortfolio();
1298             final MoneyWiseSecurity mySecurity = pSource.getSecurity();
1299             final MoneyWiseQIFAccountEvents mySource = theFile.registerAccount(mySourcePortfolio);
1300             final MoneyWiseQIFAccountEvents myTarget = theFile.registerAccount(pTarget);
1301             final MoneyWiseQIFSecurity myQSecurity = theFile.registerSecurity(mySecurity);
1302             OceanusMoney myCost = getDeltaCostForHolding(pSource, pTrans);
1303 
1304             /* If there is an associated cost */
1305             if (myCost != null) {
1306                 /* Convert cost to positive */
1307                 myCost = new OceanusMoney(myCost);
1308                 myCost.negate();
1309 
1310                 /* Obtain price for the date */
1311                 final OceanusPrice myPrice = getPriceForDate(mySecurity, pTrans.getDate());
1312 
1313                 /* Obtain classes */
1314                 final List<MoneyWiseQIFClass> myList = theBuilder.getTransactionClasses(pTrans);
1315 
1316                 /* Create a sell shares event */
1317                 MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.SELL);
1318                 myEvent.recordAmount(myCost);
1319                 myEvent.recordSecurity(myQSecurity);
1320                 myEvent.recordQuantity(myUnits);
1321                 myEvent.recordPrice(myPrice);
1322 
1323                 /* Add to event list */
1324                 mySource.addEvent(myEvent);
1325 
1326                 /* Create an XOut event */
1327                 myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.XOUT);
1328                 myEvent.recordAmount(myCost);
1329                 myEvent.recordPayee(theBuilder.buildXferToPayee(pTarget));
1330                 myEvent.recordXfer(myTarget.getAccount(), myList, myCost);
1331 
1332                 /* Add to event list */
1333                 mySource.addEvent(myEvent);
1334 
1335                 /* Create an XIn event */
1336                 myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.XIN);
1337                 myEvent.recordAmount(myCost);
1338                 myEvent.recordPayee(theBuilder.buildXferFromPayee(pSource));
1339                 myEvent.recordXfer(mySource.getAccount(), myList, myCost);
1340 
1341                 /* Add to event list */
1342                 myTarget.addEvent(myEvent);
1343 
1344                 /* Create a buy shares event */
1345                 myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.BUY);
1346                 myEvent.recordAmount(myCost);
1347                 myEvent.recordSecurity(myQSecurity);
1348                 myEvent.recordQuantity(myUnits);
1349                 myEvent.recordPrice(myPrice);
1350 
1351                 /* Add to event list */
1352                 myTarget.addEvent(myEvent);
1353 
1354                 /* else just simple transfer of shares */
1355             } else {
1356                 /* Create an SharesOut event */
1357                 MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.SHRSOUT);
1358                 myEvent.recordSecurity(myQSecurity);
1359                 myEvent.recordQuantity(myUnits);
1360 
1361                 /* Add to event list */
1362                 mySource.addEvent(myEvent);
1363 
1364                 /* Create an SharesIn event */
1365                 myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.SHRSIN);
1366                 myEvent.recordSecurity(myQSecurity);
1367                 myEvent.recordQuantity(myUnits);
1368 
1369                 /* Add to event list */
1370                 myTarget.addEvent(myEvent);
1371             }
1372         }
1373     }
1374 
1375     /**
1376      * Process Cash Transfer between portfolios.
1377      *
1378      * @param pSource the source portfolio
1379      * @param pTarget the target portfolio
1380      * @param pTrans  the transaction
1381      */
1382     protected void processCashTransferBetweenPortfolios(final MoneyWisePortfolio pSource,
1383                                                         final MoneyWisePortfolio pTarget,
1384                                                         final MoneyWiseTransaction pTrans) {
1385         /* Access details */
1386         final MoneyWiseQIFAccountEvents mySource = theFile.registerAccount(pSource);
1387         final MoneyWiseQIFAccountEvents myTarget = theFile.registerAccount(pTarget);
1388         final OceanusMoney myAmount = pTrans.getAmount();
1389 
1390         /* Obtain classes */
1391         final List<MoneyWiseQIFClass> myList = theBuilder.getTransactionClasses(pTrans);
1392 
1393         /* Create an XOut event */
1394         MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.XOUT);
1395         myEvent.recordAmount(myAmount);
1396         myEvent.recordPayee(theBuilder.buildXferToPayee(pTarget));
1397         myEvent.recordXfer(myTarget.getAccount(), myList, myAmount);
1398 
1399         /* Add to event list */
1400         mySource.addEvent(myEvent);
1401 
1402         /* Create an XIn event */
1403         myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.XIN);
1404         myEvent.recordAmount(myAmount);
1405         myEvent.recordPayee(theBuilder.buildXferFromPayee(pSource));
1406         myEvent.recordXfer(mySource.getAccount(), myList, myAmount);
1407 
1408         /* Add to event list */
1409         myTarget.addEvent(myEvent);
1410     }
1411 
1412     /**
1413      * Process transfer to a portfolio.
1414      *
1415      * @param pPortfolio the portfolio
1416      * @param pDebit     the source account
1417      * @param pTrans     the transaction
1418      */
1419     protected void processTransferToPortfolio(final MoneyWisePortfolio pPortfolio,
1420                                               final MoneyWiseTransAsset pDebit,
1421                                               final MoneyWiseTransaction pTrans) {
1422         /* Switch on transaction type */
1423         switch (pTrans.getCategoryClass()) {
1424             case TRANSFER:
1425                 processCashTransferToPortfolio(pPortfolio, pDebit, pTrans);
1426                 break;
1427             default:
1428                 LOGGER.error("Unsupported TransferToPortfolio Category: <%s>", pTrans.getCategoryClass());
1429                 break;
1430         }
1431     }
1432 
1433     /**
1434      * Process Cash Transfer to portfolio.
1435      *
1436      * @param pPortfolio the target portfolio
1437      * @param pSource    the source account
1438      * @param pTrans     the transaction
1439      */
1440     protected void processCashTransferToPortfolio(final MoneyWisePortfolio pPortfolio,
1441                                                   final MoneyWiseTransAsset pSource,
1442                                                   final MoneyWiseTransaction pTrans) {
1443         /* Access details */
1444         final MoneyWiseQIFAccountEvents myPortfolio = theFile.registerAccount(pPortfolio);
1445         final MoneyWiseQIFAccountEvents mySource = theFile.registerAccount(pSource);
1446         OceanusMoney myAmount = pTrans.getAmount();
1447 
1448         /* Obtain classes */
1449         final List<MoneyWiseQIFClass> myList = theBuilder.getTransactionClasses(pTrans);
1450 
1451         /* Create an XIn event */
1452         final MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.XIN);
1453         myEvent.recordAmount(myAmount);
1454         myEvent.recordPayee(theBuilder.buildXferToPayee(pSource));
1455         myEvent.recordXfer(mySource.getAccount(), myList, myAmount);
1456 
1457         /* Add to event list */
1458         myPortfolio.addEvent(myEvent);
1459 
1460         /* Create the sending transfer event */
1461         final MoneyWiseQIFEvent myXferEvent = new MoneyWiseQIFEvent(theFile, pTrans);
1462         myAmount = new OceanusMoney(myAmount);
1463         myAmount.negate();
1464         myXferEvent.recordAmount(myAmount);
1465         myXferEvent.recordPayee(theBuilder.buildXferFromPayee(pPortfolio));
1466         myXferEvent.recordAccount(myPortfolio.getAccount(), myList);
1467 
1468         /* Add to event list */
1469         mySource.addEvent(myXferEvent);
1470     }
1471 
1472     /**
1473      * Process transfer from a portfolio.
1474      *
1475      * @param pPortfolio the portfolio
1476      * @param pCredit    the target account
1477      * @param pTrans     the transaction
1478      */
1479     protected void processTransferFromPortfolio(final MoneyWisePortfolio pPortfolio,
1480                                                 final MoneyWiseTransAsset pCredit,
1481                                                 final MoneyWiseTransaction pTrans) {
1482         /* Switch on transaction type */
1483         switch (pTrans.getCategoryClass()) {
1484             case TRANSFER:
1485                 processCashTransferFromPortfolio(pPortfolio, pCredit, pTrans);
1486                 break;
1487             default:
1488                 LOGGER.error("Unsupported TransferFromPortfolio Category: <%s>", pTrans.getCategoryClass());
1489                 break;
1490         }
1491     }
1492 
1493     /**
1494      * Process Cash Transfer from portfolio.
1495      *
1496      * @param pPortfolio the source portfolio
1497      * @param pTarget    the target account
1498      * @param pTrans     the transaction
1499      */
1500     protected void processCashTransferFromPortfolio(final MoneyWisePortfolio pPortfolio,
1501                                                     final MoneyWiseTransAsset pTarget,
1502                                                     final MoneyWiseTransaction pTrans) {
1503         /* Access details */
1504         final MoneyWiseQIFAccountEvents myPortfolio = theFile.registerAccount(pPortfolio);
1505         final MoneyWiseQIFAccountEvents myTarget = theFile.registerAccount(pTarget);
1506         final OceanusMoney myAmount = pTrans.getAmount();
1507 
1508         /* Obtain classes */
1509         final List<MoneyWiseQIFClass> myList = theBuilder.getTransactionClasses(pTrans);
1510 
1511         /* Create an XOut event */
1512         final MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.XOUT);
1513         myEvent.recordAmount(myAmount);
1514         myEvent.recordPayee(theBuilder.buildXferToPayee(pTarget));
1515         myEvent.recordXfer(myTarget.getAccount(), myList, myAmount);
1516 
1517         /* Add to event list */
1518         myPortfolio.addEvent(myEvent);
1519 
1520         /* Create the receiving transfer event */
1521         final MoneyWiseQIFEvent myXferEvent = new MoneyWiseQIFEvent(theFile, pTrans);
1522         myXferEvent.recordAmount(myAmount);
1523         myXferEvent.recordPayee(theBuilder.buildXferFromPayee(pPortfolio));
1524         myXferEvent.recordAccount(myPortfolio.getAccount(), myList);
1525 
1526         /* Add to event list */
1527         myTarget.addEvent(myXferEvent);
1528     }
1529 
1530     /**
1531      * Process expense from a portfolio.
1532      *
1533      * @param pCredit    the target payee
1534      * @param pPortfolio the portfolio
1535      * @param pTrans     the transaction
1536      */
1537     protected void processExpenseFromPortfolio(final MoneyWisePayee pCredit,
1538                                                final MoneyWisePortfolio pPortfolio,
1539                                                final MoneyWiseTransaction pTrans) {
1540         /* Access Details */
1541         final MoneyWiseQIFAccountEvents myPortfolio = theFile.registerAccount(pPortfolio);
1542         final MoneyWiseQIFPayee myPayee = theFile.registerPayee(pCredit);
1543         final MoneyWiseQIFEventCategory myCategory = theFile.registerCategory(pTrans.getCategory());
1544         final OceanusMoney myAmount = new OceanusMoney(pTrans.getAmount());
1545         myAmount.negate();
1546 
1547         /* Create an expense event */
1548         final MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.CASH);
1549         myEvent.recordAmount(myAmount);
1550         myEvent.recordPayee(myPayee);
1551         myEvent.recordCategory(myCategory);
1552 
1553         /* Add to event list */
1554         myPortfolio.addEvent(myEvent);
1555     }
1556 
1557     /**
1558      * Process income to a portfolio.
1559      *
1560      * @param pDebit     the source payee
1561      * @param pPortfolio the portfolio
1562      * @param pTrans     the transaction
1563      */
1564     protected void processIncomeToPortfolio(final MoneyWisePayee pDebit,
1565                                             final MoneyWisePortfolio pPortfolio,
1566                                             final MoneyWiseTransaction pTrans) {
1567         /* Access Details */
1568         final MoneyWiseQIFAccountEvents myPortfolio = theFile.registerAccount(pPortfolio);
1569         final MoneyWiseQIFPayee myPayee = theFile.registerPayee(pDebit);
1570         final MoneyWiseQIFEventCategory myCategory = theFile.registerCategory(pTrans.getCategory());
1571         final OceanusMoney myAmount = pTrans.getAmount();
1572 
1573         /* Create an income event */
1574         final MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theFile, pTrans, MoneyWiseQActionType.CASH);
1575         myEvent.recordAmount(myAmount);
1576         myEvent.recordPayee(myPayee);
1577         myEvent.recordCategory(myCategory);
1578 
1579         /* Add to event list */
1580         myPortfolio.addEvent(myEvent);
1581     }
1582 }