gnucash master: Fix reported problems in the Advanced Portfolio report.

Mike Alexander mta at code.gnucash.org
Thu Feb 13 01:22:43 EST 2014


Updated	 via  https://github.com/Gnucash/gnucash/commit/e842f0e7 (commit)
	from  https://github.com/Gnucash/gnucash/commit/035959a2 (commit)



commit e842f0e751a2524547726715c29a76e9d7846f62
Author: Mike Alexander <mta at umich.edu>
Date:   Thu Feb 13 01:11:49 2014 -0500

    Fix reported problems in the Advanced Portfolio report.
    
     - Main loop rewritten to be more robust and accurate.
     - Added option to include broker fees in basis calculations.
     - Added option to ignore money transfered to or from parent
       or sibling accounts.

diff --git a/src/report/standard-reports/advanced-portfolio.scm b/src/report/standard-reports/advanced-portfolio.scm
index 107e38a..3f076c5 100644
--- a/src/report/standard-reports/advanced-portfolio.scm
+++ b/src/report/standard-reports/advanced-portfolio.scm
@@ -46,7 +46,8 @@
 (define optname-show-shares (N_ "Show number of shares"))
 (define optname-basis-method (N_ "Basis calculation method"))
 (define optname-prefer-pricelist (N_ "Set preference for price list data"))
-(define optname-ignore-brokerage-fees (N_ "Ignore brokerage fees when calculating returns"))
+(define optname-brokerage-fees (N_ "How to report brokerage fees"))
+(define optname-ignore-parent-and-sibling-transfers (N_ "Ignore money moved to parent and sibling accounts"))
 
 ;; To avoid overflows in our calculations, define a denominator for prices and unit values
 (define price-denom 100000000)
@@ -103,9 +104,24 @@
       #t))
 
     (add-option
+     (gnc:make-multichoice-option
+      gnc:pagename-general optname-brokerage-fees
+      "g" (N_ "How to report commissions and other brokerage fees.") 'include-in-basis
+      (list (vector 'include-in-basis
+                    (N_ "Include in basis")
+                    (N_ "Include brokerage fees in the basis for the asset."))
+            (vector 'include-in-gain
+                    (N_ "Include in gain")
+                    (N_  "Include brokerage fees in the gain and loss but not in the basis."))
+            (vector 'ignore-brokerage
+                    (N_ "Ignore")
+                    (N_ "Ignore brokerage fees entirely."))
+            )))
+      
+    (add-option
      (gnc:make-simple-boolean-option
-      gnc:pagename-general optname-ignore-brokerage-fees "g"
-      (N_ "Ignore brokerage fees when calculating returns.")
+      gnc:pagename-general optname-ignore-parent-and-sibling-transfers "h"
+      (N_ "Money moved from or to a parent or sibling account is not counted as money in or out")
       #f))
 
     (gnc:register-option
@@ -363,13 +379,39 @@
         (gnc-price-ref price)
         (gnc-price-list-destroy price-list)
         price)))
+        
+  ;; Return true if acct is in the list of accounts
+  (define (account-in-list acct acct-list)
+    (cond ((eqv? acct-list '()) #f)
+          ((same-account? acct (car acct-list)) #t)
+          (else (account-in-list acct (cdr acct-list)))))
+          
+  ;; Return true if account a1 is the parent or a sibling of account a2
+  (define (parent-or-sibling? a1 a2)
+    (let ((parent (gnc-account-get-parent a2)))
+          (or (same-account? parent a1)
+              (account-in-list a1 (gnc-account-get-children parent)))))
+              
+  ;; Test whether the given split is the source of a spin off transaction
+  ;; This will be a no-units split with only one other split.
+  ;; xaccSplitGetOtherSplit only returns on a two-split txn.  It's not a spinoff
+  ;; is the other split is in an income or expense account.
+  (define (spin-off? split current)
+     (let ((other-split (xaccSplitGetOtherSplit split)))
+          (and (gnc-numeric-zero-p (xaccSplitGetAmount split))
+               (same-account? current (xaccSplitGetAccount split))
+               (not (null? other-split))
+               (not (split-account-type? other-split ACCT-TYPE-EXPENSE))
+               (not (split-account-type? other-split ACCT-TYPE-INCOME)))))
+  
   
 (define (table-add-stock-rows table accounts to-date
                                 currency price-fn exchange-fn price-source
 				include-empty show-symbol show-listing show-shares show-price
-                                basis-method prefer-pricelist ignore-brokerage-fees
-                                total-basis total-value total-moneyin total-moneyout
-                                total-income total-gain total-ugain total-brokerage)
+                                basis-method prefer-pricelist handle-brokerage-fees 
+                                ignore-parent-and-siblings total-basis total-value
+                                total-moneyin total-moneyout total-income total-gain 
+                                total-ugain total-brokerage)
 
    (let ((share-print-info
 	  (gnc-share-print-info-places
@@ -390,10 +432,8 @@
                  (units (cadr (unit-collector 'getpair commodity #f)))
 
                  ;; Counter to keep track of stuff
-                 (unitscoll     (gnc:make-commodity-collector))
                  (brokeragecoll (gnc:make-commodity-collector))
                  (dividendcoll  (gnc:make-commodity-collector))
-		 (dividend-reincoll (gnc:make-commodity-collector))
                  (moneyincoll   (gnc:make-commodity-collector))
                  (moneyoutcoll  (gnc:make-commodity-collector))
                  (gaincoll      (gnc:make-commodity-collector))
@@ -410,7 +450,7 @@
 		 (use-txn #f)
 		 (basis-list '())
 		 ;; setup an alist for the splits we've already seen.
-		 (seen_split '())
+		 (seen_trans '())
 		 )
 
             (define (my-exchange-fn fromunits tocurrency)
@@ -501,223 +541,208 @@
 		      (commod-currency (xaccTransGetCurrency parent))
 		      (commod-currency-frac (gnc-commodity-get-fraction commod-currency)))
 		 
-		 (if (gnc:timepair-le txn-date to-date)
-		     (begin
+		 (if (and (gnc:timepair-le txn-date to-date)
+		          (not (assoc-ref seen_trans (gncTransGetGUID parent))))
+		     (let ((trans-income (gnc-numeric-zero))
+		           (trans-brokerage (gnc-numeric-zero))
+		           (trans-shares (gnc-numeric-zero))
+		           (trans-sold (gnc-numeric-zero))
+		           (trans-bought (gnc-numeric-zero))
+		           (trans-moneyin (gnc-numeric-zero))
+		           (trans-moneyout (gnc-numeric-zero)))
+
 		       (gnc:debug "Transaction " (xaccTransGetDescription parent))
-		       ;; here's where we have problems. we are now going to look at each
-		       ;; split of the the parent txn of the current split (above) that we
-		       ;; are on. This means we might hit each split more than once as the
-		       ;; parent transaction might touch the current account more than once.
+		       ;; Add this transaction to the list of processed transactions so we don't
+		       ;; do it again if there is another split in it for this account
+		       (set! seen_trans (acons (gncTransGetGUID parent) #t seen_trans))
+		       
+		       ;; Go through all the splits in the transaction to get an overall idea of 
+		       ;; what it does in terms of income, money in or out, shares bought or sold, etc.
 		       (for-each
-			(lambda (s)
-
-			  ;; have we seen this split?
-			  (if (not (assoc-ref seen_split (gncSplitGetGUID s)))
-
-			      (let
-				  ;; get the split's units and value
-				  ((split-units (xaccSplitGetAmount s))
-				   (split-value (xaccSplitGetValue s)))
-
-				;; first add this split to the seen_split list so we only look at it once.
-				(set! seen_split (acons (gncSplitGetGUID s) #t seen_split))
-
-				(gnc:debug "split units " (gnc-numeric-to-string split-units) " split-value " 
-				           (gnc-numeric-to-string split-value) " commod-currency " 
-				           (gnc-commodity-get-printname commod-currency))
-			        
-				;; now we look at what type of split this is and process accordingly
-			  (cond
-
-				 ;; in theory, the only expenses are
-				 ;; brokerage fees. Not true, you can
-				 ;; have expenses for "donating"
-				 ;; shares to a charity, for
-				 ;; example. In this case, there will
-				 ;; be *only* two
-				 ;; splits. xaccSplitGetOtherSplit
-				 ;; returns null for a
-				 ;; more-than-two-splits txn
-				 ((split-account-type? s ACCT-TYPE-EXPENSE)
-				  (if (equal? current (xaccSplitGetAccount (xaccSplitGetOtherSplit s)))
-				      ;; "donated shares"
-				      (begin (gnc:debug "Money out 1 " (gnc-numeric-to-string split-value))
-				             (moneyoutcoll 'add commod-currency split-value))
-				      ;; brokerage fees
-				      (begin (gnc:debug "Brokerage 1 " (gnc-numeric-to-string split-value))
-				             (brokeragecoll 'add commod-currency split-value))))
-
-				 ;; in theory, income is a dividend of
-				 ;; some kind. it could also be
-				 ;; gains. that gets handled later. it
-				 ;; could also be direct income into
-				 ;; shares, say from an employer into
-				 ;; a retirement account. basically,
-				 ;; there is nothing that can be done
-				 ;; with these to differentiate them
-				 ((split-account-type? s ACCT-TYPE-INCOME)
-				  (dividendcoll 
-				   'add commod-currency 
-				   ;; dig through the txn looking for
-				   ;; the stock itself and base the
-				   ;; dividend on that. This allows
-				   ;; dividends to be split between
-				   ;; multiple stocks based on the
-				   ;; value of each stock purchased
-				   (let* ((txn (xaccSplitGetParent s))
-					 (dividend-rein (gnc-numeric-zero))
-					 (dividend-income (gnc-numeric-neg (xaccSplitGetValue s)))
-					 (adjusted-dividend dividend-income)
-					 (split-brokerage (gnc-numeric-zero))
-					 (split-ratio (gnc-numeric-zero)))
-				     (for-each
-				      (lambda (x) 
-					(cond 
-					 ((and (same-account? current (xaccSplitGetAccount x))
-					      (gnc-numeric-positive-p (xaccSplitGetAmount x)))
-					  (begin
-					    (set! dividend-rein (xaccSplitGetValue x))
-					    (dividend-reincoll 'add commod-currency dividend-rein)
-					    (gnc:debug "setting the dividend-rein to " (gnc-numeric-to-string (xaccSplitGetValue x)))))
-					 ;; very special case: we have
-					 ;; a split that points to the
-					 ;; current account with no
-					 ;; shares (amount) but a
-					 ;; value == gains/loss split,
-					 ;; adjust this back out of
-					 ;; dividends because we'll
-					 ;; erroneously pick it up
-					 ;; later.
-					 ((and (same-account? current (xaccSplitGetAccount x))
-					       (gnc-numeric-zero-p (xaccSplitGetAmount x))
-					       (not (gnc-numeric-zero-p (xaccSplitGetValue x))))
-					  (begin (gnc:debug "dividend 2 " (gnc-numeric-to-string (xaccSplitGetValue x)))
-					         (dividendcoll 'add commod-currency (gnc-numeric-neg (xaccSplitGetValue x)))))
-
-					 ((split-account-type? x ACCT-TYPE-EXPENSE)
-					  (begin
-					    (gnc-numeric-sub adjusted-dividend (xaccSplitGetValue x) commod-currency-frac GNC-RND-ROUND)
-					    (gnc:debug "adjusting adjusted-dividend by " (gnc-numeric-to-string (xaccSplitGetValue x)))
-					    ;; grab the brokerage that
-					    ;; may be associated so we
-					    ;; can split it too
-					    (gnc-numeric-add split-brokerage (xaccSplitGetValue x) commod-currency-frac GNC-RND-ROUND)
-					    )
-					  )
-					 )
-					)
-				      (xaccTransGetSplitList txn))
-				     
-				     ;; make a ratio out of the reinvest and adjusted dividends
-				     (set! split-ratio (gnc-numeric-div dividend-rein 
-									adjusted-dividend 
-									GNC-DENOM-AUTO GNC-RND-ROUND))
-
-                                     (if (not (gnc-numeric-zero-p split-brokerage))
-                                       (begin
-                                          ;; take the brokerage back out and apply the ratio
-                                          (gnc:debug "Reducing brokerage " (gnc-numeric-to-string split-brokerage) 
-                                                     " by ratio " (gnc-numeric-to-string split-ratio))
-                                          (brokeragecoll 'add commod-currency (gnc-numeric-neg split-brokerage))
-                                          (brokeragecoll 'add commod-currency 
-                                                         (gnc-numeric-mul split-brokerage 
-                                                                          split-ratio
-                                                                          commod-currency-frac GNC-RND-ROUND))
-				       )
-				     )  
-
-				     (if (gnc-numeric-zero-p dividend-rein)
-				         (begin
-					 ;; no reinvested dividend, return just the income split
-				         (gnc:debug "Dividend 1 " (gnc-numeric-to-string dividend-income))
-					 dividend-income
-				         )	
-				        
-					 ;; dividend reinvested so
-					 ;; apply the ratio to the
-					 ;; dividend and return it for
-					 ;; use in the dividend
-					 ;; collector
-					 (let ((div (gnc-numeric-mul dividend-income 
-						                     split-ratio
-						                     commod-currency-frac GNC-RND-ROUND)))
-					    (gnc:debug "Adjusted dividend " (gnc-numeric-to-string div))
-					    div)
-					 )
-				     )
-				   ))
-
-				 ;; we have units, handle all cases of that
-				 ((not (gnc-numeric-zero-p split-units))
-				    ;; are we dealing with the actual stock/fund?
-				    (if (same-account? current (xaccSplitGetAccount s))
-					(let ((split-value-currency (gnc:gnc-monetary-amount 
-									(my-exchange-fn (gnc:make-gnc-monetary 
-									   commod-currency split-value) currency)))
-			                      (orig-basis (sum-basis basis-list currency-frac)))
-                                          (gnc:debug "going in to basis list " basis-list " " (gnc-numeric-to-string split-units) " "
-                                                     (gnc-numeric-to-string split-value))
-
-					  ;; adjust the basis
-					  (set! basis-list (basis-builder basis-list split-units split-value-currency 
-									  basis-method currency-frac))
-                                          (gnc:debug  "coming out of basis list " basis-list)
-                                          
-					  ;; adjust moneyin/out and calculate the gain
-					  (if (gnc-numeric-positive-p split-value)
-					      ;; but only adjust moneyin if it's not a spinoff
-					      (if (or (null? (xaccSplitGetOtherSplit s))
-						      (not (gnc-numeric-zero-p (xaccSplitGetAmount (xaccSplitGetOtherSplit s)))))
-					       (begin (gnc:debug "Money in 2 " (gnc-numeric-to-string split-value))
-					              (moneyincoll 'add commod-currency split-value)))
-					      ;; Split value is zero or negative.  If it's zero it's either a stock split/merge
-					      ;; or the stock has become worthless (which looks like a merge where the number
-					      ;; of shares goes to zero).  If the value is negative then it's a disposal of some sort.
-					      (let ((new-basis (sum-basis basis-list currency-frac)))
-                                                     (if (or (gnc-numeric-zero-p new-basis)
-                                                             (gnc-numeric-negative-p split-value))
-                                                       ;; Split value is negative or new basis is zero (stock is worthless), 
-                                                       ;; Capital gain is money out minus change in basis
-                                                       (let ((gain (gnc-numeric-sub (gnc-numeric-abs split-value-currency)
-                                                                                 (gnc-numeric-sub orig-basis new-basis
-                                                                                                  currency-frac GNC-RND-ROUND)
-                                                                                 currency-frac GNC-RND-ROUND)))
-                                                              (gnc:debug "Old basis=" (gnc-numeric-to-string orig-basis)
-                                                                         " New basis=" (gnc-numeric-to-string new-basis)
-                                                                         " Gain=" (gnc-numeric-to-string gain))
-                                                              (gaincoll 'add currency gain)
-                                                              (gnc:debug "Money out 2 " (gnc-numeric-to-string (gnc-numeric-neg split-value)))
-                                                              (moneyoutcoll 'add commod-currency (gnc-numeric-neg split-value))))))
-					  )
-					)
-				  )
-
-				 ;; here is where we handle a spin-off txn. This will be a no-units
-				 ;; transaction with only one other split. xaccSplitGetOtherSplit only
-				 ;; returns on a two-split txn.  It's not a spinoff is the other split is
-				 ;; in an income or expense account.
-				 ((and (gnc-numeric-zero-p split-units) 
-				       (not (null? (xaccSplitGetOtherSplit s)))
-				       (same-account? current (xaccSplitGetAccount s))
-				       (not (split-account-type? (xaccSplitGetOtherSplit s) ACCT-TYPE-EXPENSE))
-				       (not (split-account-type? (xaccSplitGetOtherSplit s) ACCT-TYPE-INCOME)))
-				    (gnc:debug "before spin-off basis list " basis-list)
-				    (set! basis-list (basis-builder basis-list split-units (gnc:gnc-monetary-amount 
-                                                                                            (my-exchange-fn (gnc:make-gnc-monetary 
-                                                                                                          commod-currency split-value) 
-                                                                                                         currency)) 
-                                                                                                         basis-method
-                                                                                                         currency-frac))
-				    (gnc:debug "after spin-off basis list "  basis-list)
-				  )
-				 )
-				)
-			      )
-			  )
-			(xaccTransGetSplitList parent)
-			)
+		         (lambda (s)
+                           (let ((split-units (xaccSplitGetAmount s))
+                                 (split-value (xaccSplitGetValue s)))
+
+                             (gnc:debug "Pass 1: split units " (gnc-numeric-to-string split-units) " split-value " 
+                                        (gnc-numeric-to-string split-value) " commod-currency " 
+                                        (gnc-commodity-get-printname commod-currency))
+                             
+                             (cond 
+                                ((split-account-type? s ACCT-TYPE-EXPENSE)
+                                 ;; Brokerage expense unless a two split transaction with other split
+                                 ;; in the stock account in which case it's a stock donation to charity.
+                                 (if (not (same-account? current (xaccSplitGetAccount (xaccSplitGetOtherSplit s)))) 
+                                   (set! trans-brokerage 
+                                         (gnc-numeric-add trans-brokerage split-value commod-currency-frac GNC-RND-ROUND))
+                                   (set! trans-moneyout
+                                         (gnc-numeric-add trans-moneyout split-value commod-currency-frac GNC-RND-ROUND))))
+                                   
+                                ((split-account-type? s ACCT-TYPE-INCOME)
+                                 (set! trans-income (gnc-numeric-sub trans-income split-value
+                                                             commod-currency-frac GNC-RND-ROUND)))
+                                                  
+                                ((same-account? current (xaccSplitGetAccount s))
+                                 (set! trans-shares (gnc-numeric-add trans-shares (gnc-numeric-abs split-units)
+                                                  units-denom GNC-RND-ROUND))
+                                 (if (gnc-numeric-zero-p split-units)
+                                     (if (and (not (gnc-numeric-zero-p split-value))
+                                              (not (spin-off? s current)))
+                                          ;; Gain/loss split (amount zero and value non-zero).  There will be
+                                          ;; a corresponding income split that will incorrectly be added to trans-income
+                                          ;; Fix that by subtracting it here
+                                          (set! trans-income (gnc-numeric-sub trans-income split-value commod-currency-frac GNC-RND-ROUND)))
+                                     ;; Non-zero amount, add the value to the sale or purchase total.
+                                     (if (gnc-numeric-positive-p split-value)
+                                          (set! trans-bought
+                                               (gnc-numeric-add trans-bought split-value commod-currency-frac GNC-RND-ROUND))
+                                          (set! trans-sold
+                                               (gnc-numeric-sub trans-sold split-value commod-currency-frac GNC-RND-ROUND)))))
+                                                  
+                                ((or (split-account-type? s ACCT-TYPE-BANK)
+                                     (split-account-type? s ACCT-TYPE-CASH)
+                                     (split-account-type? s ACCT-TYPE-ASSET)
+                                     (split-account-type? s ACCT-TYPE-STOCK)
+                                     (split-account-type? s ACCT-TYPE-MUTUAL))
+                                 (if (gnc-numeric-positive-p split-value)
+                                      (if (or (not ignore-parent-and-siblings)
+                                              (not (parent-or-sibling? (xaccSplitGetAccount s) current))) 
+                                       (set! trans-moneyout 
+                                             (gnc-numeric-add trans-moneyout split-value commod-currency-frac GNC-RND-ROUND)))
+                                      (if (or (not ignore-parent-and-siblings)
+                                              (not (parent-or-sibling? (xaccSplitGetAccount s) current))) 
+                                       (set! trans-moneyin 
+                                             (gnc-numeric-sub trans-moneyin split-value commod-currency-frac GNC-RND-ROUND))))))
+		         ))
+		         (xaccTransGetSplitList parent)
 		       )
-		     )
+		       
+		       (gnc:debug "Income: " (gnc-numeric-to-string trans-income)
+		                  " Brokerage: " (gnc-numeric-to-string trans-brokerage)
+		                  " Shares traded: " (gnc-numeric-to-string trans-shares)
+		                  " Value sold: " (gnc-numeric-to-string trans-sold)
+		                  " Value purchased: " (gnc-numeric-to-string trans-bought))
+		       (gnc:debug "     Money in: " (gnc-numeric-to-string trans-moneyin)
+		                  " Money out: " (gnc-numeric-to-string trans-moneyout))
+		                  
+		       ;; Income not reinvested
+		       (if (gnc-numeric-positive-p trans-income)
+		           (begin
+                             (set! trans-income (gnc-numeric-sub trans-income trans-bought commod-currency-frac GNC-RND-ROUND))
+                             (set! trans-income (gnc-numeric-sub trans-income trans-brokerage commod-currency-frac GNC-RND-ROUND))
+                             (if (gnc-numeric-positive-p trans-income)
+                               (begin
+                                 (gnc:debug "Adjusted income " (gnc-numeric-to-string trans-income))
+                                 (dividendcoll 'add commod-currency trans-income)))))
+
+                       ;; Brokerage fees.  May be either ignored or part of basis, but that
+                       ;; will be dealt with elsewhere.
+                       (brokeragecoll 'add commod-currency trans-brokerage)
+                           
+                       ;; Money in and out
+                       ;; Don't count non reinvested dividends as money out
+                       (if (gnc-numeric-positive-p trans-income)
+                           (set! trans-moneyout (gnc-numeric-sub trans-moneyout trans-income 
+                                                                 commod-currency-frac GNC-RND-ROUND)))
+                       ;; Exclude brokerage fees if asked to
+                       (if (and (eq? handle-brokerage-fees 'ignore-brokerage)
+                                (gnc-numeric-positive-p trans-brokerage))
+                           (if (gnc-numeric-positive-p trans-moneyin)
+                               (set! trans-moneyin (gnc-numeric-sub trans-moneyin trans-brokerage 
+                                                                    commod-currency-frac GNC-RND-ROUND))
+                               (if (gnc-numeric-positive-p trans-moneyout)
+                                   (set! trans-moneyout (gnc-numeric-add trans-moneyout trans-brokerage
+                                                                         commod-currency-frac GNC-RND-ROUND)))))
+                       ;; Don't let either of them go negative after that adjustment
+                       (if (gnc-numeric-negative-p trans-moneyin)
+                           (set! trans-moneyin (gnc-numeric-zero)))
+                       (if (gnc-numeric-negative-p trans-moneyout)
+                           (set! trans-moneyout (gnc-numeric-zero)))
+                       (gnc:debug "Adjusted moneyin " (gnc-numeric-to-string trans-moneyin)
+                                  " Adjusted moneyout " (gnc-numeric-to-string trans-moneyout))
+                       (moneyincoll 'add commod-currency trans-moneyin)
+                       (moneyoutcoll 'add commod-currency trans-moneyout)
+                           
+                       ;; Look at splits again to handle changes in basis and realized gains 
+		       (for-each
+		         (lambda (s)
+                           (let
+                              ;; get the split's units and value
+                              ((split-units (xaccSplitGetAmount s))
+                               (split-value (xaccSplitGetValue s)))
+
+                             (gnc:debug "Pass 2: split units " (gnc-numeric-to-string split-units) " split-value " 
+                                        (gnc-numeric-to-string split-value) " commod-currency " 
+                                        (gnc-commodity-get-printname commod-currency))
+                             
+                             (cond 
+                               ((and (not (gnc-numeric-zero-p split-units))
+                                     (same-account? current (xaccSplitGetAccount s)))
+                                ;; Split into subject account with non-zero amount.  This is a purchase
+                                ;; or a sale, adjust the basis
+				(let* ((split-value-currency (gnc:gnc-monetary-amount 
+								(my-exchange-fn (gnc:make-gnc-monetary 
+								   commod-currency split-value) currency)))
+			               (orig-basis (sum-basis basis-list currency-frac))
+			               ;; proportion of the fees attributable to this split
+			               (fee-ratio (gnc-numeric-div (gnc-numeric-abs split-units) trans-shares
+			                                           GNC-DENOM-AUTO GNC-RND-ROUND))
+			               ;; Fees for this split in report currency 
+			               (fees-currency (gnc:gnc-monetary-amount (my-exchange-fn 
+			                               (gnc:make-gnc-monetary commod-currency
+			                                 (gnc-numeric-mul fee-ratio trans-brokerage
+			                                                commod-currency-frac GNC-RND-ROUND))
+			                                currency)))
+			               (split-value-with-fees (if (eq? handle-brokerage-fees 'include-in-basis) 
+			                                          ;; Include brokerage fees in basis 
+			                                          (gnc-numeric-add split-value-currency fees-currency
+			                                                        currency-frac GNC-RND-ROUND)
+			                                          split-value-currency)))
+                                  (gnc:debug "going in to basis list " basis-list " " (gnc-numeric-to-string split-units) " "
+                                             (gnc-numeric-to-string split-value-with-fees))
+
+				  ;; adjust the basis
+				  (set! basis-list (basis-builder basis-list split-units split-value-with-fees 
+								  basis-method currency-frac))
+                                  (gnc:debug  "coming out of basis list " basis-list)
+                                  
+                                  ;; If it's a sale or the stock is worthless, calculate the gain
+                                  (if (not (gnc-numeric-positive-p split-value))
+                                       ;; Split value is zero or negative.  If it's zero it's either a stock split/merge
+                                       ;; or the stock has become worthless (which looks like a merge where the number
+                                       ;; of shares goes to zero).  If the value is negative then it's a disposal of some sort.
+                                       (let ((new-basis (sum-basis basis-list currency-frac)))
+                                              (if (or (gnc-numeric-zero-p new-basis)
+                                                      (gnc-numeric-negative-p split-value))
+                                                ;; Split value is negative or new basis is zero (stock is worthless), 
+                                                ;; Capital gain is money out minus change in basis
+                                                (let ((gain (gnc-numeric-sub (gnc-numeric-abs split-value-with-fees)
+                                                                          (gnc-numeric-sub orig-basis new-basis
+                                                                                           currency-frac GNC-RND-ROUND)
+                                                                          currency-frac GNC-RND-ROUND)))
+                                                       (gnc:debug "Old basis=" (gnc-numeric-to-string orig-basis)
+                                                                  " New basis=" (gnc-numeric-to-string new-basis)
+                                                                  " Gain=" (gnc-numeric-to-string gain))
+                                                       (gaincoll 'add currency gain)))))))
+
+                               ;; here is where we handle a spin-off txn. This will be a no-units
+                               ;; split with only one other split. xaccSplitGetOtherSplit only
+                               ;; returns on a two-split txn.  It's not a spinoff is the other split is
+                               ;; in an income or expense account.
+                               ((spin-off? s current)
+                                  (gnc:debug "before spin-off basis list " basis-list)
+                                  (set! basis-list (basis-builder basis-list split-units (gnc:gnc-monetary-amount 
+                                                                                          (my-exchange-fn (gnc:make-gnc-monetary 
+                                                                                                        commod-currency split-value) 
+                                                                                                       currency)) 
+                                                                                                       basis-method
+                                                                                                       currency-frac))
+                                  (gnc:debug "after spin-off basis list "  basis-list))
+                             )
+		         ))
+		         (xaccTransGetSplitList parent)
+		       )  
+		      )
+		   )
 		 )
 	       )
 	     (xaccAccountGetSplitList current)
@@ -732,10 +757,7 @@
 	                                                            currency-frac)))
 	    (gnc:debug "but the actual basis list is " basis-list)
 
-            ;; This removes the already-counted reinvested dividends from moneyin.
-	    (moneyincoll 'minusmerge dividend-reincoll #f)
-
-            (if (not ignore-brokerage-fees)
+            (if (eq? handle-brokerage-fees 'include-in-gain)
 	      (gaincoll 'minusmerge brokeragecoll #f))
 
 	  (if (or include-empty (not (gnc-numeric-zero-p units)))
@@ -817,7 +839,7 @@
 						  (sprintf #f "%.2f%%" (* 100 (/ bothgainvalue moneyinvalue)))))
 					)
 					(gnc:make-html-table-header-cell/markup "number-cell" income)))
-	      (if (not ignore-brokerage-fees)
+	      (if (not (eq? handle-brokerage-fees 'ignore-brokerage))
 		  (append! activecols (list (gnc:make-html-table-header-cell/markup "number-cell" brokerage))))
 	      (append! activecols (list (gnc:make-html-table-header-cell/markup "number-cell" totalreturn)
 					(gnc:make-html-table-header-cell/markup "number-cell" 
@@ -878,8 +900,10 @@
 				  optname-basis-method))
 	(prefer-pricelist (get-option gnc:pagename-general
 				      optname-prefer-pricelist))
-	(ignore-brokerage-fees (get-option gnc:pagename-general
-				  optname-ignore-brokerage-fees))
+	(handle-brokerage-fees (get-option gnc:pagename-general
+				  optname-brokerage-fees))
+	(ignore-parent-and-siblings (get-option gnc:pagename-general
+	                               optname-ignore-parent-and-sibling-transfers))
 
 	(total-basis (gnc:make-commodity-collector))
         (total-value    (gnc:make-commodity-collector))
@@ -950,7 +974,7 @@
 				    (_ "Rate of Gain")
 				    (_ "Income")))
 
-	  (if (not ignore-brokerage-fees)
+	  (if (not (eq? handle-brokerage-fees 'ignore-brokerage))
 	      (append! headercols (list (_ "Brokerage Fees"))))
 
 	  (append! headercols (list (_ "Total Return")
@@ -964,8 +988,8 @@
           
           (table-add-stock-rows
            table accounts to-date currency price-fn exchange-fn price-source
-           include-empty show-symbol show-listing show-shares show-price
-	   basis-method prefer-pricelist ignore-brokerage-fees
+           include-empty show-symbol show-listing show-shares show-price basis-method
+	   prefer-pricelist handle-brokerage-fees ignore-parent-and-siblings
            total-basis total-value total-moneyin total-moneyout
            total-income total-gain total-ugain total-brokerage)
 	  
@@ -1017,7 +1041,7 @@
 				      (sprintf #f "%.2f%%" (* 100 (/ totalgainvalue totalinvalue))))))
 			       (gnc:make-html-table-cell/markup
 				"total-number-cell" sum-total-income)))
-	  (if (not ignore-brokerage-fees)
+	  (if (not (eq? handle-brokerage-fees 'ignore-brokerage))
 	      (append! totalscols (list
 			       (gnc:make-html-table-cell/markup
                                 "total-number-cell" sum-total-brokerage))))



Summary of changes:
 src/report/standard-reports/advanced-portfolio.scm | 492 +++++++++++----------
 1 file changed, 258 insertions(+), 234 deletions(-)



More information about the gnucash-changes mailing list