Bug 799258 - Reports calculating net worth incorrectly after stock split

For average-cost and weighted-average: when the exchange-fn is
created, it would formerly consider the whole book foreign currency
conversions to calculate the date-specific conversions. This bug fix
will limit the book conversions to those earlier than the
date-specific conversions.

The book retrieval functions are expensive, therefore the analysis
splitlists are cached in the date-hash hash-table, which is scoped
within the exchange-fn lambda only, and is garbage collected when
exchange-fn is out of scope.

Also: amend relevant commodity-utilities.scm tests

The old weighted-average and average-cost tests were written to
document the (knowingly) incorrect exchanged amounts as originally
calculated pre-2024.

In 2024, the exchange-fn is fixed so that foreign currency
conversions at a particular report date would consider only
transactions up to the relevant report date.

The first AAPL transaction in this test book is on 09/08/2013: a
purchase of 600 AAPL at $36840 total @ $61.40 each.

Therefore the first test, on 20/02/2012 the AAPL->USD exchange rate
conversion should be zero, since there are no AAPL transactions
yet.

Subsequent AAPL test on 20/02/2014 shows the conversion of 1
AAPL to be 307/5 which matches $61.40.

etc.
This commit is contained in:
Christopher Lam 2024-04-18 21:30:38 +08:00
parent a993e97204
commit 0bd0f77742
2 changed files with 19 additions and 14 deletions

View File

@ -823,19 +823,24 @@
(define (gnc:case-exchange-time-fn
source-option report-currency commodity-list to-date-tp
start-percent delta-percent)
(define date-hash (make-hash-table))
(define-syntax-rule (memoize date expensive-fn)
(or (hash-ref date-hash date) (hashv-set! date-hash date expensive-fn)))
(case source-option
;; Make this the same as gnc:case-exchange-fn
((average-cost) (let* ((exchange-fn (gnc:make-exchange-function
(gnc:make-exchange-cost-alist
report-currency to-date-tp))))
(lambda (foreign domestic date)
((average-cost) (lambda (foreign domestic date)
(let* ((end-day (gnc:time64-end-day-time date))
(exchange-fn (memoize end-day
(gnc:make-exchange-function
(gnc:make-exchange-cost-alist
report-currency end-day)))))
(exchange-fn foreign domestic))))
((weighted-average) (let ((pricealist
(gnc:get-commoditylist-totalavg-prices
commodity-list report-currency to-date-tp
start-percent delta-percent)))
(gnc:debug "weighted-average pricealist " pricealist)
(lambda (foreign domestic date)
((weighted-average) (lambda (foreign domestic date)
(let* ((end-day (gnc:time64-end-day-time date))
(pricealist (memoize end-day
(gnc:get-commoditylist-totalavg-prices
commodity-list report-currency end-day
start-percent delta-percent))))
(gnc:exchange-by-pricealist-nearest
pricealist foreign domestic date))))
((pricedb-before) gnc:exchange-by-pricedb-nearest-before)

View File

@ -655,7 +655,7 @@
(gnc-dmy2time64-neutral 20 02 2016)
#f #f)))
(test-equal "gnc:case-exchange-time-fn weighted-average 20/02/2012"
307/5
0
(gnc:gnc-monetary-amount
(exchange-fn
(gnc:make-gnc-monetary AAPL 1)
@ -663,7 +663,7 @@
(gnc-dmy2time64-neutral 20 02 2012))))
(test-equal "gnc:case-exchange-time-fn weighted-average 20/02/2014"
9366/125
307/5
(gnc:gnc-monetary-amount
(exchange-fn
(gnc:make-gnc-monetary AAPL 1)
@ -687,7 +687,7 @@
(gnc-dmy2time64-neutral 11 08 2014))))
(test-equal "gnc:case-exchange-time-fn weighted-average 22/10/2015"
27663/325
9366/125
(gnc:gnc-monetary-amount
(exchange-fn
(gnc:make-gnc-monetary AAPL 1)
@ -708,7 +708,7 @@
(gnc-dmy2time64-neutral 20 02 2016)
#f #f)))
(test-equal "gnc:case-exchange-time-fn average-cost 20/02/2012"
14127/175
0
(gnc:gnc-monetary-amount
(exchange-fn
(gnc:make-gnc-monetary AAPL 1)