1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
;;; ldg-commodities.el --- Helper code for use with the "ledger" command-line tool
;; Copyright (C) 2003-2013 John Wiegley (johnw AT gnu DOT org)
;; This file is not part of GNU Emacs.
;; This is free software; you can redistribute it and/or modify it under
;; the terms of the GNU General Public License as published by the Free
;; Software Foundation; either version 2, or (at your option) any later
;; version.
;;
;; This is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
;; for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
;; MA 02111-1307, USA.
;;; Commentary:
;; Helper functions to deal with commoditized numbers. A commoditized
;; number will be a cons of value and string where the string contains
;; the commodity
;;; Code:
(defcustom ledger-reconcile-default-commodity "$"
"The default commodity for use in target calculations in ledger reconcile."
:type 'string
:group 'ledger)
(defcustom ledger-use-decimal-comma nil
"If non-nil the use commas as decimal separator.
This only has effect interfacing to calc mode in edit amount"
:type 'boolean
:group 'ledger)
(defun ledger-string-balance-to-commoditized-amount (str)
"Return a commoditized amount (val, 'comm') from STR."
(let ((fields (split-string str "[\n\r]"))) ; break any balances
; with multi commodities
; into a list
(mapcar '(lambda (str)
(let* ((parts (split-string str)) ;break into number and commodity string
(first (car parts))
(second (cadr parts)))
(if (string-match "^-*[1-9]+" first)
(list (string-to-number
(ledger-commodity-string-number-decimalize first :from-user)) second)
(list (string-to-number
(ledger-commodity-string-number-decimalize second :from-user)) first))))
fields)))
(defun -commodity (c1 c2)
"Subtract C2 from C1, ensuring their commodities match."
(if (string= (cadr c1) (cadr c2))
(list (- (car c1) (car c2)) (cadr c1))
(error "Can't subtract different commodities %S from %S" c2 c1)))
(defun +commodity (c1 c2)
"Add C1 and C2, ensuring their commodities match."
(if (string= (cadr c1) (cadr c2))
(list (+ (car c1) (car c2)) (cadr c1))
(error "Can't add different commodities, %S to %S" c1 c2)))
(defun ledger-commodity-string-number-decimalize (number-string direction)
"Take NUMBER-STRING and ensure proper decimalization for use by string-to-number and number-to-string.
DIRECTION can be :to-user or :from-user. All math calculations
are done with decimal-period, some users may prefer decimal-comma
which must be translated both directions."
(let ((val number-string))
(if ledger-use-decimal-comma
(cond ((eq direction :from-user)
;; change string to decimal-period
(while (string-match "," val)
(setq val (replace-match "." nil nil val)))) ;; switch to period separator
((eq direction :to-user)
;; change to decimal-comma
(while (string-match "\\." val)
(setq val (replace-match "," nil nil val)))) ;; gets rid of periods
(t
(error "ledger-commodity-string-number-decimalize: direction not properly specified %S" direction))))
val))
(defun ledger-commodity-to-string (c1)
"Return string representing C1.
Single character commodities are placed ahead of the value,
longer one are after the value."
(let ((val (ledger-commodity-string-number-decimalize
(number-to-string (car c1)) :to-user))
(commodity (cadr c1)))
(if (> (length commodity) 1)
(concat val " " commodity)
(concat commodity " " val))))
(defun ledger-read-commodity-string (prompt)
"Return a commoditizd value (val 'comm') from COMM.
Assumes a space between the value and the commodity."
(let ((parts (split-string (read-from-minibuffer
(concat prompt " (" ledger-reconcile-default-commodity "): ")))))
(if parts
(if (/= (length parts) 2) ;;assume a number was entered and use default commodity
(list (string-to-number (car parts))
ledger-reconcile-default-commodity)
(let ((valp1 (string-to-number (car parts)))
(valp2 (string-to-number (cadr parts))))
(cond ((and (= valp1 valp2) (= 0 valp1));; means neither contained a valid number (both = 0)
(list 0 ""))
((and (/= 0 valp1) (= valp2 0))
(list valp1 (cadr parts)))
((and (/= 0 valp2) (= valp1 0))
(list valp2 (car parts)))
(t
(error "Cannot understand commodity"))))))))
(provide 'ldg-commodities)
;;; ldg-commodities.el ends here
|