mirror of
https://github.com/Gnucash/gnucash.git
synced 2024-12-02 13:39:43 -06:00
Implement support for !Type:Prices QIF records.
This commit is contained in:
parent
0909fd9b0f
commit
af11a549e3
@ -39,6 +39,7 @@ Type of account identifiers
|
|||||||
!Type:Cat Category list
|
!Type:Cat Category list
|
||||||
!Type:Class Class list
|
!Type:Class Class list
|
||||||
!Type:Memorized Memorized transaction list
|
!Type:Memorized Memorized transaction list
|
||||||
|
!Type:Prices Security prices
|
||||||
|
|
||||||
Note that !Account is used both to be a header for account information,
|
Note that !Account is used both to be a header for account information,
|
||||||
and to be a header for a list of transactions.
|
and to be a header for a list of transactions.
|
||||||
@ -236,6 +237,22 @@ L Link
|
|||||||
W Private
|
W Private
|
||||||
|
|
||||||
|
|
||||||
|
Security Prices
|
||||||
|
---------------
|
||||||
|
The records in a list of security prices do not use any letter
|
||||||
|
identifiers, and take the following form:
|
||||||
|
"<symbol>",<price>,"<date>"
|
||||||
|
where <symbol> is the security's ticker symbol, <price> is the security
|
||||||
|
price, and <date> is the date the security had that price.
|
||||||
|
The price can be either a decimal number of a whole number followed by
|
||||||
|
a practional price. For example, for shares of Intuit updated 6/30/98,
|
||||||
|
with a price of $50.50 per share, the price record would be:
|
||||||
|
"INTU",50 1/2,"6/30/98"
|
||||||
|
or
|
||||||
|
"INTU",50.50,"6/30/98"
|
||||||
|
Each price is followed by a ^ (end of entry indicator).
|
||||||
|
|
||||||
|
|
||||||
=====================================================================
|
=====================================================================
|
||||||
General Notes
|
General Notes
|
||||||
=====================================================================
|
=====================================================================
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
(export qif-file:parse-fields-results)
|
(export qif-file:parse-fields-results)
|
||||||
(export qif-file:read-file)
|
(export qif-file:read-file)
|
||||||
(export qif-file:reparse-dates)
|
(export qif-file:reparse-dates)
|
||||||
|
(export qif-file:parse-price-line)
|
||||||
|
|
||||||
(define qif-bad-numeric-rexp
|
(define qif-bad-numeric-rexp
|
||||||
(make-regexp "^\\.\\.\\."))
|
(make-regexp "^\\.\\.\\."))
|
||||||
@ -229,8 +230,7 @@
|
|||||||
|
|
||||||
;; Security price list
|
;; Security price list
|
||||||
((type:prices)
|
((type:prices)
|
||||||
;; Not supported. We really should warn the user.
|
(set! current-xtn (make-qif-price)))
|
||||||
#f)
|
|
||||||
|
|
||||||
((option:autoswitch)
|
((option:autoswitch)
|
||||||
(set! ignore-accounts #t))
|
(set! ignore-accounts #t))
|
||||||
@ -516,6 +516,20 @@
|
|||||||
(else
|
(else
|
||||||
(mywarn (G_ "Ignoring security line") ": " line))))
|
(mywarn (G_ "Ignoring security line") ": " line))))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;
|
||||||
|
;; Price list ;;
|
||||||
|
;;;;;;;;;;;;;;;;
|
||||||
|
((type:prices)
|
||||||
|
(case tag
|
||||||
|
((#\^)
|
||||||
|
;; end-of-record
|
||||||
|
(set! current-xtn (make-qif-price)))
|
||||||
|
(else
|
||||||
|
;; Parse the price which is a mini csv of 3 entries
|
||||||
|
(set! current-xtn (qif-file:parse-price-line line))
|
||||||
|
(if (equal? current-xtn #f)
|
||||||
|
(mywarn (G_ "Could not parse price line") ": " line)
|
||||||
|
(qif-file:add-price! self current-xtn)))))
|
||||||
|
|
||||||
;; trying to sneak one by, eh?
|
;; trying to sneak one by, eh?
|
||||||
(else
|
(else
|
||||||
@ -724,6 +738,9 @@
|
|||||||
;;
|
;;
|
||||||
(set-sub (G_ "Parsing categories"))
|
(set-sub (G_ "Parsing categories"))
|
||||||
;; The category tasks will be 5% of the overall parsing effort.
|
;; The category tasks will be 5% of the overall parsing effort.
|
||||||
|
;; Note, the presumption is that reading the file is 70% of the
|
||||||
|
;; overall effort, so this function only has ~30% to dole out,
|
||||||
|
;; hence 5% at a time here
|
||||||
(start-sub 0.05)
|
(start-sub 0.05)
|
||||||
|
|
||||||
;; Tax classes; assume this is 50% of the category parsing effort.
|
;; Tax classes; assume this is 50% of the category parsing effort.
|
||||||
@ -792,6 +809,39 @@
|
|||||||
(finish-sub)
|
(finish-sub)
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Prices
|
||||||
|
;;
|
||||||
|
(set-sub (G_ "Parsing prices"))
|
||||||
|
;; We process the list of prices
|
||||||
|
(start-sub 0.05)
|
||||||
|
|
||||||
|
;; Dates
|
||||||
|
(start-sub 0.5)
|
||||||
|
(check-and-parse-field
|
||||||
|
qif-price:date qif-price:set-date! equal?
|
||||||
|
qif-parse:check-date-format '(m-d-y d-m-y y-m-d y-d-m)
|
||||||
|
qif-parse:parse-date/format
|
||||||
|
(qif-file:prices self)
|
||||||
|
qif-parse:print-date
|
||||||
|
'error-on-ambiguity add-error 'date
|
||||||
|
update-progress)
|
||||||
|
(finish-sub)
|
||||||
|
|
||||||
|
;; Amounts
|
||||||
|
(start-sub 1)
|
||||||
|
(check-and-parse-field
|
||||||
|
qif-price:share-price qif-price:set-share-price! gnc-numeric-equal
|
||||||
|
qif-parse:check-number-format '(decimal comma)
|
||||||
|
qif-parse:parse-number/format (qif-file:prices self)
|
||||||
|
qif-parse:print-number
|
||||||
|
'guess-on-ambiguity add-error 'share-price
|
||||||
|
update-progress)
|
||||||
|
(finish-sub)
|
||||||
|
|
||||||
|
(finish-sub)
|
||||||
|
|
||||||
|
|
||||||
;;
|
;;
|
||||||
;; fields of transactions
|
;; fields of transactions
|
||||||
;;
|
;;
|
||||||
@ -1083,3 +1133,36 @@
|
|||||||
(test-results (cdr results))))))
|
(test-results (cdr results))))))
|
||||||
|
|
||||||
(if results (test-results results) #f))
|
(if results (test-results results) #f))
|
||||||
|
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; qif-file:parse-price-line
|
||||||
|
;;
|
||||||
|
;; Takes an expected price line and returns a qif-price object
|
||||||
|
;;
|
||||||
|
;; We are parsing "SYMBOL",value,"DATE"
|
||||||
|
;; where symbol is the alpha name of the security symbol,
|
||||||
|
;; value can be a float like 42.50 or a fractional expression like 42 1/2
|
||||||
|
;; and date can be the typical Quicken date formats
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(define (qif-file:parse-price-line line)
|
||||||
|
(let ((current-xtn (make-qif-price)))
|
||||||
|
(if (not (false-if-exception
|
||||||
|
(let* ((symbol (string-trim-both (string-trim-both (car (string-split line #\,)) char-set:punctuation)))
|
||||||
|
(price (cadr (string-split line #\,)))
|
||||||
|
(fracprice (string-split price #\space))
|
||||||
|
(date (string-trim-both (string-trim-both (caddr (string-split line #\,)) char-set:punctuation))))
|
||||||
|
|
||||||
|
(qif-price:set-symbol! current-xtn symbol)
|
||||||
|
(qif-price:set-date! current-xtn date)
|
||||||
|
|
||||||
|
;; If we have a fractional portion, convert it to float
|
||||||
|
(if (> (length fracprice) 1)
|
||||||
|
(let ((total (+ (string->number (car fracprice))
|
||||||
|
(string->number (cadr fracprice)))))
|
||||||
|
(set! price (format #f "~f" total))))
|
||||||
|
|
||||||
|
(qif-price:set-share-price! current-xtn price))))
|
||||||
|
(set! current-xtn #f))
|
||||||
|
current-xtn))
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
(export make-qif-map-entry)
|
(export make-qif-map-entry)
|
||||||
(export make-qif-split)
|
(export make-qif-split)
|
||||||
(export make-qif-stock-symbol)
|
(export make-qif-stock-symbol)
|
||||||
|
(export make-qif-price)
|
||||||
(export make-qif-xtn)
|
(export make-qif-xtn)
|
||||||
(export make-ticker-map)
|
(export make-ticker-map)
|
||||||
(export qif-acct:budget)
|
(export qif-acct:budget)
|
||||||
@ -75,6 +76,8 @@
|
|||||||
(export qif-file:add-cat!)
|
(export qif-file:add-cat!)
|
||||||
(export qif-file:add-class!)
|
(export qif-file:add-class!)
|
||||||
(export qif-file:add-xtn!)
|
(export qif-file:add-xtn!)
|
||||||
|
(export qif-file:add-price!)
|
||||||
|
(export qif-file:prices)
|
||||||
(export qif-file:cats)
|
(export qif-file:cats)
|
||||||
(export qif-file:path)
|
(export qif-file:path)
|
||||||
(export qif-file:path-to-accountname)
|
(export qif-file:path-to-accountname)
|
||||||
@ -112,6 +115,12 @@
|
|||||||
(export qif-stock-symbol:set-name!)
|
(export qif-stock-symbol:set-name!)
|
||||||
(export qif-stock-symbol:set-symbol!)
|
(export qif-stock-symbol:set-symbol!)
|
||||||
(export qif-stock-symbol:set-type!)
|
(export qif-stock-symbol:set-type!)
|
||||||
|
(export qif-price:symbol)
|
||||||
|
(export qif-price:set-symbol!)
|
||||||
|
(export qif-price:date)
|
||||||
|
(export qif-price:set-date!)
|
||||||
|
(export qif-price:share-price)
|
||||||
|
(export qif-price:set-share-price!)
|
||||||
(export qif-ticker-map:add-ticker!)
|
(export qif-ticker-map:add-ticker!)
|
||||||
(export qif-ticker-map:lookup-symbol)
|
(export qif-ticker-map:lookup-symbol)
|
||||||
(export qif-ticker-map:lookup-type)
|
(export qif-ticker-map:lookup-type)
|
||||||
@ -156,6 +165,7 @@
|
|||||||
;; accounts : list of <qif-acct>
|
;; accounts : list of <qif-acct>
|
||||||
;; cats : list of <qif-cat>
|
;; cats : list of <qif-cat>
|
||||||
;; classes : list of <qif-class>
|
;; classes : list of <qif-class>
|
||||||
|
;; prices : list of <qif-price>
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(define <qif-file>
|
(define <qif-file>
|
||||||
@ -166,7 +176,8 @@
|
|||||||
xtns
|
xtns
|
||||||
accounts
|
accounts
|
||||||
cats
|
cats
|
||||||
classes)))
|
classes
|
||||||
|
prices)))
|
||||||
|
|
||||||
(define qif-file?
|
(define qif-file?
|
||||||
(record-predicate <qif-file>))
|
(record-predicate <qif-file>))
|
||||||
@ -207,6 +218,12 @@
|
|||||||
(define qif-file:set-accounts!
|
(define qif-file:set-accounts!
|
||||||
(record-modifier <qif-file> 'accounts))
|
(record-modifier <qif-file> 'accounts))
|
||||||
|
|
||||||
|
(define qif-file:prices
|
||||||
|
(record-accessor <qif-file> 'prices))
|
||||||
|
|
||||||
|
(define qif-file:set-prices!
|
||||||
|
(record-modifier <qif-file> 'prices))
|
||||||
|
|
||||||
(define (make-qif-file)
|
(define (make-qif-file)
|
||||||
(let ((self (construct <qif-file>)))
|
(let ((self (construct <qif-file>)))
|
||||||
(qif-file:set-y2k-threshold! self 50)
|
(qif-file:set-y2k-threshold! self 50)
|
||||||
@ -214,6 +231,7 @@
|
|||||||
(qif-file:set-accounts! self '())
|
(qif-file:set-accounts! self '())
|
||||||
(qif-file:set-cats! self '())
|
(qif-file:set-cats! self '())
|
||||||
(qif-file:set-classes! self '())
|
(qif-file:set-classes! self '())
|
||||||
|
(qif-file:set-prices! self '())
|
||||||
self))
|
self))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
@ -627,6 +645,10 @@
|
|||||||
(qif-file:set-accounts! self
|
(qif-file:set-accounts! self
|
||||||
(cons account (qif-file:accounts self))))
|
(cons account (qif-file:accounts self))))
|
||||||
|
|
||||||
|
(define (qif-file:add-price! self price)
|
||||||
|
(qif-file:set-prices! self
|
||||||
|
(cons price (qif-file:prices self))))
|
||||||
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; munge the QIF filename to create a simple default account name
|
;; munge the QIF filename to create a simple default account name
|
||||||
@ -787,6 +809,44 @@
|
|||||||
(qif-stock-symbol:set-type! retval "")
|
(qif-stock-symbol:set-type! retval "")
|
||||||
retval))
|
retval))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; <qif-price>
|
||||||
|
;; Symbol,shares,date
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(define <qif-price>
|
||||||
|
(make-record-type
|
||||||
|
'qif-price
|
||||||
|
'(symbol share-price date)))
|
||||||
|
|
||||||
|
(define qif-price:symbol
|
||||||
|
(record-accessor <qif-price> 'symbol))
|
||||||
|
|
||||||
|
(define qif-price:set-symbol!
|
||||||
|
(record-modifier <qif-price> 'symbol))
|
||||||
|
|
||||||
|
(define qif-price:share-price
|
||||||
|
(record-accessor <qif-price> 'share-price))
|
||||||
|
|
||||||
|
(define qif-price:set-share-price!
|
||||||
|
(record-modifier <qif-price> 'share-price))
|
||||||
|
|
||||||
|
(define qif-price:date
|
||||||
|
(record-accessor <qif-price> 'date))
|
||||||
|
|
||||||
|
(define qif-price:set-date!
|
||||||
|
(record-modifier <qif-price> 'date))
|
||||||
|
|
||||||
|
(define (qif-price:print self)
|
||||||
|
(write self))
|
||||||
|
|
||||||
|
(define (make-qif-price)
|
||||||
|
(let ((retval (construct <qif-price>)))
|
||||||
|
(qif-price:set-symbol! retval "")
|
||||||
|
(qif-price:set-share-price! retval "")
|
||||||
|
(qif-price:set-date! retval "")
|
||||||
|
retval))
|
||||||
|
|
||||||
(define <qif-ticker-map>
|
(define <qif-ticker-map>
|
||||||
(make-record-type
|
(make-record-type
|
||||||
'qif-ticker-map
|
'qif-ticker-map
|
||||||
|
@ -347,6 +347,12 @@
|
|||||||
qif-files-list)
|
qif-files-list)
|
||||||
|
|
||||||
|
|
||||||
|
;; Add any prices to the list of work to do
|
||||||
|
(for-each
|
||||||
|
(lambda (qif-file)
|
||||||
|
(set! work-to-do (+ (length (qif-file:prices qif-file)) work-to-do)))
|
||||||
|
qif-files-list)
|
||||||
|
|
||||||
;; Build a local account tree to hold converted transactions.
|
;; Build a local account tree to hold converted transactions.
|
||||||
(if progress-dialog
|
(if progress-dialog
|
||||||
(gnc-progress-dialog-set-sub progress-dialog
|
(gnc-progress-dialog-set-sub progress-dialog
|
||||||
@ -449,6 +455,87 @@
|
|||||||
(qif-file:xtns qif-file)))
|
(qif-file:xtns qif-file)))
|
||||||
sorted-qif-files-list)
|
sorted-qif-files-list)
|
||||||
|
|
||||||
|
;; Add prices
|
||||||
|
(let ((pricedb (gnc-pricedb-get-db (gnc-get-current-book)))
|
||||||
|
(symbol-hash (make-symbol-hash stock-map))
|
||||||
|
(sorted-prices '())
|
||||||
|
(pruned-prices '())
|
||||||
|
(pruned-price-count 0)
|
||||||
|
(commodity #f)
|
||||||
|
(current-symbol " invalid symbol "))
|
||||||
|
|
||||||
|
;; Let's combine all the prices and sort them; it
|
||||||
|
;; is slightly more efficient to do them by symbol and
|
||||||
|
;; tidies up error reporting on missing symbols
|
||||||
|
(for-each
|
||||||
|
(lambda (qif-file)
|
||||||
|
(set! sorted-prices (append sorted-prices (qif-file:prices qif-file))))
|
||||||
|
qif-files-list)
|
||||||
|
|
||||||
|
(set! sorted-prices (sort sorted-prices
|
||||||
|
(lambda (a b)
|
||||||
|
(string-ci<? (qif-price:symbol a) (qif-price:symbol b)))))
|
||||||
|
|
||||||
|
(gnc-pricedb-begin-edit pricedb)
|
||||||
|
;; Turning off bulk update avoids an n^2 performance
|
||||||
|
;; cost, but also turns off duplicate checking
|
||||||
|
(gnc-pricedb-set-bulk-update pricedb #t)
|
||||||
|
|
||||||
|
(if progress-dialog
|
||||||
|
(gnc-progress-dialog-set-sub progress-dialog
|
||||||
|
(string-append (G_ "Discarding duplicate prices"))))
|
||||||
|
|
||||||
|
;; Prune duplicate records before inserting
|
||||||
|
(for-each
|
||||||
|
(lambda (price)
|
||||||
|
(if (not (string-ci=? current-symbol (qif-price:symbol price)))
|
||||||
|
(begin
|
||||||
|
(set! current-symbol (qif-price:symbol price))
|
||||||
|
(set! commodity (hash-ref symbol-hash current-symbol))
|
||||||
|
|
||||||
|
(if (equal? commodity #f)
|
||||||
|
(qif-import:log progress-dialog
|
||||||
|
"qif-import:qif-to-gnc"
|
||||||
|
(G_ (format #f "Warning: cannot find commodity for symbol ~a." current-symbol))))))
|
||||||
|
(if (not (equal? commodity #f))
|
||||||
|
(if (not (duplicate-price? pricedb commodity default-currency price))
|
||||||
|
(set! pruned-prices (append pruned-prices (list price)))
|
||||||
|
(set! pruned-price-count (+ 1 pruned-price-count))))
|
||||||
|
(update-progress))
|
||||||
|
sorted-prices)
|
||||||
|
|
||||||
|
(if progress-dialog
|
||||||
|
(begin
|
||||||
|
(if (> pruned-price-count 0)
|
||||||
|
(qif-import:log progress-dialog
|
||||||
|
"qif-import:qif-to-gnc"
|
||||||
|
(G_ (format #f "Warning: skipped ~a prices on days that already had prices." pruned-price-count))))
|
||||||
|
|
||||||
|
(gnc-progress-dialog-set-sub progress-dialog
|
||||||
|
(string-append (G_ "Adding prices")))))
|
||||||
|
|
||||||
|
;; Reset and run again, this time to actually add prices
|
||||||
|
(set! current-symbol " -- unknown --")
|
||||||
|
(for-each
|
||||||
|
(lambda (price)
|
||||||
|
(if (not (string-ci=? current-symbol (qif-price:symbol price)))
|
||||||
|
(begin
|
||||||
|
(set! current-symbol (qif-price:symbol price))
|
||||||
|
(set! commodity (hash-ref symbol-hash current-symbol))))
|
||||||
|
|
||||||
|
(if (not (equal? commodity #f))
|
||||||
|
(let* ((gnc-price (gnc-price-create (gnc-get-current-book)))
|
||||||
|
;; We skip any bad price results, instead of crashing the whole process
|
||||||
|
(okay (false-if-exception
|
||||||
|
(qif-price-to-gnc-price price gnc-price
|
||||||
|
default-currency commodity progress-dialog))))
|
||||||
|
(if okay (gnc-pricedb-add-price pricedb gnc-price))
|
||||||
|
(gnc-price-unref gnc-price)))
|
||||||
|
(update-progress))
|
||||||
|
pruned-prices)
|
||||||
|
|
||||||
|
(gnc-pricedb-commit-edit pricedb))
|
||||||
|
|
||||||
;; Finished.
|
;; Finished.
|
||||||
(if progress-dialog
|
(if progress-dialog
|
||||||
(gnc-progress-dialog-set-value progress-dialog 1))
|
(gnc-progress-dialog-set-value progress-dialog 1))
|
||||||
@ -1259,3 +1346,98 @@
|
|||||||
(xaccAccountBeginEdit root)
|
(xaccAccountBeginEdit root)
|
||||||
(xaccAccountDestroy root))))
|
(xaccAccountDestroy root))))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Utility procedures for handling prices
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; make-symbol-hash
|
||||||
|
;;
|
||||||
|
;; Quicken provides historical price data using the stock symbol.
|
||||||
|
;; We generate a map of symbol to commodity in order to update prices.
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
(define (make-symbol-hash stock-map)
|
||||||
|
(let ((table (make-hash-table 20)))
|
||||||
|
(hash-for-each
|
||||||
|
(lambda (stock commodity)
|
||||||
|
(let ((symbol (gnc-commodity-get-mnemonic commodity)))
|
||||||
|
(hash-set! table symbol commodity)))
|
||||||
|
stock-map)
|
||||||
|
table))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; duplicate-price?
|
||||||
|
;;
|
||||||
|
;; Is there already a price for this commodity?
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
(define (duplicate-price? pricedb commodity currency qif-price)
|
||||||
|
(let* ((qif-date (qif-price:date qif-price))
|
||||||
|
(time64 (qif-date-to-time64 qif-date))
|
||||||
|
(tm (gnc-localtime time64))
|
||||||
|
(near-price (gnc-pricedb-lookup-nearest-before-t64 pricedb commodity currency time64))
|
||||||
|
(near-tm (gnc-localtime (gnc-price-get-time64 near-price)))
|
||||||
|
(dupe #f))
|
||||||
|
(if (and (equal? (tm:year tm) (tm:year near-tm))
|
||||||
|
(equal? (tm:mon tm) (tm:mon near-tm))
|
||||||
|
(equal? (tm:mday tm) (tm:mday near-tm)))
|
||||||
|
(set! dupe #t))
|
||||||
|
dupe))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; qif-date-to-time64
|
||||||
|
;;
|
||||||
|
;; Convert our internal qif-date format into a time64
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
(define (qif-date-to-time64 qif-date)
|
||||||
|
(let ((tm (gnc-localtime (current-time))))
|
||||||
|
;; Choose a 'neutral' time of day, instead of time of import
|
||||||
|
(set-tm:hour tm 10)
|
||||||
|
(set-tm:min tm 59)
|
||||||
|
(set-tm:sec tm 0)
|
||||||
|
(set-tm:mday tm (car qif-date))
|
||||||
|
(set-tm:mon tm (- (cadr qif-date) 1))
|
||||||
|
(set-tm:year tm (- (caddr qif-date) 1900))
|
||||||
|
(gnc-mktime tm)))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; qif-price-to-gnc-price
|
||||||
|
;; translate a single price to a GNCPrice structure
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(define (qif-price-to-gnc-price qif-price gnc-price
|
||||||
|
default-currency commodity progress-dialog)
|
||||||
|
(let* ((qif-symbol (qif-price:symbol qif-price))
|
||||||
|
(qif-share-price (qif-price:share-price qif-price))
|
||||||
|
(qif-date (qif-price:date qif-price))
|
||||||
|
(gnc-value (gnc-numeric-zero)))
|
||||||
|
|
||||||
|
;; Check the transaction date.
|
||||||
|
(if (not qif-date)
|
||||||
|
((qif-import:log progress-dialog
|
||||||
|
"qif-price-to-gnc-price"
|
||||||
|
(G_ "Invalid transaction date."))
|
||||||
|
(throw 'bad-date
|
||||||
|
"qif-price-to-gnc-price"
|
||||||
|
"Missing transaction date."
|
||||||
|
#f
|
||||||
|
#f)))
|
||||||
|
|
||||||
|
;; Set properties of the whole transaction.
|
||||||
|
(gnc-price-begin-edit gnc-price)
|
||||||
|
(gnc-price-set-commodity gnc-price commodity)
|
||||||
|
(gnc-price-set-currency gnc-price default-currency)
|
||||||
|
(gnc-price-set-source-string gnc-price "user:price")
|
||||||
|
|
||||||
|
;; other options for type are "last" or "nav" which are
|
||||||
|
;; the last known price for a stock or the net asset value
|
||||||
|
;; for a mutual fund. We don't really know, so we'll
|
||||||
|
;; go with unknown
|
||||||
|
(gnc-price-set-typestr gnc-price "unknown")
|
||||||
|
|
||||||
|
(gnc-price-set-value gnc-price (gnc-numeric-create
|
||||||
|
(numerator qif-share-price) (denominator qif-share-price)))
|
||||||
|
(gnc-price-set-time64 gnc-price (qif-date-to-time64 qif-date))
|
||||||
|
(gnc-price-commit-edit gnc-price)
|
||||||
|
|
||||||
|
;; return the transaction
|
||||||
|
gnc-price))
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
(test-runner-factory gnc:test-runner)
|
(test-runner-factory gnc:test-runner)
|
||||||
(test-begin "test-qif-imp")
|
(test-begin "test-qif-imp")
|
||||||
(test-string)
|
(test-string)
|
||||||
|
(test-price-parse)
|
||||||
(test-qif-objects)
|
(test-qif-objects)
|
||||||
(test-end "test-qif-imp"))
|
(test-end "test-qif-imp"))
|
||||||
|
|
||||||
@ -57,3 +58,24 @@
|
|||||||
(test-assert "make-ticker-map is called from C"
|
(test-assert "make-ticker-map is called from C"
|
||||||
(make-ticker-map)))
|
(make-ticker-map)))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; qif-file.scm
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(define (test-price-parse)
|
||||||
|
(let ((parsed (qif-file:parse-price-line "\"ABC\",1.0,\"1/1/04\""))
|
||||||
|
(model (make-qif-price)))
|
||||||
|
(qif-price:set-symbol! model "ABC")
|
||||||
|
(qif-price:set-share-price! model "1.0")
|
||||||
|
(qif-price:set-date! model "1/1/04")
|
||||||
|
(test-equal parsed model))
|
||||||
|
|
||||||
|
(let ((parsed (qif-file:parse-price-line "\"ABC\",1 3/4,\"1/1' 4\""))
|
||||||
|
(model (make-qif-price)))
|
||||||
|
(qif-price:set-symbol! model "ABC")
|
||||||
|
(qif-price:set-share-price! model "1.75")
|
||||||
|
(qif-price:set-date! model "1/1' 4")
|
||||||
|
(test-equal parsed model))
|
||||||
|
|
||||||
|
(let ((parsed (qif-file:parse-price-line "\"ABC\",\"1/1' 4\"")))
|
||||||
|
(test-equal parsed #f)))
|
||||||
|
Loading…
Reference in New Issue
Block a user