Unfortunately cleaning up advanced-portfolio.scm is much more challenging than I expected, so, I've now reverted it to its original (as of 4.0) state, with suitable changes to support guile-3.0 only.
On Sat, 1 Aug 2020 at 05:55, Christopher Lam <c...@code.gnucash.org> wrote: > Updated via https://github.com/Gnucash/gnucash/commit/12ab85fa (commit) > via https://github.com/Gnucash/gnucash/commit/6f196031 (commit) > from https://github.com/Gnucash/gnucash/commit/4df6493b (commit) > > > > commit 12ab85fa6c147df2714a14c778412f930f89ed40 > Author: Christopher Lam <christopher....@gmail.com> > Date: Sat Aug 1 10:12:38 2020 +0800 > > [advanced-portfolio] use G_ for guile-3.0 > > diff --git a/gnucash/report/reports/standard/advanced-portfolio.scm > b/gnucash/report/reports/standard/advanced-portfolio.scm > index 279fcb91f..192e97e63 100644 > --- a/gnucash/report/reports/standard/advanced-portfolio.scm > +++ b/gnucash/report/reports/standard/advanced-portfolio.scm > @@ -1048,8 +1048,8 @@ by preventing negative stock balances.<br/>") > (lambda (foreign domestic date) > (find-price > (gnc-pricedb-lookup-nearest-in-time-any-currency-t64 > pricedb foreign (time64CanonicalDayTime date)) > domestic))))) > - (headercols (list (_ "Account"))) > - (totalscols (list (gnc:make-html-table-cell/markup > "total-label-cell" (_ "Total")))) > + (headercols (list (G_ "Account"))) > + (totalscols (list (gnc:make-html-table-cell/markup > "total-label-cell" (G_ "Total")))) > (sum-total-moneyin (gnc-numeric-zero)) > (sum-total-income (gnc-numeric-zero)) > (sum-total-both-gains (gnc-numeric-zero)) > @@ -1060,37 +1060,37 @@ by preventing negative stock balances.<br/>") > > ;;begin building lists for which columns to display > (if show-symbol > - (begin (append! headercols (list (_ "Symbol"))) > + (begin (append! headercols (list (G_ "Symbol"))) > (append! totalscols (list " ")))) > > (if show-listing > - (begin (append! headercols (list (_ "Listing"))) > + (begin (append! headercols (list (G_ "Listing"))) > (append! totalscols (list " ")))) > > (if show-shares > - (begin (append! headercols (list (_ "Shares"))) > + (begin (append! headercols (list (G_ "Shares"))) > (append! totalscols (list " ")))) > > (if show-price > - (begin (append! headercols (list (_ "Price"))) > + (begin (append! headercols (list (G_ "Price"))) > (append! totalscols (list " ")))) > > (append! headercols (list " " > - (_ "Basis") > - (_ "Value") > - (_ "Money In") > - (_ "Money Out") > - (_ "Realized Gain") > - (_ "Unrealized Gain") > - (_ "Total Gain") > - (_ "Rate of Gain") > - (_ "Income"))) > + (G_ "Basis") > + (G_ "Value") > + (G_ "Money In") > + (G_ "Money Out") > + (G_ "Realized Gain") > + (G_ "Unrealized Gain") > + (G_ "Total Gain") > + (G_ "Rate of Gain") > + (G_ "Income"))) > > (if (not (eq? handle-brokerage-fees 'ignore-brokerage)) > - (append! headercols (list (_ "Brokerage Fees")))) > + (append! headercols (list (G_ "Brokerage Fees")))) > > - (append! headercols (list (_ "Total Return") > - (_ "Rate of Return"))) > + (append! headercols (list (G_ "Total Return") > + (G_ "Rate of Return"))) > > (append! totalscols (list " ")) > > @@ -1187,14 +1187,14 @@ by preventing negative stock balances.<br/>") > (gnc:html-document-add-object! document table) > (if warn-price-dirty > (gnc:html-document-append-objects! document > - (list > (gnc:make-html-text (_ "* this commodity data was built using transaction > pricing instead of the price list.")) > + (list > (gnc:make-html-text (G_ "* this commodity data was built using transaction > pricing instead of the price list.")) > (gnc:make-html-text > (gnc:html-markup-br)) > - (gnc:make-html-text > (_ "If you are in a multi-currency situation, the exchanges may not be > correct."))))) > + (gnc:make-html-text > (G_ "If you are in a multi-currency situation, the exchanges may not be > correct."))))) > > (if warn-no-price > (gnc:html-document-append-objects! document > (list > (gnc:make-html-text (if warn-price-dirty (gnc:html-markup-br) "")) > - > (gnc:make-html-text (_ "** this commodity has no price and a price of 1 > has been used."))))) > + > (gnc:make-html-text (G_ "** this commodity has no price and a price of 1 > has been used."))))) > ) > > ;if no accounts selected. > > commit 6f1960313f3f8a8b4bd7207ab07de0b5ee436582 > Author: Christopher Lam <christopher....@gmail.com> > Date: Sat Aug 1 10:12:17 2020 +0800 > > [advanced-portfolio] restore to original 4.0 state > > and remove tests which cannot be run anymore > > diff --git a/gnucash/report/reports/standard/advanced-portfolio.scm > b/gnucash/report/reports/standard/advanced-portfolio.scm > index 92587aa91..279fcb91f 100644 > --- a/gnucash/report/reports/standard/advanced-portfolio.scm > +++ b/gnucash/report/reports/standard/advanced-portfolio.scm > @@ -33,7 +33,6 @@ > (use-modules (gnucash app-utils)) > (use-modules (gnucash report)) > (use-modules (srfi srfi-1)) > -(use-modules (ice-9 match)) > > (define reportname (N_ "Advanced Portfolio")) > > @@ -180,709 +179,6 @@ by preventing negative stock balances.<br/>") > (gnc:options-set-default-section options gnc:pagename-general) > options)) > > -;; helper functions for renderer > - > -(define (same-account? a1 a2) > - (equal? (gncAccountGetGUID a1) (gncAccountGetGUID a2))) > - > -;; Return true if either account is the parent of the other or they are > siblings > -(define (parent-or-sibling? a1 a2) > - (let ((a2parent (gnc-account-get-parent a2)) > - (a1parent (gnc-account-get-parent a1))) > - (or (same-account? a2parent a1) > - (same-account? a1parent a2) > - (same-account? a1parent a2parent)))) > - > -;; sum up the contents of the b-list built by basis-builder below > -(define (sum-basis b-list currency-frac) > - (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) > - (fold (lambda (a b) (+ (car a) b)) 0 b-list)) > - > -;; apply a ratio to an existing basis-list, useful for splits/mergers and > spinoffs > -(define (apply-basis-ratio 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 > -;; 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 > -;; mutates to the new average. Need to add a date checker so that we > -;; allow for prices coming in out of order, such as a transfer with a > -;; 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 " 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 (zero? b-value)) (positive? b-units)) > - (case b-method > - ((average-basis) > - (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 (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 (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) 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 (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 > - ((and (zero? b-units) (not (zero? b-value))) > - (let* ((current-value (sum-basis b-list GNC-DENOM-AUTO)) > - (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))) > - > - > -(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 warnings) > - > - (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 > - (hashq-set! warnings 'warn-price-dirty #t) > - (hashq-set! warnings '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 > ;; report-html.txt for documentation; the file report-html.scm > @@ -892,8 +188,10 @@ by preventing negative stock balances.<br/>") > > (define (advanced-portfolio-renderer report-obj) > > - ;; report-warnings hash-table. > - (define warnings (make-hash-table)) > + (let ((work-done 0) > + (work-to-do 0) > + (warn-no-price #f) > + (warn-price-dirty #f)) > > ;; These are some helper functions for looking up option values. > (define (get-op section name) > @@ -908,6 +206,161 @@ by preventing negative stock balances.<br/>") > (define (same-split? s1 s2) > (equal? (gncSplitGetGUID s1) (gncSplitGetGUID s2))) > > + (define (same-account? a1 a2) > + (equal? (gncAccountGetGUID a1) (gncAccountGetGUID a2))) > + > + ;; 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) > + ) > + ) > + > + ;; 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) > + ) > + ) > + > + ;; 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)) > + '() > + ) > + ) > + > + ;; 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 mutates to the new average. Need to add a date checker so > that we allow for prices > + ;; coming in out of order, such as a transfer with a 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) > + > + ;; 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)) > + (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))))))) > + > + ;; 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) > + b-value ;; Only the sign of b-value > matters since the new b-units is negative > + 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))))) > + '() > + )) > + > + ;; no value, just units, this is a split/merge... > + ((and (gnc-numeric-zero-p b-value) > + (not (gnc-numeric-zero-p 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))) > + ;; 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)) > + (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))) > + (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)) > + ) > + > + ;; when all else fails, just send the b-list back > + (else > + b-list) > + ) > + ) > + > ;; Given a price list and a currency find the price for that currency > on the list. > ;; If there is none for the requested currency, return the first one. > ;; The price list is released but the price returned is ref counted. > @@ -925,6 +378,14 @@ by preventing negative stock balances.<br/>") > (gnc-price-list-destroy price-list) > price))) > > + ;; Return true if either account is the parent of the other or they are > siblings > + (define (parent-or-sibling? a1 a2) > + (let ((a2parent (gnc-account-get-parent a2)) > + (a1parent (gnc-account-get-parent a1))) > + (or (same-account? a2parent a1) > + (same-account? a1parent a2) > + (same-account? a1parent a2parent)))) > + > ;; Test whether the given split is the source of a spin off transaction > ;; This will be a no-units split with only one other split. > ;; xaccSplitGetOtherSplit only returns on a two-split txn. It's not a > spinoff > @@ -937,6 +398,593 @@ 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) > > @@ -968,12 +1016,8 @@ 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)) > @@ -1004,8 +1048,8 @@ by preventing negative stock balances.<br/>") > (lambda (foreign domestic date) > (find-price > (gnc-pricedb-lookup-nearest-in-time-any-currency-t64 > pricedb foreign (time64CanonicalDayTime date)) > domestic))))) > - (headercols (list (G_ "Account"))) > - (totalscols (list (gnc:make-html-table-cell/markup > "total-label-cell" (G_ "Total")))) > + (headercols (list (_ "Account"))) > + (totalscols (list (gnc:make-html-table-cell/markup > "total-label-cell" (_ "Total")))) > (sum-total-moneyin (gnc-numeric-zero)) > (sum-total-income (gnc-numeric-zero)) > (sum-total-both-gains (gnc-numeric-zero)) > @@ -1016,37 +1060,37 @@ by preventing negative stock balances.<br/>") > > ;;begin building lists for which columns to display > (if show-symbol > - (begin (append! headercols (list (G_ "Symbol"))) > + (begin (append! headercols (list (_ "Symbol"))) > (append! totalscols (list " ")))) > > (if show-listing > - (begin (append! headercols (list (G_ "Listing"))) > + (begin (append! headercols (list (_ "Listing"))) > (append! totalscols (list " ")))) > > (if show-shares > - (begin (append! headercols (list (G_ "Shares"))) > + (begin (append! headercols (list (_ "Shares"))) > (append! totalscols (list " ")))) > > (if show-price > - (begin (append! headercols (list (G_ "Price"))) > + (begin (append! headercols (list (_ "Price"))) > (append! totalscols (list " ")))) > > (append! headercols (list " " > - (G_ "Basis") > - (G_ "Value") > - (G_ "Money In") > - (G_ "Money Out") > - (G_ "Realized Gain") > - (G_ "Unrealized Gain") > - (G_ "Total Gain") > - (G_ "Rate of Gain") > - (G_ "Income"))) > + (_ "Basis") > + (_ "Value") > + (_ "Money In") > + (_ "Money Out") > + (_ "Realized Gain") > + (_ "Unrealized Gain") > + (_ "Total Gain") > + (_ "Rate of Gain") > + (_ "Income"))) > > (if (not (eq? handle-brokerage-fees 'ignore-brokerage)) > - (append! headercols (list (G_ "Brokerage Fees")))) > + (append! headercols (list (_ "Brokerage Fees")))) > > - (append! headercols (list (G_ "Total Return") > - (G_ "Rate of Return"))) > + (append! headercols (list (_ "Total Return") > + (_ "Rate of Return"))) > > (append! totalscols (list " ")) > > @@ -1061,8 +1105,7 @@ 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 > - share-print-info warnings)) > + total-income total-gain total-ugain total-brokerage)) > (lambda (k reason) > (gnc:html-document-add-object! > document (format #f OVERFLOW-ERROR reason)))) > @@ -1142,16 +1185,16 @@ by preventing negative stock balances.<br/>") > ) > > (gnc:html-document-add-object! document table) > - (if (hashq-ref warnings 'warn-price-dirty) > + (if warn-price-dirty > (gnc:html-document-append-objects! document > - (list > (gnc:make-html-text (G_ "* this commodity data was built using transaction > pricing instead of the price list.")) > + (list > (gnc:make-html-text (_ "* this commodity data was built using transaction > pricing instead of the price list.")) > (gnc:make-html-text > (gnc:html-markup-br)) > - (gnc:make-html-text > (G_ "If you are in a multi-currency situation, the exchanges may not be > correct."))))) > + (gnc:make-html-text > (_ "If you are in a multi-currency situation, the exchanges may not be > correct."))))) > > - (if (hashq-ref warnings 'warn-no-price) > + (if warn-no-price > (gnc:html-document-append-objects! document > - (list > (gnc:make-html-text (if (hashq-ref warnings 'warn-price-dirty) > (gnc:html-markup-br) "")) > - > (gnc:make-html-text (G_ "** this commodity has no price and a price of 1 > has been used."))))) > + (list > (gnc:make-html-text (if warn-price-dirty (gnc:html-markup-br) "")) > + > (gnc:make-html-text (_ "** this commodity has no price and a price of 1 > has been used."))))) > ) > > ;if no accounts selected. > @@ -1161,7 +1204,7 @@ by preventing negative stock balances.<br/>") > report-title (gnc:report-id report-obj)))) > > (gnc:report-finished) > - document)) > + document))) > > (gnc:define-report > 'version 1 > diff --git a/gnucash/report/reports/standard/test/test-portfolios.scm > b/gnucash/report/reports/standard/test/test-portfolios.scm > index a5008983e..298d072e0 100644 > --- a/gnucash/report/reports/standard/test/test-portfolios.scm > +++ b/gnucash/report/reports/standard/test/test-portfolios.scm > @@ -43,7 +43,6 @@ > (null-test "portfolio" portfolio-uuid) > (null-test "advanced-portfolio" advanced-uuid) > (portfolio-tests) > - (advanced-helper-tests) > (advanced-tests) > (test-end "test-portfolios.scm")) > > @@ -123,76 +122,3 @@ > "-$1.00" "-0.13%") > (sxml->table-row-col sxml 1 1 #f)))) > (teardown))) > - > -(define (advanced-helper-tests) > - (define sum-basis > - (@@ (gnucash reports standard advanced-portfolio) sum-basis)) > - (define units-basis > - (@@ (gnucash reports standard advanced-portfolio) units-basis)) > - (define apply-basis-ratio > - (@@ (gnucash reports standard advanced-portfolio) apply-basis-ratio)) > - (define basis-builder > - (@@ (gnucash reports standard advanced-portfolio) basis-builder)) > - (define basis1 '((3 . 4) (5 . 6) (7 . 8))) > - (define basis2 '((3 . 4) (5 . 6) (7 . 8) (9 . 10))) > - > - (test-equal "sum-basis" > - 98 > - (sum-basis basis1 100)) > - (test-equal "sum-basis" > - 188 > - (sum-basis basis2 100)) > - > - (test-equal "units-basis" > - 15 > - (units-basis basis1)) > - (test-equal "units-basis" > - 24 > - (units-basis basis2)) > - > - (test-equal "apply-basis-ratio" > - '((6 . 12) (10 . 18) (14 . 24)) > - (apply-basis-ratio basis1 2 3)) > - (test-equal "apply-basis-ratio" > - '((6 . 12) (10 . 18) (14 . 24) (18 . 30)) > - (apply-basis-ratio basis2 2 3)) > - > - (test-equal "basis-builder buy new units" > - '((3 . 4/3)) > - (basis-builder '() 3 4 'average-basis 100)) > - (test-equal "basis-builder buy new units average" > - '((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 . 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 . 4/3)) > - (basis-builder '((3 . 4) (5 . 6) (7 . 8)) 3 4 'filo-basis 100)) > - > - (test-equal "basis-builder sell average" > - '((0 . 4)) > - (basis-builder '((3 . 4) (5 . 6) (7 . 8)) -3 4 'average-basis 100)) > - (test-equal "basis-builder sell FIFO first" > - '((5 . 6) (7 . 8)) > - (basis-builder '((3 . 4) (5 . 6) (7 . 8)) -3 4 'fifo-basis 100)) > - (test-equal "basis-builder sell FIFO 2 lots" > - '((3 . 6) (7 . 8)) > - (basis-builder '((3 . 4) (5 . 6) (7 . 8)) -5 4 'fifo-basis 100)) > - (test-equal "basis-builder sell LIFO" > - '((3 . 4) (5 . 6) (4 . 8)) > - (basis-builder '((3 . 4) (5 . 6) (7 . 8)) -3 4 'filo-basis 100)) > - (test-equal "basis-builder sell LIFO all" > - '() > - (basis-builder '((3 . 4) (5 . 6) (7 . 8)) -15 4 'filo-basis 100)) > - (test-equal "basis-builder sell LIFO more than we have" > - '() > - (basis-builder '() -15 4 'filo-basis 100)) > - > - (test-equal "basis-builder = no value just units = split/merge" > - '((12/5 . 5) (4 . 15/2) (28/5 . 10)) > - (basis-builder '((3 . 4) (5 . 6) (7 . 8)) -3 0 'average-basis 100)) > - > - (test-equal "basis-builder = no units just value = spin-off" > - '((3 . 8) (5 . 12) (7 . 16)) > - (basis-builder '((3 . 4) (5 . 6) (7 . 8)) 0 98 'average-basis 100))) > > > > Summary of changes: > .../report/reports/standard/advanced-portfolio.scm | 1477 > ++++++++++---------- > .../reports/standard/test/test-portfolios.scm | 74 - > 2 files changed, 760 insertions(+), 791 deletions(-) > > _______________________________________________ > gnucash-changes mailing list > gnucash-chan...@gnucash.org > https://lists.gnucash.org/mailman/listinfo/gnucash-changes > _______________________________________________ gnucash-devel mailing list gnucash-devel@gnucash.org https://lists.gnucash.org/mailman/listinfo/gnucash-devel