gnucash master: Multiple changes pushed

Christopher Lam clam at code.gnucash.org
Mon Jun 29 07:45:22 EDT 2020


Updated	 via  https://github.com/Gnucash/gnucash/commit/8d47622a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/945c11e2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c68f2828 (commit)
	from  https://github.com/Gnucash/gnucash/commit/3d931511 (commit)



commit 8d47622ab6e9ffe6e59effad90fd0b4f03b023a1
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Thu Jun 25 09:00:35 2020 +0800

    [advanced-portfolio] move table-add-stock-rows toplevel

diff --git a/gnucash/report/reports/standard/advanced-portfolio.scm b/gnucash/report/reports/standard/advanced-portfolio.scm
index 2bcbebed7..8e6abb8ba 100644
--- a/gnucash/report/reports/standard/advanced-portfolio.scm
+++ b/gnucash/report/reports/standard/advanced-portfolio.scm
@@ -275,6 +275,602 @@ by preventing negative stock balances.<br/>")
    (else b-list)))
 
 
+(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 handle-brokerage-fees
+         total-basis total-value
+         total-moneyin total-moneyout total-income total-gain
+         total-ugain total-brokerage share-print-info)
+
+  (define work-to-do 0)
+
+  (define work-done 0)
+
+  (define (split-account-type? split type)
+    (eq? type (xaccAccountGetType (xaccSplitGetAccount split))))
+
+  (define (spin-off? split current)
+     (let ((other-split (xaccSplitGetOtherSplit split)))
+          (and (gnc-numeric-zero-p (xaccSplitGetAmount split))
+               (equal? 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-internal accounts odd-row?)
+    (if (null? accounts) total-value
+        (let* ((row-style (if odd-row? "normal-row" "alternate-row"))
+               (current (car accounts))
+               (rest (cdr accounts))
+               ;; commodity is the actual stock/thing we are looking at
+               (commodity (xaccAccountGetCommodity current))
+               (ticker-symbol (gnc-commodity-get-mnemonic commodity))
+               (listing (gnc-commodity-get-namespace commodity))
+               (unit-collector (gnc:account-get-comm-balance-at-date
+                                current to-date #f))
+               (units (cadr (unit-collector 'getpair commodity #f)))
+
+               ;; Counter to keep track of stuff
+               (brokeragecoll (gnc:make-commodity-collector))
+               (dividendcoll  (gnc:make-commodity-collector))
+               (moneyincoll   (gnc:make-commodity-collector))
+               (moneyoutcoll  (gnc:make-commodity-collector))
+               (gaincoll      (gnc:make-commodity-collector))
+
+
+               ;; the price of the commodity at the time of the report
+               (price (price-fn commodity currency to-date))
+               ;; the value of the commodity, expressed in terms of
+               ;; the report's currency.
+               (value (gnc:make-gnc-monetary currency (gnc-numeric-zero)))  ;; Set later
+               (currency-frac (gnc-commodity-get-fraction currency))
+
+               (pricing-txn #f)
+               (use-txn #f)
+               (basis-list '())
+               ;; setup an alist for the splits we've already seen.
+               (seen_trans '())
+               ;; Account used to hold remainders from income reinvestments and
+               ;; running total of amount moved there
+               (drp-holding-account #f)
+               (drp-holding-amount (gnc-numeric-zero))
+               )
+
+          (define (my-exchange-fn fromunits tocurrency)
+            (if (and (gnc-commodity-equiv currency tocurrency)
+                     (gnc-commodity-equiv (gnc:gnc-monetary-commodity fromunits) commodity))
+                ;; Have a price for this commodity, but not necessarily in the report's
+                ;; currency.  Get the value in the commodity's currency and convert it to
+                ;; report currency.
+                (exchange-fn
+                 ;; This currency will usually be the same as tocurrency so the
+                 ;; call to exchange-fn below will do nothing
+                 (gnc:make-gnc-monetary
+                  (if use-txn
+                      (gnc:gnc-monetary-commodity price)
+                      (gnc-price-get-currency price))
+                  (gnc-numeric-mul (gnc:gnc-monetary-amount fromunits)
+                                   (if use-txn
+                                       (gnc:gnc-monetary-amount price)
+                                       (gnc-price-get-value price))
+                                   currency-frac GNC-RND-ROUND))
+                 tocurrency)
+                (exchange-fn fromunits tocurrency)))
+
+          (gnc:debug "Starting account " (xaccAccountGetName current) ", initial price: "
+                     (and price
+                          (gnc:monetary->string
+                           (gnc:make-gnc-monetary
+                            (gnc-price-get-currency price) (gnc-price-get-value price)))))
+
+          ;; If we have a price that can't be converted to the report currency
+          ;; don't use it
+          (if (and price (zero? (gnc:gnc-monetary-amount
+                                 (exchange-fn
+                                  (gnc:make-gnc-monetary
+                                   (gnc-price-get-currency price)
+                                   100)
+                                  currency))))
+              (set! price #f))
+
+          ;; If we are told to use a pricing transaction, or if we don't have a price
+          ;; from the price DB, find a good transaction to use.
+          (if (and (not use-txn)
+                   (or (not price) (not prefer-pricelist)))
+              (let ((split-list (reverse (gnc:get-match-commodity-splits-sorted
+                                          (list current)
+                                          (case price-source
+                                            ((pricedb-latest) (gnc:get-today))
+                                            ((pricedb-nearest) to-date)
+                                            (else (gnc:get-today)))  ;; error, but don't crash
+                                          #f))))  ;; Any currency
+                ;; Find the first (most recent) one that can be converted to report currency
+                (while (and (not use-txn) (not (eqv? split-list '())))
+                  (let ((split (car split-list)))
+                    (if (and (not (gnc-numeric-zero-p (xaccSplitGetAmount split)))
+                             (not (gnc-numeric-zero-p (xaccSplitGetValue split))))
+                        (let* ((trans (xaccSplitGetParent split))
+                               (trans-currency (xaccTransGetCurrency trans))
+                               (trans-price (exchange-fn (gnc:make-gnc-monetary
+                                                          trans-currency
+                                                          (xaccSplitGetSharePrice split))
+                                                         currency)))
+                          (if (not (gnc-numeric-zero-p (gnc:gnc-monetary-amount trans-price)))
+                              ;; We can exchange the price from this transaction into the report currency
+                              (begin
+                                (if price (gnc-price-unref price))
+                                (set! pricing-txn trans)
+                                (set! price trans-price)
+                                (gnc:debug "Transaction price is " (gnc:monetary->string price))
+                                (set! use-txn #t))
+                              (set! split-list (cdr split-list))))
+                        (set! split-list (cdr split-list)))
+                    ))))
+
+          ;; If we still don't have a price, use a price of 1 and complain later
+          (if (not price)
+              (begin
+                (set! price (gnc:make-gnc-monetary currency 1/1))
+                ;; If use-txn is set, but pricing-txn isn't set, it's a bogus price
+                (set! use-txn #t)
+                (set! pricing-txn #f)
+                )
+              )
+
+          ;; Now that we have a pricing transaction if needed, set the value of the asset
+          (set! value (my-exchange-fn (gnc:make-gnc-monetary commodity units) currency))
+          (gnc:debug "Value " (gnc:monetary->string value)
+                     " from " (gnc:monetary->string
+                               (gnc:make-gnc-monetary commodity units)))
+
+          (for-each
+           ;; we're looking at each split we find in the account. these splits
+           ;; could refer to the same transaction, so we have to examine each
+           ;; split, determine what kind of split it is and then act accordingly.
+           (lambda (split)
+             (set! work-done (+ 1 work-done))
+             (gnc:report-percent-done (* 100 (/ work-done work-to-do)))
+
+             (let* ((parent (xaccSplitGetParent split))
+                    (txn-date (xaccTransGetDate parent))
+                    (commod-currency (xaccTransGetCurrency parent))
+                    (commod-currency-frac (gnc-commodity-get-fraction commod-currency)))
+
+               (if (and (<= 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))
+                         (shares-bought (gnc-numeric-zero))
+                         (trans-sold (gnc-numeric-zero))
+                         (trans-bought (gnc-numeric-zero))
+                         (trans-spinoff (gnc-numeric-zero))
+                         (trans-drp-residual (gnc-numeric-zero))
+                         (trans-drp-account #f))
+
+                     (gnc:debug "Transaction " (xaccTransGetDescription parent))
+                     ;; 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)
+                        (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 (equal? current (xaccSplitGetAccount (xaccSplitGetOtherSplit s))))
+                                (set! trans-brokerage
+                                  (gnc-numeric-add trans-brokerage 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)))
+
+                           ((equal? 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 (spin-off? s current)
+                                    ;; Count money used in a spin off as money out
+                                    (if (gnc-numeric-negative-p split-value)
+                                        (set! trans-spinoff (gnc-numeric-sub trans-spinoff split-value
+                                                                             commod-currency-frac GNC-RND-ROUND)))
+                                    (if (not (gnc-numeric-zero-p split-value))
+                                        ;; Gain/loss split (amount zero, value non-zero, and not spinoff).  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)
+                                    (begin
+                                      (set! trans-bought
+                                        (gnc-numeric-add trans-bought split-value commod-currency-frac GNC-RND-ROUND))
+                                      (set! shares-bought
+                                        (gnc-numeric-add shares-bought split-units units-denom GNC-RND-ROUND)))
+                                    (set! trans-sold
+                                      (gnc-numeric-sub trans-sold split-value commod-currency-frac GNC-RND-ROUND)))))
+
+                           ((split-account-type? s ACCT-TYPE-ASSET)
+                            ;; If all the asset accounts mentioned in the transaction are siblings of each other
+                            ;; keep track of the money transferred to them if it is in the correct currency
+                            (if (not trans-drp-account)
+                                (begin
+                                  (set! trans-drp-account (xaccSplitGetAccount s))
+                                  (if (gnc-commodity-equiv commod-currency (xaccAccountGetCommodity trans-drp-account))
+                                      (set! trans-drp-residual split-value)
+                                      (set! trans-drp-account 'none)))
+                                (if (not (eq? trans-drp-account 'none))
+                                    (if (parent-or-sibling? trans-drp-account (xaccSplitGetAccount s))
+                                        (set! trans-drp-residual (gnc-numeric-add trans-drp-residual split-value
+                                                                                  commod-currency-frac GNC-RND-ROUND))
+                                        (set! trans-drp-account 'none))))))
+                          ))
+                      (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)
+                                " Shares bought: " (gnc-numeric-to-string shares-bought))
+                     (gnc:debug " Value sold: " (gnc-numeric-to-string trans-sold)
+                                " Value purchased: " (gnc-numeric-to-string trans-bought)
+                                " Spinoff value " (gnc-numeric-to-string trans-spinoff)
+                                " Trans DRP residual: " (gnc-numeric-to-string trans-drp-residual))
+
+                     ;; We need to calculate several things for this transaction:
+                     ;; 1. Total income: this is already in trans-income
+                     ;; 2. Change in basis: calculated by loop below that looks at every
+                     ;;    that acquires or disposes of shares
+                     ;; 3. Realized gain: also calculated below while calculating basis
+                     ;; 4. Money in to the account: this is the value of shares bought
+                     ;;    except those purchased with reinvested income
+                     ;; 5. Money out: the money received by disposing of shares.   This
+                     ;;    is in trans-sold plus trans-spinoff
+                     ;; 6. Brokerage fees: this is in trans-brokerage
+
+                     ;; 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)
+
+                     ;; Add brokerage fees to trans-bought if not ignoring them and there are any
+                     (if (and (not (eq? handle-brokerage-fees 'ignore-brokerage))
+                              (gnc-numeric-positive-p trans-brokerage)
+                              (gnc-numeric-positive-p trans-shares))
+                         (let* ((fee-frac (gnc-numeric-div shares-bought trans-shares GNC-DENOM-AUTO GNC-DENOM-REDUCE))
+                                (fees (gnc-numeric-mul trans-brokerage fee-frac commod-currency-frac GNC-RND-ROUND)))
+                           (set! trans-bought (gnc-numeric-add trans-bought fees commod-currency-frac GNC-RND-ROUND))))
+
+                     ;; Update the running total of the money in the DRP residual account.  This is relevant
+                     ;; if this is a reinvestment transaction (both income and purchase) and there seems to
+                     ;; asset accounts used to hold excess income.
+                     (if (and trans-drp-account
+                              (not (eq? trans-drp-account 'none))
+                              (gnc-numeric-positive-p trans-income)
+                              (gnc-numeric-positive-p trans-bought))
+                         (if (not drp-holding-account)
+                             (begin
+                               (set! drp-holding-account trans-drp-account)
+                               (set! drp-holding-amount trans-drp-residual))
+                             (if (and (not (eq? drp-holding-account 'none))
+                                      (parent-or-sibling? trans-drp-account drp-holding-account))
+                                 (set! drp-holding-amount (gnc-numeric-add drp-holding-amount trans-drp-residual
+                                                                           commod-currency-frac GNC-RND-ROUND))
+                                 (begin
+                                   ;; Wrong account (or no account), assume there isn't a DRP holding account
+                                   (set! drp-holding-account 'none)
+                                   (set trans-drp-residual (gnc-numeric-zero))
+                                   (set! drp-holding-amount (gnc-numeric-zero))))))
+
+                     ;; Set trans-bought to the amount of money moved in to the account which was used to
+                     ;; purchase more shares.  If this is not a DRP transaction then all money used to purchase
+                     ;; shares is money in.
+                     (if (and (gnc-numeric-positive-p trans-income)
+                              (gnc-numeric-positive-p trans-bought))
+                         (begin
+                           (set! trans-bought (gnc-numeric-sub trans-bought trans-income
+                                                               commod-currency-frac GNC-RND-ROUND))
+                           (set! trans-bought (gnc-numeric-add trans-bought trans-drp-residual
+                                                               commod-currency-frac GNC-RND-ROUND))
+                           (set! trans-bought (gnc-numeric-sub trans-bought drp-holding-amount
+                                                               commod-currency-frac GNC-RND-ROUND))
+                           ;; If the DRP holding account balance is negative, adjust it by the amount
+                           ;; used in this transaction
+                           (if (and (gnc-numeric-negative-p drp-holding-amount)
+                                    (gnc-numeric-positive-p trans-bought))
+                               (set! drp-holding-amount (gnc-numeric-add drp-holding-amount trans-bought
+                                                                         commod-currency-frac GNC-RND-ROUND)))
+                           ;; Money in is never more than amount spent to purchase shares
+                           (if (gnc-numeric-negative-p trans-bought)
+                               (set! trans-bought (gnc-numeric-zero)))))
+
+                     (gnc:debug "Adjusted trans-bought " (gnc-numeric-to-string trans-bought)
+                                " DRP holding account " (gnc-numeric-to-string drp-holding-amount))
+
+                     (moneyincoll 'add commod-currency trans-bought)
+                     (moneyoutcoll 'add commod-currency trans-sold)
+                     (moneyoutcoll 'add commod-currency trans-spinoff)
+
+                     ;; 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))
+                                 (equal? 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-DENOM-REDUCE))
+                                   ;; 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)
+           )
+
+          ;; Look for income and expense transactions that don't have a split in the
+          ;; the account we're processing.  We do this as follow
+          ;; 1. Make sure the parent account is a currency-valued asset or bank account
+          ;; 2. If so go through all the splits in that account
+          ;; 3. If a split is part of a two split transaction where the other split is
+          ;;    to an income or expense account and the leaf name of that account is the
+          ;;    same as the leaf name of the account we're processing, add it to the
+          ;;    income or expense accumulator
+          ;;
+          ;; In other words with an account structure like
+          ;;
+          ;;   Assets (type ASSET)
+          ;;     Broker (type ASSET)
+          ;;       Widget Stock (type STOCK)
+          ;;   Income (type INCOME)
+          ;;     Dividends (type INCOME)
+          ;;       Widget Stock (type INCOME)
+          ;;
+          ;; If you are producing a report on "Assets:Broker:Widget Stock" a
+          ;; transaction that debits the Assets:Broker account and credits the
+          ;; "Income:Dividends:Widget Stock" account will count as income in
+          ;; the report even though it doesn't have a split in the account
+          ;; being reported on.
+
+          (let ((parent-account (gnc-account-get-parent current))
+                (account-name (xaccAccountGetName current)))
+            (if (and (not (null? parent-account))
+                     (member (xaccAccountGetType parent-account) (list ACCT-TYPE-ASSET ACCT-TYPE-BANK))
+                     (gnc-commodity-is-currency (xaccAccountGetCommodity parent-account)))
+                (for-each
+                 (lambda (split)
+                   (let* ((other-split (xaccSplitGetOtherSplit split))
+                          ;; This is safe because xaccSplitGetAccount returns null for a null split
+                          (other-acct (xaccSplitGetAccount other-split))
+                          (parent (xaccSplitGetParent split))
+                          (txn-date (xaccTransGetDate parent)))
+                     (if (and (not (null? other-acct))
+                              (<= txn-date to-date)
+                              (string=? (xaccAccountGetName other-acct) account-name)
+                              (gnc-commodity-is-currency (xaccAccountGetCommodity other-acct)))
+                         ;; This is a two split transaction where the other split is to an
+                         ;; account with the same name as the current account.  If it's an
+                         ;; income or expense account accumulate the value of the transaction
+                         (let ((val (xaccSplitGetValue split))
+                               (curr (xaccAccountGetCommodity other-acct)))
+                           (cond ((split-account-type? other-split ACCT-TYPE-INCOME)
+                                  (gnc:debug "More income " (gnc-numeric-to-string val))
+                                  (dividendcoll 'add curr val))
+                                 ((split-account-type? other-split ACCT-TYPE-EXPENSE)
+                                  (gnc:debug "More expense " (gnc-numeric-to-string
+                                                              (gnc-numeric-neg val)))
+                                  (brokeragecoll 'add curr (gnc-numeric-neg val)))
+                                 )
+                           )
+                         )
+                     )
+                   )
+                 (xaccAccountGetSplitList parent-account)
+                 )
+                )
+            )
+
+          (gnc:debug "pricing txn is " pricing-txn)
+          (gnc:debug "use txn is " use-txn)
+          (gnc:debug "prefer-pricelist is " prefer-pricelist)
+          (gnc:debug "price is " price)
+
+          (gnc:debug "basis we're using to build rows is " (gnc-numeric-to-string (sum-basis basis-list
+                                                                                             currency-frac)))
+          (gnc:debug "but the actual basis list is " basis-list)
+
+          (if (eq? handle-brokerage-fees 'include-in-gain)
+              (gaincoll 'minusmerge brokeragecoll #f))
+
+          (if (or include-empty (not (gnc-numeric-zero-p units)))
+              (let* ((moneyin (gnc:sum-collector-commodity moneyincoll currency my-exchange-fn))
+                     (moneyout (gnc:sum-collector-commodity moneyoutcoll currency my-exchange-fn))
+                     (brokerage (gnc:sum-collector-commodity brokeragecoll currency my-exchange-fn))
+                     (income (gnc:sum-collector-commodity dividendcoll currency my-exchange-fn))
+                     ;; just so you know, gain == realized gain, ugain == un-realized gain, bothgain, well..
+                     (gain (gnc:sum-collector-commodity gaincoll currency my-exchange-fn))
+                     (ugain (gnc:make-gnc-monetary currency
+                                                   (gnc-numeric-sub (gnc:gnc-monetary-amount (my-exchange-fn value currency))
+                                                                    (sum-basis basis-list (gnc-commodity-get-fraction currency))
+                                                                    currency-frac GNC-RND-ROUND)))
+                     (bothgain (gnc:make-gnc-monetary currency  (gnc-numeric-add (gnc:gnc-monetary-amount gain)
+                                                                                 (gnc:gnc-monetary-amount ugain)
+                                                                                 currency-frac GNC-RND-ROUND)))
+                     (totalreturn (gnc:make-gnc-monetary currency (gnc-numeric-add (gnc:gnc-monetary-amount bothgain)
+                                                                                   (gnc:gnc-monetary-amount income)
+                                                                                   currency-frac GNC-RND-ROUND)))
+
+                     (activecols (list (gnc:html-account-anchor current)))
+                     )
+
+                ;; If we're using the txn, warn the user
+                (if use-txn
+                    (if pricing-txn
+                        (set! warn-price-dirty #t)
+                        (set! warn-no-price #t)
+                        ))
+
+                (total-value 'add (gnc:gnc-monetary-commodity value) (gnc:gnc-monetary-amount value))
+                (total-moneyin 'merge moneyincoll #f)
+                (total-moneyout 'merge moneyoutcoll #f)
+                (total-brokerage 'merge brokeragecoll #f)
+                (total-income 'merge dividendcoll #f)
+                (total-gain 'merge gaincoll #f)
+                (total-ugain 'add (gnc:gnc-monetary-commodity ugain) (gnc:gnc-monetary-amount ugain))
+                (total-basis 'add currency (sum-basis basis-list currency-frac))
+
+                ;; build a list for the row  based on user selections
+                (if show-symbol (append! activecols (list (gnc:make-html-table-header-cell/markup "text-cell" ticker-symbol))))
+                (if show-listing (append! activecols (list (gnc:make-html-table-header-cell/markup "text-cell" listing))))
+                (if show-shares (append! activecols (list (gnc:make-html-table-header-cell/markup
+                                                           "number-cell" (xaccPrintAmount units share-print-info)))))
+                (if show-price (append! activecols (list (gnc:make-html-table-header-cell/markup
+                                                          "number-cell"
+                                                          (if use-txn
+                                                              (if pricing-txn
+                                                                  (gnc:html-transaction-anchor pricing-txn price)
+                                                                  price)
+                                                              (gnc:html-price-anchor
+                                                               price (gnc:default-price-renderer
+                                                                      (gnc-price-get-currency price)
+                                                                      (gnc-price-get-value price))))))))
+                (append! activecols (list (if use-txn (if pricing-txn "*" "**") " ")
+                                          (gnc:make-html-table-header-cell/markup
+                                           "number-cell" (gnc:make-gnc-monetary currency (sum-basis basis-list
+                                                                                                    currency-frac)))
+                                          (gnc:make-html-table-header-cell/markup "number-cell" value)
+                                          (gnc:make-html-table-header-cell/markup "number-cell" moneyin)
+                                          (gnc:make-html-table-header-cell/markup "number-cell" moneyout)
+                                          (gnc:make-html-table-header-cell/markup "number-cell" gain)
+                                          (gnc:make-html-table-header-cell/markup "number-cell" ugain)
+                                          (gnc:make-html-table-header-cell/markup "number-cell" bothgain)
+                                          (gnc:make-html-table-header-cell/markup "number-cell"
+                                                                                  (let* ((moneyinvalue (gnc-numeric-to-double
+                                                                                                        (gnc:gnc-monetary-amount moneyin)))
+                                                                                         (bothgainvalue (gnc-numeric-to-double
+                                                                                                         (gnc:gnc-monetary-amount bothgain)))
+                                                                                         )
+                                                                                    (if (= 0.0 moneyinvalue)
+                                                                                        ""
+                                                                                        (format #f "~,2f%" (* 100 (/ bothgainvalue moneyinvalue)))))
+                                                                                  )
+                                          (gnc:make-html-table-header-cell/markup "number-cell" income)))
+                (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"
+                                                                                  (let* ((moneyinvalue (gnc-numeric-to-double
+                                                                                                        (gnc:gnc-monetary-amount moneyin)))
+                                                                                         (totalreturnvalue (gnc-numeric-to-double
+                                                                                                            (gnc:gnc-monetary-amount totalreturn)))
+                                                                                         )
+                                                                                    (if (= 0.0 moneyinvalue)
+                                                                                        ""
+                                                                                        (format #f "~,2f%" (* 100 (/ totalreturnvalue moneyinvalue))))))
+                                          )
+                         )
+
+                (gnc:html-table-append-row/markup!
+                 table
+                 row-style
+                 activecols)
+
+                (if (and (not use-txn) price) (gnc-price-unref price))
+                (table-add-stock-rows-internal rest (not odd-row?))
+                )
+              (begin
+                (if (and (not use-txn) price) (gnc-price-unref price))
+                (table-add-stock-rows-internal rest odd-row?)
+                )
+              )
+          )))
+
+  (set! work-to-do (gnc:accounts-count-splits accounts))
+  (table-add-stock-rows-internal accounts #t))
 
 ;; This is the rendering function. It accepts a database of options
 ;; and generates an object of type <html-document>.  See the file
@@ -285,9 +881,7 @@ by preventing negative stock balances.<br/>")
 
 (define (advanced-portfolio-renderer report-obj)
 
- (let ((work-done 0)
-       (work-to-do 0)
-       (warn-no-price #f)
+ (let ((warn-no-price #f)
        (warn-price-dirty #f))
 
   ;; These are some helper functions for looking up option values.
@@ -343,593 +937,6 @@ by preventing negative stock balances.<br/>")
                (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 handle-brokerage-fees
-                                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
-	   (inexact->exact (get-option gnc:pagename-display
-      			       optname-shares-digits)))))
-
-    (define (table-add-stock-rows-internal accounts odd-row?)
-      (if (null? accounts) total-value
-          (let* ((row-style (if odd-row? "normal-row" "alternate-row"))
-                 (current (car accounts))
-                 (rest (cdr accounts))
-		 ;; commodity is the actual stock/thing we are looking at
-                 (commodity (xaccAccountGetCommodity current))
-                 (ticker-symbol (gnc-commodity-get-mnemonic commodity))
-                 (listing (gnc-commodity-get-namespace commodity))
-                 (unit-collector (gnc:account-get-comm-balance-at-date
-                                  current to-date #f))
-                 (units (cadr (unit-collector 'getpair commodity #f)))
-
-                 ;; Counter to keep track of stuff
-                 (brokeragecoll (gnc:make-commodity-collector))
-                 (dividendcoll  (gnc:make-commodity-collector))
-                 (moneyincoll   (gnc:make-commodity-collector))
-                 (moneyoutcoll  (gnc:make-commodity-collector))
-                 (gaincoll      (gnc:make-commodity-collector))
-
-
-		 ;; the price of the commodity at the time of the report
-                 (price (price-fn commodity currency to-date))
-		 ;; the value of the commodity, expressed in terms of
-		 ;; the report's currency.
-                 (value (gnc:make-gnc-monetary currency (gnc-numeric-zero)))  ;; Set later
-                 (currency-frac (gnc-commodity-get-fraction currency))
-
-		 (pricing-txn #f)
-		 (use-txn #f)
-		 (basis-list '())
-		 ;; setup an alist for the splits we've already seen.
-		 (seen_trans '())
-		 ;; Account used to hold remainders from income reinvestments and
-		 ;; running total of amount moved there
-		 (drp-holding-account #f)
-		 (drp-holding-amount (gnc-numeric-zero))
-		 )
-
-            (define (my-exchange-fn fromunits tocurrency)
-              (if (and (gnc-commodity-equiv currency tocurrency)
-                       (gnc-commodity-equiv (gnc:gnc-monetary-commodity fromunits) commodity))
-                    ;; Have a price for this commodity, but not necessarily in the report's
-                    ;; currency.  Get the value in the commodity's currency and convert it to
-                    ;; report currency.
-                    (exchange-fn
-                      ;; This currency will usually be the same as tocurrency so the
-                      ;; call to exchange-fn below will do nothing
-                      (gnc:make-gnc-monetary
-                        (if use-txn
-                            (gnc:gnc-monetary-commodity price)
-                            (gnc-price-get-currency price))
-                        (gnc-numeric-mul (gnc:gnc-monetary-amount fromunits)
-                                         (if use-txn
-                                             (gnc:gnc-monetary-amount price)
-                                             (gnc-price-get-value price))
-                                         currency-frac GNC-RND-ROUND))
-                      tocurrency)
-                    (exchange-fn fromunits tocurrency)))
-
-            (gnc:debug "Starting account " (xaccAccountGetName current) ", initial price: "
-                       (and price
-                            (gnc:monetary->string
-                             (gnc:make-gnc-monetary
-                              (gnc-price-get-currency price) (gnc-price-get-value price)))))
-
-            ;; If we have a price that can't be converted to the report currency
-            ;; don't use it
-            (if (and price (gnc-numeric-zero-p (gnc:gnc-monetary-amount
-                                       (exchange-fn
-                                          (gnc:make-gnc-monetary
-                                            (gnc-price-get-currency price)
-                                            100/1)
-                                          currency))))
-                (set! price #f))
-
-            ;; If we are told to use a pricing transaction, or if we don't have a price
-            ;; from the price DB, find a good transaction to use.
-            (if (and (not use-txn)
-                     (or (not price) (not prefer-pricelist)))
-                  (let ((split-list (reverse (gnc:get-match-commodity-splits-sorted
-                                                 (list current)
-                                                 (case price-source
-                                                   ((pricedb-latest) (gnc:get-today))
-                                                   ((pricedb-nearest) to-date)
-                                                   (else (gnc:get-today)))  ;; error, but don't crash
-                                                 #f))))  ;; Any currency
-                        ;; Find the first (most recent) one that can be converted to report currency
-                        (while (and (not use-txn) (not (eqv? split-list '())))
-                          (let ((split (car split-list)))
-                            (if (and (not (gnc-numeric-zero-p (xaccSplitGetAmount split)))
-                                     (not (gnc-numeric-zero-p (xaccSplitGetValue split))))
-                              (let* ((trans (xaccSplitGetParent split))
-                                     (trans-currency (xaccTransGetCurrency trans))
-                                     (trans-price (exchange-fn (gnc:make-gnc-monetary
-                                                                   trans-currency
-                                                                   (xaccSplitGetSharePrice split))
-                                                               currency)))
-                                (if (not (gnc-numeric-zero-p (gnc:gnc-monetary-amount trans-price)))
-                                  ;; We can exchange the price from this transaction into the report currency
-                                  (begin
-                                    (if price (gnc-price-unref price))
-                                    (set! pricing-txn trans)
-                                    (set! price trans-price)
-                                    (gnc:debug "Transaction price is " (gnc:monetary->string price))
-                                    (set! use-txn #t))
-                                  (set! split-list (cdr split-list))))
-                              (set! split-list (cdr split-list)))
-                            ))))
-
-            ;; If we still don't have a price, use a price of 1 and complain later
-            (if (not price)
-              (begin
-                (set! price (gnc:make-gnc-monetary currency 1/1))
-                ;; If use-txn is set, but pricing-txn isn't set, it's a bogus price
-                (set! use-txn #t)
-                (set! pricing-txn #f)
-              )
-            )
-
-            ;; Now that we have a pricing transaction if needed, set the value of the asset
-            (set! value (my-exchange-fn (gnc:make-gnc-monetary commodity units) currency))
-            (gnc:debug "Value " (gnc:monetary->string value)
-                       " from " (gnc:monetary->string
-                                 (gnc:make-gnc-monetary commodity units)))
-
-	    (for-each
-	     ;; we're looking at each split we find in the account. these splits
-	     ;; could refer to the same transaction, so we have to examine each
-	     ;; split, determine what kind of split it is and then act accordingly.
-	     (lambda (split)
-	       (set! work-done (+ 1 work-done))
-	       (gnc:report-percent-done (* 100 (/ work-done work-to-do)))
-
-	       (let* ((parent (xaccSplitGetParent split))
-		      (txn-date (xaccTransGetDate parent))
-		      (commod-currency (xaccTransGetCurrency parent))
-		      (commod-currency-frac (gnc-commodity-get-fraction commod-currency)))
-
-		 (if (and (<= 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))
-		           (shares-bought (gnc-numeric-zero))
-		           (trans-sold (gnc-numeric-zero))
-		           (trans-bought (gnc-numeric-zero))
-		           (trans-spinoff (gnc-numeric-zero))
-		           (trans-drp-residual (gnc-numeric-zero))
-		           (trans-drp-account #f))
-
-		       (gnc:debug "Transaction " (xaccTransGetDescription parent))
-		       ;; 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)
-                           (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))))
-
-                                ((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 (spin-off? s current)
-                                         ;; Count money used in a spin off as money out
-                                         (if (gnc-numeric-negative-p split-value)
-                                             (set! trans-spinoff (gnc-numeric-sub trans-spinoff split-value
-                                                                                  commod-currency-frac GNC-RND-ROUND)))
-                                         (if (not (gnc-numeric-zero-p split-value))
-                                              ;; Gain/loss split (amount zero, value non-zero, and not spinoff).  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)
-                                          (begin
-                                             (set! trans-bought
-                                                  (gnc-numeric-add trans-bought split-value commod-currency-frac GNC-RND-ROUND))
-                                             (set! shares-bought
-                                                  (gnc-numeric-add shares-bought split-units units-denom GNC-RND-ROUND)))
-                                          (set! trans-sold
-                                               (gnc-numeric-sub trans-sold split-value commod-currency-frac GNC-RND-ROUND)))))
-
-                                ((split-account-type? s ACCT-TYPE-ASSET)
-                                 ;; If all the asset accounts mentioned in the transaction are siblings of each other
-                                 ;; keep track of the money transferred to them if it is in the correct currency
-                                 (if (not trans-drp-account)
-                                     (begin
-                                       (set! trans-drp-account (xaccSplitGetAccount s))
-                                         (if (gnc-commodity-equiv commod-currency (xaccAccountGetCommodity trans-drp-account))
-                                             (set! trans-drp-residual split-value)
-                                             (set! trans-drp-account 'none)))
-                                     (if (not (eq? trans-drp-account 'none))
-                                       (if (parent-or-sibling? trans-drp-account (xaccSplitGetAccount s))
-                                           (set! trans-drp-residual (gnc-numeric-add trans-drp-residual split-value
-                                                                                     commod-currency-frac GNC-RND-ROUND))
-                                           (set! trans-drp-account 'none))))))
-		         ))
-		         (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)
-		                  " Shares bought: " (gnc-numeric-to-string shares-bought))
-		       (gnc:debug " Value sold: " (gnc-numeric-to-string trans-sold)
-		                  " Value purchased: " (gnc-numeric-to-string trans-bought)
-		                  " Spinoff value " (gnc-numeric-to-string trans-spinoff)
-		                  " Trans DRP residual: " (gnc-numeric-to-string trans-drp-residual))
-
-		       ;; We need to calculate several things for this transaction:
-		       ;; 1. Total income: this is already in trans-income
-		       ;; 2. Change in basis: calculated by loop below that looks at every
-		       ;;    that acquires or disposes of shares
-		       ;; 3. Realized gain: also calculated below while calculating basis
-		       ;; 4. Money in to the account: this is the value of shares bought
-		       ;;    except those purchased with reinvested income
-		       ;; 5. Money out: the money received by disposing of shares.   This
-		       ;;    is in trans-sold plus trans-spinoff
-		       ;; 6. Brokerage fees: this is in trans-brokerage
-
-		       ;; 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)
-
-                       ;; Add brokerage fees to trans-bought if not ignoring them and there are any
-                       (if (and (not (eq? handle-brokerage-fees 'ignore-brokerage))
-                                (gnc-numeric-positive-p trans-brokerage)
-                                (gnc-numeric-positive-p trans-shares))
-                           (let* ((fee-frac (gnc-numeric-div shares-bought trans-shares GNC-DENOM-AUTO GNC-DENOM-REDUCE))
-                                  (fees (gnc-numeric-mul trans-brokerage fee-frac commod-currency-frac GNC-RND-ROUND)))
-                                 (set! trans-bought (gnc-numeric-add trans-bought fees commod-currency-frac GNC-RND-ROUND))))
-
-                       ;; Update the running total of the money in the DRP residual account.  This is relevant
-                       ;; if this is a reinvestment transaction (both income and purchase) and there seems to
-                       ;; asset accounts used to hold excess income.
-                       (if (and trans-drp-account
-                                (not (eq? trans-drp-account 'none))
-                                (gnc-numeric-positive-p trans-income)
-                                (gnc-numeric-positive-p trans-bought))
-                           (if (not drp-holding-account)
-                               (begin
-                                 (set! drp-holding-account trans-drp-account)
-                                 (set! drp-holding-amount trans-drp-residual))
-                               (if (and (not (eq? drp-holding-account 'none))
-                                        (parent-or-sibling? trans-drp-account drp-holding-account))
-                                   (set! drp-holding-amount (gnc-numeric-add drp-holding-amount trans-drp-residual
-                                                                              commod-currency-frac GNC-RND-ROUND))
-                                   (begin
-                                     ;; Wrong account (or no account), assume there isn't a DRP holding account
-                                     (set! drp-holding-account 'none)
-                                     (set trans-drp-residual (gnc-numeric-zero))
-                                     (set! drp-holding-amount (gnc-numeric-zero))))))
-
-                       ;; Set trans-bought to the amount of money moved in to the account which was used to
-                       ;; purchase more shares.  If this is not a DRP transaction then all money used to purchase
-                       ;; shares is money in.
-                       (if (and (gnc-numeric-positive-p trans-income)
-                                (gnc-numeric-positive-p trans-bought))
-                           (begin
-                             (set! trans-bought (gnc-numeric-sub trans-bought trans-income
-                                                                 commod-currency-frac GNC-RND-ROUND))
-                             (set! trans-bought (gnc-numeric-add trans-bought trans-drp-residual
-                                                                 commod-currency-frac GNC-RND-ROUND))
-                             (set! trans-bought (gnc-numeric-sub trans-bought drp-holding-amount
-                                                                 commod-currency-frac GNC-RND-ROUND))
-                             ;; If the DRP holding account balance is negative, adjust it by the amount
-                             ;; used in this transaction
-                             (if (and (gnc-numeric-negative-p drp-holding-amount)
-                                      (gnc-numeric-positive-p trans-bought))
-                                 (set! drp-holding-amount (gnc-numeric-add drp-holding-amount trans-bought
-                                                                           commod-currency-frac GNC-RND-ROUND)))
-                             ;; Money in is never more than amount spent to purchase shares
-                             (if (gnc-numeric-negative-p trans-bought)
-                                 (set! trans-bought (gnc-numeric-zero)))))
-
-                       (gnc:debug "Adjusted trans-bought " (gnc-numeric-to-string trans-bought)
-                                  " DRP holding account " (gnc-numeric-to-string drp-holding-amount))
-
-                       (moneyincoll 'add commod-currency trans-bought)
-                       (moneyoutcoll 'add commod-currency trans-sold)
-                       (moneyoutcoll 'add commod-currency trans-spinoff)
-
-                       ;; 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-DENOM-REDUCE))
-			               ;; 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)
-	     )
-
-	    ;; Look for income and expense transactions that don't have a split in the
-	    ;; the account we're processing.  We do this as follow
-	    ;; 1. Make sure the parent account is a currency-valued asset or bank account
-	    ;; 2. If so go through all the splits in that account
-	    ;; 3. If a split is part of a two split transaction where the other split is
-	    ;;    to an income or expense account and the leaf name of that account is the
-	    ;;    same as the leaf name of the account we're processing, add it to the
-	    ;;    income or expense accumulator
-	    ;;
-	    ;; In other words with an account structure like
-	    ;;
-	    ;;   Assets (type ASSET)
-	    ;;     Broker (type ASSET)
-	    ;;       Widget Stock (type STOCK)
-	    ;;   Income (type INCOME)
-	    ;;     Dividends (type INCOME)
-	    ;;       Widget Stock (type INCOME)
-	    ;;
-	    ;; If you are producing a report on "Assets:Broker:Widget Stock" a
-	    ;; transaction that debits the Assets:Broker account and credits the
-	    ;; "Income:Dividends:Widget Stock" account will count as income in
-	    ;; the report even though it doesn't have a split in the account
-	    ;; being reported on.
-
-	    (let ((parent-account (gnc-account-get-parent current))
-	          (account-name (xaccAccountGetName current)))
-	      (if (and (not (null? parent-account))
-	               (member (xaccAccountGetType parent-account) (list ACCT-TYPE-ASSET ACCT-TYPE-BANK))
-	               (gnc-commodity-is-currency (xaccAccountGetCommodity parent-account)))
-	        (for-each
-	          (lambda (split)
-	            (let* ((other-split (xaccSplitGetOtherSplit split))
-	                   ;; This is safe because xaccSplitGetAccount returns null for a null split
-	                   (other-acct (xaccSplitGetAccount other-split))
-	                   (parent (xaccSplitGetParent split))
-	                   (txn-date (xaccTransGetDate parent)))
-	              (if (and (not (null? other-acct))
-	                       (<= txn-date to-date)
-	                       (string=? (xaccAccountGetName other-acct) account-name)
-	                       (gnc-commodity-is-currency (xaccAccountGetCommodity other-acct)))
-	                ;; This is a two split transaction where the other split is to an
-	                ;; account with the same name as the current account.  If it's an
-	                ;; income or expense account accumulate the value of the transaction
-	                (let ((val (xaccSplitGetValue split))
-	                      (curr (xaccAccountGetCommodity other-acct)))
-                          (cond ((split-account-type? other-split ACCT-TYPE-INCOME)
-	                         (gnc:debug "More income " (gnc-numeric-to-string val))
-	                         (dividendcoll 'add curr val))
-                                ((split-account-type? other-split ACCT-TYPE-EXPENSE)
-                                 (gnc:debug "More expense " (gnc-numeric-to-string
-                                                             (gnc-numeric-neg val)))
-                                 (brokeragecoll 'add curr (gnc-numeric-neg val)))
-	                  )
-	                )
-	              )
-	            )
-	          )
-	          (xaccAccountGetSplitList parent-account)
-	        )
-	      )
-	    )
-
-	    (gnc:debug "pricing txn is " pricing-txn)
-	    (gnc:debug "use txn is " use-txn)
-	    (gnc:debug "prefer-pricelist is " prefer-pricelist)
-	    (gnc:debug "price is " price)
-
-	    (gnc:debug "basis we're using to build rows is " (gnc-numeric-to-string (sum-basis basis-list
-	                                                            currency-frac)))
-	    (gnc:debug "but the actual basis list is " basis-list)
-
-            (if (eq? handle-brokerage-fees 'include-in-gain)
-	      (gaincoll 'minusmerge brokeragecoll #f))
-
-	  (if (or include-empty (not (gnc-numeric-zero-p units)))
-	    (let* ((moneyin (gnc:sum-collector-commodity moneyincoll currency my-exchange-fn))
-		  (moneyout (gnc:sum-collector-commodity moneyoutcoll currency my-exchange-fn))
-                  (brokerage (gnc:sum-collector-commodity brokeragecoll currency my-exchange-fn))
-		  (income (gnc:sum-collector-commodity dividendcoll currency my-exchange-fn))
-		  ;; just so you know, gain == realized gain, ugain == un-realized gain, bothgain, well..
-		  (gain (gnc:sum-collector-commodity gaincoll currency my-exchange-fn))
-		  (ugain (gnc:make-gnc-monetary currency
-						(gnc-numeric-sub (gnc:gnc-monetary-amount (my-exchange-fn value currency))
-								 (sum-basis basis-list (gnc-commodity-get-fraction currency))
-								 currency-frac GNC-RND-ROUND)))
-		  (bothgain (gnc:make-gnc-monetary currency  (gnc-numeric-add (gnc:gnc-monetary-amount gain)
-									      (gnc:gnc-monetary-amount ugain)
-									      currency-frac GNC-RND-ROUND)))
-		  (totalreturn (gnc:make-gnc-monetary currency (gnc-numeric-add (gnc:gnc-monetary-amount bothgain)
-										    (gnc:gnc-monetary-amount income)
-										currency-frac GNC-RND-ROUND)))
-
-		  (activecols (list (gnc:html-account-anchor current)))
-		  )
-
-              ;; If we're using the txn, warn the user
-              (if use-txn
-                  (if pricing-txn
-                      (set! warn-price-dirty #t)
-                      (set! warn-no-price #t)
-                  ))
-
-	      (total-value 'add (gnc:gnc-monetary-commodity value) (gnc:gnc-monetary-amount value))
-	      (total-moneyin 'merge moneyincoll #f)
-	      (total-moneyout 'merge moneyoutcoll #f)
-              (total-brokerage 'merge brokeragecoll #f)
-	      (total-income 'merge dividendcoll #f)
-	      (total-gain 'merge gaincoll #f)
-	      (total-ugain 'add (gnc:gnc-monetary-commodity ugain) (gnc:gnc-monetary-amount ugain))
-	      (total-basis 'add currency (sum-basis basis-list currency-frac))
-
-	      ;; build a list for the row  based on user selections
-	      (if show-symbol (append! activecols (list (gnc:make-html-table-header-cell/markup "text-cell" ticker-symbol))))
-	      (if show-listing (append! activecols (list (gnc:make-html-table-header-cell/markup "text-cell" listing))))
-	      (if show-shares (append! activecols (list (gnc:make-html-table-header-cell/markup
- 	        "number-cell" (xaccPrintAmount units share-print-info)))))
-	      (if show-price (append! activecols (list (gnc:make-html-table-header-cell/markup
-	        "number-cell"
-	        (if use-txn
-	            (if pricing-txn
-                        (gnc:html-transaction-anchor pricing-txn price)
-                        price)
-	 	    (gnc:html-price-anchor
-	 	     price (gnc:default-price-renderer
-                            (gnc-price-get-currency price)
-                            (gnc-price-get-value price))))))))
- 	      (append! activecols (list (if use-txn (if pricing-txn "*" "**") " ")
-					(gnc:make-html-table-header-cell/markup
-					 "number-cell" (gnc:make-gnc-monetary currency (sum-basis basis-list
-					                         currency-frac)))
-					(gnc:make-html-table-header-cell/markup "number-cell" value)
-					(gnc:make-html-table-header-cell/markup "number-cell" moneyin)
-					(gnc:make-html-table-header-cell/markup "number-cell" moneyout)
-					(gnc:make-html-table-header-cell/markup "number-cell" gain)
-					(gnc:make-html-table-header-cell/markup "number-cell" ugain)
-					(gnc:make-html-table-header-cell/markup "number-cell" bothgain)
-					(gnc:make-html-table-header-cell/markup "number-cell"
-					    (let* ((moneyinvalue (gnc-numeric-to-double
-								  (gnc:gnc-monetary-amount moneyin)))
-					           (bothgainvalue (gnc-numeric-to-double
-								   (gnc:gnc-monetary-amount bothgain)))
-                                             )
-					      (if (= 0.0 moneyinvalue)
-						  ""
-						  (format #f "~,2f%" (* 100 (/ bothgainvalue moneyinvalue)))))
-					)
-					(gnc:make-html-table-header-cell/markup "number-cell" income)))
-	      (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"
-					    (let* ((moneyinvalue (gnc-numeric-to-double
-								  (gnc:gnc-monetary-amount moneyin)))
-					           (totalreturnvalue (gnc-numeric-to-double
-								      (gnc:gnc-monetary-amount totalreturn)))
-                                             )
-					      (if (= 0.0 moneyinvalue)
-						  ""
-						  (format #f "~,2f%" (* 100 (/ totalreturnvalue moneyinvalue))))))
-					 )
-			)
-
-	      (gnc:html-table-append-row/markup!
-	       table
-	       row-style
-	       activecols)
-
-              (if (and (not use-txn) price) (gnc-price-unref price))
-	      (table-add-stock-rows-internal rest (not odd-row?))
-	      )
-	    (begin
-	      (if (and (not use-txn) price) (gnc-price-unref price))
-	      (table-add-stock-rows-internal rest odd-row?)
-	      )
-            )
-	    )))
-
-    (set! work-to-do (gnc:accounts-count-splits accounts))
-    (table-add-stock-rows-internal accounts #t)))
-
   ;; Tell the user that we're starting.
   (gnc:report-starting reportname)
 
@@ -961,8 +968,12 @@ by preventing negative stock balances.<br/>")
 				      optname-prefer-pricelist))
 	(handle-brokerage-fees (get-option gnc:pagename-general
 				  optname-brokerage-fees))
+        (share-print-info
+         (gnc-share-print-info-places
+          (inexact->exact
+           (get-option gnc:pagename-display optname-shares-digits))))
 
-	(total-basis (gnc:make-commodity-collector))
+        (total-basis (gnc:make-commodity-collector))
         (total-value    (gnc:make-commodity-collector))
         (total-moneyin  (gnc:make-commodity-collector))
         (total-moneyout (gnc:make-commodity-collector))
@@ -1050,7 +1061,8 @@ by preventing negative stock balances.<br/>")
                include-empty show-symbol show-listing show-shares show-price basis-method
                prefer-pricelist handle-brokerage-fees
                total-basis total-value total-moneyin total-moneyout
-               total-income total-gain total-ugain total-brokerage))
+               total-income total-gain total-ugain total-brokerage
+               share-print-info))
             (lambda (k reason)
               (gnc:html-document-add-object!
                document (format #f OVERFLOW-ERROR reason))))

commit 945c11e2b12fc19809ca1173e202987431789431
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Wed Jun 24 11:09:05 2020 +0800

    [advanced-portfolio] simplify basis functions

diff --git a/gnucash/report/reports/standard/advanced-portfolio.scm b/gnucash/report/reports/standard/advanced-portfolio.scm
index 249a7f3d7..2bcbebed7 100644
--- a/gnucash/report/reports/standard/advanced-portfolio.scm
+++ b/gnucash/report/reports/standard/advanced-portfolio.scm
@@ -184,27 +184,16 @@ by preventing negative stock balances.<br/>")
 
 ;; sum up the contents of the b-list built by basis-builder below
 (define (sum-basis b-list currency-frac)
-  (if (not (eqv? b-list '()))
-      (gnc-numeric-add (gnc-numeric-mul (caar b-list) (cdar b-list) currency-frac GNC-RND-ROUND)
-                       (sum-basis (cdr b-list) currency-frac) currency-frac GNC-RND-ROUND)
-      (gnc-numeric-zero)))
+  (fold (lambda (a b) (+ (* (car a) (cdr a)) b)) 0 b-list))
 
 ;; sum up the total number of units in the b-list built by
 ;; basis-builder below
 (define (units-basis b-list)
-  (if (not (eqv? b-list '()))
-      (gnc-numeric-add (caar b-list) (units-basis (cdr b-list))
-                       units-denom GNC-RND-ROUND)
-      (gnc-numeric-zero)))
+  (fold (lambda (a b) (+ (car a) b)) 0 b-list))
 
 ;; apply a ratio to an existing basis-list, useful for splits/mergers and spinoffs
-;; I need to get a brain and use (map) for this.
 (define (apply-basis-ratio b-list units-ratio value-ratio)
-  (if (not (eqv? b-list '()))
-      (cons (cons (gnc-numeric-mul units-ratio (caar b-list) units-denom GNC-RND-ROUND)
-                  (gnc-numeric-mul value-ratio (cdar b-list) price-denom GNC-RND-ROUND))
-            (apply-basis-ratio (cdr b-list) units-ratio value-ratio))
-      '()))
+  (map (lambda (a) (cons (* units-ratio (car a)) (* value-ratio (cdr a)))) b-list))
 
 ;; in: b-list: an alist of pair of (num-units . price-per-unit)
 ;;     b-units: units being sold - starts from first pair

commit c68f28286117cf57c104db7ebf7bfc7705c19612
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Wed Jun 24 10:36:00 2020 +0800

    [advanced-portfolio] simplify basis-builder
    
    and use scheme division which is more accurate than
    gnc_numeric_div. tests need to change slightly.

diff --git a/gnucash/report/reports/standard/advanced-portfolio.scm b/gnucash/report/reports/standard/advanced-portfolio.scm
index 2210045d5..249a7f3d7 100644
--- a/gnucash/report/reports/standard/advanced-portfolio.scm
+++ b/gnucash/report/reports/standard/advanced-portfolio.scm
@@ -33,6 +33,7 @@
 (use-modules (gnucash app-utils))
 (use-modules (gnucash report))
 (use-modules (srfi srfi-1))
+(use-modules (ice-9 match))
 
 (define reportname (N_ "Advanced Portfolio"))
 
@@ -205,6 +206,20 @@ by preventing negative stock balances.<br/>")
             (apply-basis-ratio (cdr b-list) units-ratio value-ratio))
       '()))
 
+;; in: b-list: an alist of pair of (num-units . price-per-unit)
+;;     b-units: units being sold - starts from first pair
+;; in:  '((4 . 2) (3 . 4)) -3     --> '((1 . 2) (3 . 4))
+;; in:  '((5 . 6) (4 . 5)) -8     --> '((1 . 5))
+(define (remove-from-head b-list b-units)
+  (match b-list
+    (() (gnc:warn "selling more than available units") '())
+    (((unit1 . value1) . rest)
+     (let ((units-left (+ b-units unit1)))
+       (cond
+        ((< 0 units-left) (cons (cons units-left value1) rest))
+        ((= 0 units-left) rest)
+        (else (remove-from-head rest units-left)))))))
+
 ;; this builds a list for basis calculation and handles average, fifo
 ;; and lifo methods the list is cons cells of (units-of-stock
 ;; . price-per-unit)... average method produces only one cell that
@@ -213,121 +228,62 @@ by preventing negative stock balances.<br/>")
 ;; price adjusted to carryover the basis.
 (define (basis-builder b-list b-units b-value b-method currency-frac)
   (gnc:debug "actually in basis-builder")
-  (gnc:debug "b-list is " b-list " b-units is " (gnc-numeric-to-string b-units)
-             " b-value is " (gnc-numeric-to-string b-value) " b-method is " b-method)
+  (gnc:debug "b-list is " b-list " b-units is " b-units
+             " b-value is " b-value " b-method is " b-method)
 
   ;; if there is no b-value, then this is a split/merger and needs
   ;; special handling
   (cond
 
    ;; we have value and positive units, add units to basis
-   ((and (not (gnc-numeric-zero-p b-value))
-         (gnc-numeric-positive-p b-units))
+   ((and (not (zero? b-value)) (positive? b-units))
     (case b-method
       ((average-basis)
-       (if (not (eqv? b-list '()))
-           (list (cons (gnc-numeric-add b-units
-                                        (caar b-list) units-denom GNC-RND-ROUND)
-                       (gnc-numeric-div
-                        (gnc-numeric-add b-value
-                                         (gnc-numeric-mul (caar b-list)
-                                                          (cdar b-list)
-                                                          GNC-DENOM-AUTO GNC-DENOM-REDUCE)
-                                         GNC-DENOM-AUTO GNC-DENOM-REDUCE)
-                        (let ((denom (gnc-numeric-add b-units
-                                                      (caar b-list) GNC-DENOM-AUTO GNC-DENOM-REDUCE)))
-                          (if (zero? denom)
-                              (throw 'div/0 (format #f "buying ~0,4f share units" b-units))
-                              denom))
-                        price-denom GNC-RND-ROUND)))
-           (append b-list
-                   (list (cons b-units (gnc-numeric-div
-                                        b-value b-units price-denom GNC-RND-ROUND))))))
-      (else (append b-list
-                    (list (cons b-units (gnc-numeric-div
-                                         b-value b-units price-denom GNC-RND-ROUND)))))))
+       (match b-list
+         (() (list (cons b-units (/ b-value b-units))))
+         (((unit1 . value1) . _)
+          (let ((new-units (+ b-units unit1))
+                (new-value (+ b-value (* unit1 value1))))
+            (if (zero? new-units)
+                (throw 'div/0 (format #f "buying ~0,4f share units" b-units))
+                (list (cons new-units (/ new-value new-units))))))))
+
+      (else (append b-list (list (cons b-units (/ b-value b-units)))))))
 
    ;; we have value and negative units, remove units from basis
-   ((and (not (gnc-numeric-zero-p b-value))
-         (gnc-numeric-negative-p b-units))
-    (if (not (eqv? b-list '()))
-        (case b-method
-          ((fifo-basis)
-           (case (gnc-numeric-compare (gnc-numeric-abs b-units) (caar b-list))
-             ((-1)
-              ;; Sold less than the first lot, create a new first lot
-              ;; from the remainder
-              (let ((new-units (gnc-numeric-add b-units (caar b-list) units-denom GNC-RND-ROUND)))
-                (cons (cons new-units (cdar b-list)) (cdr b-list))))
-             ((0)
-              ;; Sold all of the first lot
-              (cdr b-list))
-             ((1)
-              ;; Sold more than the first lot, delete it and recurse
-              (basis-builder (cdr b-list) (gnc-numeric-add b-units (caar b-list) units-denom GNC-RND-ROUND)
-                             ;; Only the sign of b-value matters since
-                             ;; the new b-units is negative
-                             b-value
-                             b-method currency-frac))))
-          ((filo-basis)
-           (let ((rev-b-list (reverse b-list)))
-             (case (gnc-numeric-compare (gnc-numeric-abs b-units) (caar rev-b-list))
-               ((-1)
-                ;; Sold less than the last lot
-                (let ((new-units (gnc-numeric-add b-units (caar rev-b-list) units-denom GNC-RND-ROUND)))
-                  (reverse (cons (cons new-units (cdar rev-b-list)) (cdr rev-b-list)))))
-               ((0)
-                ;; Sold all of the last lot
-                (reverse (cdr rev-b-list))
-                )
-               ((1)
-                ;; Sold more than the last lot
-                (basis-builder (reverse (cdr rev-b-list)) (gnc-numeric-add b-units (caar rev-b-list) units-denom GNC-RND-ROUND)
-                               b-value b-method currency-frac)))))
-          ((average-basis)
-           (list (cons (gnc-numeric-add
-                        (caar b-list) b-units units-denom GNC-RND-ROUND)
-                       (cdar b-list)))))
-        '()))
+   ((and (not (zero? b-value)) (negative? b-units))
+    (case b-method
+      ((fifo-basis) (remove-from-head b-list b-units))
+      ((filo-basis) (reverse (remove-from-head (reverse b-list) b-units)))
+      ((average-basis)
+       (match b-list
+         (() '())
+         (((unit1 . value1) . _) (list (cons (+ unit1 b-units) value1)))))))
 
    ;; no value, just units, this is a split/merge...
-   ((and (gnc-numeric-zero-p b-value)
-         (not (gnc-numeric-zero-p b-units)))
+   ((and (zero? b-value) (not (zero? b-units)))
     (let* ((current-units (units-basis b-list))
            ;; If current-units is zero then so should be everything else.
-           (units-ratio (if (zero? current-units) (gnc-numeric-zero)
-                            (gnc-numeric-div (gnc-numeric-add b-units current-units GNC-DENOM-AUTO GNC-DENOM-REDUCE)
-                                             current-units GNC-DENOM-AUTO GNC-DENOM-REDUCE)))
+           (units-ratio (if (zero? current-units) 0
+                            (/ (+ b-units current-units) current-units)))
            ;; If the units ratio is zero the stock is worthless and
            ;; the value should be zero too
-           (value-ratio (if (gnc-numeric-zero-p units-ratio)
-                            (gnc-numeric-zero)
-                            (gnc-numeric-div 1/1 units-ratio GNC-DENOM-AUTO GNC-DENOM-REDUCE))))
-
-      (gnc:debug "blist is " b-list " current units is "
-                 (gnc-numeric-to-string current-units)
-                 " value ratio is " (gnc-numeric-to-string value-ratio)
-                 " units ratio is " (gnc-numeric-to-string units-ratio))
+           (value-ratio (if (zero? units-ratio) 0 (/ 1 units-ratio))))
+      (gnc:debug "blist is " b-list " current units is " current-units
+                 " value ratio is " value-ratio " units ratio is " units-ratio)
       (apply-basis-ratio b-list units-ratio value-ratio)))
 
    ;; If there are no units, just a value, then its a spin-off,
    ;; calculate a ratio for the values, but leave the units alone
-   ;; with a ratio of 1
-   ((and (gnc-numeric-zero-p b-units)
-         (not (gnc-numeric-zero-p b-value)))
+   ((and (zero? b-units) (not (zero? b-value)))
     (let* ((current-value (sum-basis b-list GNC-DENOM-AUTO))
-           (value-ratio (if (zero? current-value)
-                            (throw 'div/0 (format #f "spinoff of ~,2f currency units" current-value))
-                            (gnc-numeric-div (gnc-numeric-add b-value current-value GNC-DENOM-AUTO GNC-DENOM-REDUCE)
-                                             current-value GNC-DENOM-AUTO GNC-DENOM-REDUCE))))
-
-      (gnc:debug "this is a spinoff")
-      (gnc:debug "blist is " b-list " value ratio is " (gnc-numeric-to-string value-ratio))
-      (apply-basis-ratio b-list 1/1 value-ratio)))
+           (value-ratio (if (zero? current-value) 0
+                            (/ (+ b-value current-value) current-value))))
+      (gnc:debug "spinoff: blist is " b-list " value ratio is " value-ratio)
+      (apply-basis-ratio b-list 1 value-ratio)))
 
    ;; when all else fails, just send the b-list back
-   (else
-    b-list)))
+   (else b-list)))
 
 
 
diff --git a/gnucash/report/reports/standard/test/test-portfolios.scm b/gnucash/report/reports/standard/test/test-portfolios.scm
index 84138072d..a5008983e 100644
--- a/gnucash/report/reports/standard/test/test-portfolios.scm
+++ b/gnucash/report/reports/standard/test/test-portfolios.scm
@@ -158,16 +158,16 @@
     (apply-basis-ratio basis2 2 3))
 
   (test-equal "basis-builder buy new units"
-    '((3 . 133333333/100000000))
+    '((3 . 4/3))
     (basis-builder '() 3 4 'average-basis 100))
   (test-equal "basis-builder buy new units average"
-    '((6 . 266666667/100000000))
+    '((6 . 8/3))
     (basis-builder '((3 . 4) (5 . 6) (7 . 8)) 3 4 'average-basis 100))
   (test-equal "basis-builder buy new units FIFO"
-    '((3 . 4) (5 . 6) (7 . 8) (3 . 133333333/100000000))
+    '((3 . 4) (5 . 6) (7 . 8) (3 . 4/3))
     (basis-builder '((3 . 4) (5 . 6) (7 . 8)) 3 4 'fifo-basis 100))
   (test-equal "basis-builder buy new units LIFO"
-    '((3 . 4) (5 . 6) (7 . 8) (3 . 133333333/100000000))
+    '((3 . 4) (5 . 6) (7 . 8) (3 . 4/3))
     (basis-builder '((3 . 4) (5 . 6) (7 . 8)) 3 4 'filo-basis 100))
 
   (test-equal "basis-builder sell average"



Summary of changes:
 .../report/reports/standard/advanced-portfolio.scm | 1353 ++++++++++----------
 .../reports/standard/test/test-portfolios.scm      |    8 +-
 2 files changed, 659 insertions(+), 702 deletions(-)



More information about the gnucash-changes mailing list