Merge branch 'next' into ledger-mode-automatic-transactions
This commit is contained in:
commit
896d1cc3ec
13 changed files with 241 additions and 136 deletions
|
|
@ -672,6 +672,11 @@ Default face for pending (!) transactions
|
||||||
Default face for other transactions
|
Default face for other transactions
|
||||||
@item ledger-font-posting-account-face
|
@item ledger-font-posting-account-face
|
||||||
Face for Ledger accounts
|
Face for Ledger accounts
|
||||||
|
@item ledger-font-posting-account-cleared-face
|
||||||
|
Face for cleared Ledger accounts
|
||||||
|
@item ledger-font-posting-account-pending-face
|
||||||
|
Face for Ledger pending accounts
|
||||||
|
|
||||||
@item ledger-font-posting-amount-face
|
@item ledger-font-posting-amount-face
|
||||||
Face for Ledger amounts
|
Face for Ledger amounts
|
||||||
@item ledger-occur-narrowed-face
|
@item ledger-occur-narrowed-face
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ twinkling in their father's CRT.
|
||||||
* Reporting Commands::
|
* Reporting Commands::
|
||||||
* Command-line Syntax::
|
* Command-line Syntax::
|
||||||
* Budgeting and Forecasting::
|
* Budgeting and Forecasting::
|
||||||
|
* Time Keeping::
|
||||||
* Value Expressions::
|
* Value Expressions::
|
||||||
* Format Strings::
|
* Format Strings::
|
||||||
* Extending with Python::
|
* Extending with Python::
|
||||||
|
|
@ -6389,7 +6390,7 @@ weekly last august
|
||||||
@end smallexample
|
@end smallexample
|
||||||
|
|
||||||
|
|
||||||
@node Budgeting and Forecasting, Value Expressions, Command-line Syntax, Top
|
@node Budgeting and Forecasting, Time Keeping, Command-line Syntax, Top
|
||||||
@chapter Budgeting and Forecasting
|
@chapter Budgeting and Forecasting
|
||||||
|
|
||||||
@menu
|
@menu
|
||||||
|
|
@ -6485,8 +6486,37 @@ only, and not against the running total:
|
||||||
ledger --forecast "d<[2010]" bal ^assets ^liabilities
|
ledger --forecast "d<[2010]" bal ^assets ^liabilities
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
|
@node Time Keeping, Value Expressions, Budgeting and Forecasting, Top
|
||||||
|
@chapter Time Keeping
|
||||||
|
|
||||||
@node Value Expressions, Format Strings, Budgeting and Forecasting, Top
|
|
||||||
|
Ledger directly supports ``timelog'' entries, which have this form:
|
||||||
|
|
||||||
|
@smallexample
|
||||||
|
i 2013/03/28 22:13:00 ACCOUNT[ PAYEE]
|
||||||
|
o 2013/03/29 03:39:00
|
||||||
|
@end smallexample
|
||||||
|
|
||||||
|
This records a check-in to the given ACCOUNT, and a check-out. You can
|
||||||
|
be checked-in to multiple accounts at a time, if you wish, and they can
|
||||||
|
span multiple days (use @code{--day-break} to break them up in the
|
||||||
|
report). The number of seconds between is accumulated as time to that
|
||||||
|
ACCOUNT. If the checkout uses a capital ``O'', the transaction is marked
|
||||||
|
``cleared''. You can use an optional PAYEE for whatever meaning you like.
|
||||||
|
|
||||||
|
Now, there are a few ways to generate this information. You can use the
|
||||||
|
@file{timeclock.el} package, which is part of Emacs. Or you can write a
|
||||||
|
simple script in whichever language you prefer to emit similar
|
||||||
|
information. Or you can use Org-mode's time-clocking abilities and the
|
||||||
|
org2tc script developed by John Wiegly.
|
||||||
|
|
||||||
|
These timelog entries can appear in a separate file, or directly in your
|
||||||
|
main ledger file. The initial "i" and "o" count as Ledger "directives",
|
||||||
|
and are accepted anywhere that ordinary transactions are.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@node Value Expressions, Format Strings, Time Keeping, Top
|
||||||
@chapter Value Expressions
|
@chapter Value Expressions
|
||||||
|
|
||||||
Ledger uses value expressions to make calculations for many different
|
Ledger uses value expressions to make calculations for many different
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@
|
||||||
|
|
||||||
;;; Code:
|
;;; Code:
|
||||||
|
|
||||||
|
(require 'ldg-regex)
|
||||||
|
|
||||||
(defcustom ledger-reconcile-default-commodity "$"
|
(defcustom ledger-reconcile-default-commodity "$"
|
||||||
"The default commodity for use in target calculations in ledger reconcile."
|
"The default commodity for use in target calculations in ledger reconcile."
|
||||||
:type 'string
|
:type 'string
|
||||||
|
|
@ -36,13 +38,13 @@
|
||||||
Returns a list with (value commodity)."
|
Returns a list with (value commodity)."
|
||||||
(if (> (length str) 0)
|
(if (> (length str) 0)
|
||||||
(let ((number-regex (if (assoc "decimal-comma" ledger-environment-alist)
|
(let ((number-regex (if (assoc "decimal-comma" ledger-environment-alist)
|
||||||
"-?[1-9][0-9.]*[,]?[0-9]*"
|
ledger-amount-decimal-comma-regex
|
||||||
"-?[1-9][0-9,]*[.]?[0-9]*")))
|
ledger-amount-decimal-period-regex)))
|
||||||
(with-temp-buffer
|
(with-temp-buffer
|
||||||
(insert str)
|
(insert str)
|
||||||
(goto-char (point-min))
|
(goto-char (point-min))
|
||||||
(cond
|
(cond
|
||||||
((re-search-forward "\"\\(.*\\)\"" nil t)
|
((re-search-forward "\"\\(.*\\)\"" nil t) ; look for quoted commodities
|
||||||
(let ((com (delete-and-extract-region
|
(let ((com (delete-and-extract-region
|
||||||
(match-beginning 1)
|
(match-beginning 1)
|
||||||
(match-end 1))))
|
(match-end 1))))
|
||||||
|
|
@ -127,25 +129,15 @@ longer ones are after the value."
|
||||||
(concat commodity " " val))))
|
(concat commodity " " val))))
|
||||||
|
|
||||||
(defun ledger-read-commodity-string (prompt)
|
(defun ledger-read-commodity-string (prompt)
|
||||||
"Return a commoditizd value (val 'comm') from COMM.
|
(let ((str (read-from-minibuffer
|
||||||
Assumes a space between the value and the commodity."
|
(concat prompt " (" ledger-reconcile-default-commodity "): ")))
|
||||||
(let ((parts (split-string (read-from-minibuffer
|
comm)
|
||||||
(concat prompt " (" ledger-reconcile-default-commodity "): ")))))
|
(if (> (length str) 0)
|
||||||
(if parts
|
(progn
|
||||||
(if (/= (length parts) 2) ;;assume a number was entered and
|
(setq comm (ledger-split-commodity-string str))
|
||||||
;;use default commodity
|
(if (cadr comm)
|
||||||
(list (string-to-number (car parts))
|
comm
|
||||||
ledger-reconcile-default-commodity)
|
(list (car comm) 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)
|
(provide 'ldg-commodities)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,7 @@
|
||||||
(save-excursion
|
(save-excursion
|
||||||
(goto-char (point-min))
|
(goto-char (point-min))
|
||||||
(while (re-search-forward
|
(while (re-search-forward
|
||||||
(concat "^[0-9/.=-]+\\(\\s-+\\*\\)?\\(\\s-+(.*?)\\)?\\s-+"
|
ledger-payee-any-status-regex nil t) ;; matches first line
|
||||||
"\\(.+?\\)\\(\t\\|\n\\| [ \t]\\)") nil t) ;; matches first line
|
|
||||||
(unless (and (>= origin (match-beginning 0))
|
(unless (and (>= origin (match-beginning 0))
|
||||||
(< origin (match-end 0)))
|
(< origin (match-end 0)))
|
||||||
(setq payees-list (cons (match-string-no-properties 3)
|
(setq payees-list (cons (match-string-no-properties 3)
|
||||||
|
|
@ -70,7 +69,7 @@ Return tree structure"
|
||||||
(save-excursion
|
(save-excursion
|
||||||
(goto-char (point-min))
|
(goto-char (point-min))
|
||||||
(while (re-search-forward
|
(while (re-search-forward
|
||||||
"^[ \t]+\\([*!]\\s-+\\)?[[(]?\\(.+?\\)\\(\t\\|\n\\| [ \t]\\)" nil t)
|
ledger-account-any-status-regex nil t)
|
||||||
(unless (and (>= origin (match-beginning 0))
|
(unless (and (>= origin (match-beginning 0))
|
||||||
(< origin (match-end 0)))
|
(< origin (match-end 0)))
|
||||||
(setq account-elements
|
(setq account-elements
|
||||||
|
|
@ -154,7 +153,7 @@ Does not use ledger xact"
|
||||||
(setq rest-of-name (match-string 3))
|
(setq rest-of-name (match-string 3))
|
||||||
;; Start copying the postings
|
;; Start copying the postings
|
||||||
(forward-line)
|
(forward-line)
|
||||||
(while (looking-at ledger-post-account-regex)
|
(while (looking-at ledger-account-any-status-regex)
|
||||||
(setq xacts (cons (buffer-substring-no-properties
|
(setq xacts (cons (buffer-substring-no-properties
|
||||||
(line-beginning-position)
|
(line-beginning-position)
|
||||||
(line-end-position))
|
(line-end-position))
|
||||||
|
|
|
||||||
|
|
@ -26,18 +26,20 @@
|
||||||
|
|
||||||
;;; Code:
|
;;; Code:
|
||||||
|
|
||||||
|
(require 'ldg-regex)
|
||||||
|
|
||||||
(defgroup ledger-faces nil "Ledger mode highlighting" :group 'ledger)
|
(defgroup ledger-faces nil "Ledger mode highlighting" :group 'ledger)
|
||||||
(defface ledger-font-uncleared-face
|
(defface ledger-font-payee-uncleared-face
|
||||||
`((t :foreground "#dc322f" :weight bold ))
|
`((t :foreground "#dc322f" :weight bold ))
|
||||||
"Default face for Ledger"
|
"Default face for Ledger"
|
||||||
:group 'ledger-faces)
|
:group 'ledger-faces)
|
||||||
|
|
||||||
(defface ledger-font-cleared-face
|
(defface ledger-font-payee-cleared-face
|
||||||
`((t :foreground "#657b83" :weight normal ))
|
`((t :foreground "#657b83" :weight normal ))
|
||||||
"Default face for cleared (*) transactions"
|
"Default face for cleared (*) transactions"
|
||||||
:group 'ledger-faces)
|
:group 'ledger-faces)
|
||||||
|
|
||||||
(defface ledger-font-highlight-face
|
(defface ledger-font-xact-highlight-face
|
||||||
`((t :background "#eee8d5"))
|
`((t :background "#eee8d5"))
|
||||||
"Default face for transaction under point"
|
"Default face for transaction under point"
|
||||||
:group 'ledger-faces)
|
:group 'ledger-faces)
|
||||||
|
|
@ -48,7 +50,7 @@
|
||||||
:group 'ledger-faces)
|
:group 'ledger-faces)
|
||||||
|
|
||||||
(defface ledger-font-other-face
|
(defface ledger-font-other-face
|
||||||
`((t :foreground "yellow" ))
|
`((t :foreground "#657b83" :weight bold))
|
||||||
"Default face for other transactions"
|
"Default face for other transactions"
|
||||||
:group 'ledger-faces)
|
:group 'ledger-faces)
|
||||||
|
|
||||||
|
|
@ -57,8 +59,18 @@
|
||||||
"Face for Ledger accounts"
|
"Face for Ledger accounts"
|
||||||
:group 'ledger-faces)
|
:group 'ledger-faces)
|
||||||
|
|
||||||
|
(defface ledger-font-posting-account-cleared-face
|
||||||
|
`((t :foreground "#657b83" ))
|
||||||
|
"Face for Ledger accounts"
|
||||||
|
:group 'ledger-faces)
|
||||||
|
|
||||||
|
(defface ledger-font-posting-account-pending-face
|
||||||
|
`((t :foreground "#cb4b16" ))
|
||||||
|
"Face for Ledger accounts"
|
||||||
|
:group 'ledger-faces)
|
||||||
|
|
||||||
(defface ledger-font-posting-amount-face
|
(defface ledger-font-posting-amount-face
|
||||||
`((t :foreground "yellow" ))
|
`((t :foreground "#cb4b16" ))
|
||||||
"Face for Ledger amounts"
|
"Face for Ledger amounts"
|
||||||
:group 'ledger-faces)
|
:group 'ledger-faces)
|
||||||
|
|
||||||
|
|
@ -99,19 +111,30 @@
|
||||||
|
|
||||||
|
|
||||||
(defvar ledger-font-lock-keywords
|
(defvar ledger-font-lock-keywords
|
||||||
'(("^[0-9]+[-/.=][-/.=0-9]+\\s-\\!\\s-+\\(([^)]+)\\s-+\\)?\\([^*].+?\\)\\(\\( ;\\| ;\\|$\\)\\)" 2 'ledger-font-pending-face)
|
`( ;; (,ledger-other-entries-regex 1
|
||||||
("^[0-9]+[-/.=][-/.=0-9]+\\s-\\*\\s-+\\(([^)]+)\\s-+\\)?\\([^*].+?\\)\\(\\( ;\\| ;\\|$\\)\\)" 2 'ledger-font-cleared-face)
|
;; ledger-font-other-face)
|
||||||
("^[0-9]+[-/.=][-/.=0-9]+\\s-+\\(([^)]+)\\s-+\\)?\\([^*].+?\\)\\(\\( ;\\| ;\\|$\\)\\)" 2 'ledger-font-uncleared-face)
|
(,ledger-comment-regex 2
|
||||||
("^\\s-+\\([*]\\s-*\\)?\\(\\([[(]\\)?[^*:
|
'ledger-font-comment-face)
|
||||||
]+?:\\([^]);
|
(,ledger-payee-pending-regex 2
|
||||||
]\\|\\s-\\)+?\\([])]\\)?\\)\\( \\| \\|$\\)"
|
'ledger-font-payee-pending-face) ; Works
|
||||||
2 'ledger-font-posting-account-face) ; works
|
(,ledger-payee-cleared-regex 2
|
||||||
("\\( \\| \\|^\\)\\(;.*\\)" 2 'ledger-font-comment-face) ; works
|
'ledger-font-payee-cleared-face) ; Works
|
||||||
("^\\([~=].+\\)" 1 ledger-font-other-face)
|
(,ledger-payee-uncleared-regex 2
|
||||||
("^\\([A-Za-z]+ .+\\)" 1 ledger-font-other-face))
|
'ledger-font-payee-uncleared-face) ; Works
|
||||||
|
(,ledger-account-cleared-regex 2
|
||||||
|
'ledger-font-posting-account-cleared-face) ; Works
|
||||||
|
(,ledger-account-pending-regex 2
|
||||||
|
'ledger-font-posting-account-pending-face) ; Works
|
||||||
|
(,ledger-account-any-status-regex 2
|
||||||
|
'ledger-font-posting-account-face)) ; Works
|
||||||
"Expressions to highlight in Ledger mode.")
|
"Expressions to highlight in Ledger mode.")
|
||||||
|
|
||||||
|
|
||||||
|
;; (defvar ledger-font-lock-keywords
|
||||||
|
;; `( (,ledger-other-entries-regex 1
|
||||||
|
;; ledger-font-other-face))
|
||||||
|
;; "Expressions to highlight in Ledger mode.")
|
||||||
|
|
||||||
(provide 'ldg-fonts)
|
(provide 'ldg-fonts)
|
||||||
|
|
||||||
;;; ldg-fonts.el ends here
|
;;; ldg-fonts.el ends here
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@
|
||||||
;;; Commentary:
|
;;; Commentary:
|
||||||
;; Determine the ledger environment
|
;; Determine the ledger environment
|
||||||
|
|
||||||
|
(require 'ldg-regex)
|
||||||
|
|
||||||
(defcustom ledger-init-file-name "~/.ledgerrc"
|
(defcustom ledger-init-file-name "~/.ledgerrc"
|
||||||
"Location of the ledger initialization file. nil if you don't have one"
|
"Location of the ledger initialization file. nil if you don't have one"
|
||||||
:group 'ledger-exec)
|
:group 'ledger-exec)
|
||||||
|
|
@ -32,7 +34,7 @@
|
||||||
(with-current-buffer file
|
(with-current-buffer file
|
||||||
(setq ledger-environment-alist nil)
|
(setq ledger-environment-alist nil)
|
||||||
(goto-char (point-min))
|
(goto-char (point-min))
|
||||||
(while (re-search-forward "^--.+?\\($\\|[ ]\\)" nil t )
|
(while (re-search-forward ledger-init-string-regex nil t )
|
||||||
(let ((matchb (match-beginning 0)) ;; save the match data, string-match stamp on it
|
(let ((matchb (match-beginning 0)) ;; save the match data, string-match stamp on it
|
||||||
(matche (match-end 0)))
|
(matche (match-end 0)))
|
||||||
(end-of-line)
|
(end-of-line)
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ Can be pcomplete, or align-posting"
|
||||||
|
|
||||||
(ledger-init-load-init-file)
|
(ledger-init-load-init-file)
|
||||||
|
|
||||||
(setq indent-region-function 'ledger-post-align-postings)
|
(set (make-local-variable 'indent-region-function) 'ledger-post-align-postings)
|
||||||
|
|
||||||
(let ((map (current-local-map)))
|
(let ((map (current-local-map)))
|
||||||
(define-key map [(control ?c) (control ?a)] 'ledger-add-transaction)
|
(define-key map [(control ?c) (control ?a)] 'ledger-add-transaction)
|
||||||
|
|
@ -101,7 +101,7 @@ Can be pcomplete, or align-posting"
|
||||||
(define-key map [(control ?c) (control ?d)] 'ledger-delete-current-transaction)
|
(define-key map [(control ?c) (control ?d)] 'ledger-delete-current-transaction)
|
||||||
(define-key map [(control ?c) (control ?e)] 'ledger-toggle-current-transaction)
|
(define-key map [(control ?c) (control ?e)] 'ledger-toggle-current-transaction)
|
||||||
(define-key map [(control ?c) (control ?f)] 'ledger-occur)
|
(define-key map [(control ?c) (control ?f)] 'ledger-occur)
|
||||||
(define-key map [(control ?c) (control ?k)] 'ledger-copy-transaction)
|
(define-key map [(control ?c) (control ?k)] 'ledger-copy-transaction-at-point)
|
||||||
(define-key map [(control ?c) (control ?m)] 'ledger-set-month)
|
(define-key map [(control ?c) (control ?m)] 'ledger-set-month)
|
||||||
(define-key map [(control ?c) (control ?r)] 'ledger-reconcile)
|
(define-key map [(control ?c) (control ?r)] 'ledger-reconcile)
|
||||||
(define-key map [(control ?c) (control ?s)] 'ledger-sort-region)
|
(define-key map [(control ?c) (control ?s)] 'ledger-sort-region)
|
||||||
|
|
@ -146,7 +146,7 @@ Can be pcomplete, or align-posting"
|
||||||
(define-key map [sort-reg] '(menu-item "Sort Region" ledger-sort-region :enable mark-active))
|
(define-key map [sort-reg] '(menu-item "Sort Region" ledger-sort-region :enable mark-active))
|
||||||
(define-key map [align-reg] '(menu-item "Align Region" ledger-post-align-postings :enable mark-active))
|
(define-key map [align-reg] '(menu-item "Align Region" ledger-post-align-postings :enable mark-active))
|
||||||
(define-key map [sep2] '(menu-item "--"))
|
(define-key map [sep2] '(menu-item "--"))
|
||||||
(define-key map [copy-xact] '(menu-item "Copy Trans at Point" ledger-copy-transaction))
|
(define-key map [copy-xact] '(menu-item "Copy Trans at Point" ledger-copy-transaction-at-point))
|
||||||
(define-key map [toggle-post] '(menu-item "Toggle Current Posting" ledger-toggle-current))
|
(define-key map [toggle-post] '(menu-item "Toggle Current Posting" ledger-toggle-current))
|
||||||
(define-key map [toggle-xact] '(menu-item "Toggle Current Transaction" ledger-toggle-current-transaction))
|
(define-key map [toggle-xact] '(menu-item "Toggle Current Transaction" ledger-toggle-current-transaction))
|
||||||
(define-key map [sep4] '(menu-item "--"))
|
(define-key map [sep4] '(menu-item "--"))
|
||||||
|
|
@ -174,43 +174,6 @@ Return the difference in the format of a time value."
|
||||||
(list (- (car t1) (car t2) (if borrow 1 0))
|
(list (- (car t1) (car t2) (if borrow 1 0))
|
||||||
(- (+ (if borrow 65536 0) (cadr t1)) (cadr t2)))))
|
(- (+ (if borrow 65536 0) (cadr t1)) (cadr t2)))))
|
||||||
|
|
||||||
(defun ledger-find-slot (moment)
|
|
||||||
"Find the right place in the buffer for a transaction at MOMENT.
|
|
||||||
MOMENT is an encoded date"
|
|
||||||
(catch 'found
|
|
||||||
(ledger-iterate-transactions
|
|
||||||
(function
|
|
||||||
(lambda (start date mark desc)
|
|
||||||
(if (ledger-time-less-p moment date)
|
|
||||||
(throw 'found t)))))))
|
|
||||||
|
|
||||||
(defun ledger-iterate-transactions (callback)
|
|
||||||
"Iterate through each transaction call CALLBACK for each."
|
|
||||||
(goto-char (point-min))
|
|
||||||
(let* ((now (current-time))
|
|
||||||
(current-year (nth 5 (decode-time now))))
|
|
||||||
(while (not (eobp))
|
|
||||||
(when (looking-at
|
|
||||||
(concat "\\(Y\\s-+\\([0-9]+\\)\\|"
|
|
||||||
"\\([0-9]\\{4\\}+\\)?[./-]?"
|
|
||||||
"\\([0-9]+\\)[./-]\\([0-9]+\\)\\s-+"
|
|
||||||
"\\(\\*\\s-+\\)?\\(.+\\)\\)"))
|
|
||||||
(let ((found (match-string 2)))
|
|
||||||
(if found
|
|
||||||
(setq current-year (string-to-number found))
|
|
||||||
(let ((start (match-beginning 0))
|
|
||||||
(year (match-string 3))
|
|
||||||
(month (string-to-number (match-string 4)))
|
|
||||||
(day (string-to-number (match-string 5)))
|
|
||||||
(mark (match-string 6))
|
|
||||||
(desc (match-string 7)))
|
|
||||||
(if (and year (> (length year) 0))
|
|
||||||
(setq year (string-to-number year)))
|
|
||||||
(funcall callback start
|
|
||||||
(encode-time 0 0 0 day month
|
|
||||||
(or year current-year))
|
|
||||||
mark desc)))))
|
|
||||||
(forward-line))))
|
|
||||||
|
|
||||||
(defun ledger-set-year (newyear)
|
(defun ledger-set-year (newyear)
|
||||||
"Set ledger's idea of the current year to the prefix argument NEWYEAR."
|
"Set ledger's idea of the current year to the prefix argument NEWYEAR."
|
||||||
|
|
@ -229,7 +192,7 @@ MOMENT is an encoded date"
|
||||||
(defun ledger-add-transaction (transaction-text &optional insert-at-point)
|
(defun ledger-add-transaction (transaction-text &optional insert-at-point)
|
||||||
"Use ledger xact TRANSACTION-TEXT to add a transaction to the buffer.
|
"Use ledger xact TRANSACTION-TEXT to add a transaction to the buffer.
|
||||||
If INSERT-AT-POINT is non-nil insert the transaction
|
If INSERT-AT-POINT is non-nil insert the transaction
|
||||||
there, otherwise call `ledger-find-slot' to insert it at the
|
there, otherwise call `ledger-xact-find-slot' to insert it at the
|
||||||
correct chronological place in the buffer."
|
correct chronological place in the buffer."
|
||||||
(interactive (list
|
(interactive (list
|
||||||
(read-string "Transaction: " (concat ledger-year "/" ledger-month "/"))))
|
(read-string "Transaction: " (concat ledger-year "/" ledger-month "/"))))
|
||||||
|
|
@ -240,12 +203,12 @@ correct chronological place in the buffer."
|
||||||
exit-code)
|
exit-code)
|
||||||
(unless insert-at-point
|
(unless insert-at-point
|
||||||
(let ((date (car args)))
|
(let ((date (car args)))
|
||||||
(if (string-match "\\([0-9]+\\)[-/]\\([0-9]+\\)[-/]\\([0-9]+\\)" date)
|
(if (string-match ledger-iso-date-regexp date)
|
||||||
(setq date
|
(setq date
|
||||||
(encode-time 0 0 0 (string-to-number (match-string 3 date))
|
(encode-time 0 0 0 (string-to-number (match-string 4 date))
|
||||||
(string-to-number (match-string 2 date))
|
(string-to-number (match-string 3 date))
|
||||||
(string-to-number (match-string 1 date)))))
|
(string-to-number (match-string 2 date)))))
|
||||||
(ledger-find-slot date)))
|
(ledger-xact-find-slot date)))
|
||||||
(if (> (length args) 1)
|
(if (> (length args) 1)
|
||||||
(save-excursion
|
(save-excursion
|
||||||
(insert
|
(insert
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
;;; Commentary:
|
;;; Commentary:
|
||||||
;; Load up the ledger mode
|
;; Load up the ledger mode
|
||||||
|
(require 'ldg-regex)
|
||||||
(require 'esh-util)
|
(require 'esh-util)
|
||||||
(require 'esh-arg)
|
(require 'esh-arg)
|
||||||
(require 'ldg-commodities)
|
(require 'ldg-commodities)
|
||||||
|
|
@ -43,7 +44,6 @@
|
||||||
(require 'ldg-occur)
|
(require 'ldg-occur)
|
||||||
(require 'ldg-post)
|
(require 'ldg-post)
|
||||||
(require 'ldg-reconcile)
|
(require 'ldg-reconcile)
|
||||||
(require 'ldg-regex)
|
|
||||||
(require 'ldg-report)
|
(require 'ldg-report)
|
||||||
(require 'ldg-sort)
|
(require 'ldg-sort)
|
||||||
(require 'ldg-state)
|
(require 'ldg-state)
|
||||||
|
|
|
||||||
|
|
@ -115,43 +115,38 @@ PROMPT is a string to prompt with. CHOICES is a list of
|
||||||
(delete-char 1)))))))
|
(delete-char 1)))))))
|
||||||
(goto-char pos)))
|
(goto-char pos)))
|
||||||
|
|
||||||
(defvar ledger-post-amount-regex
|
|
||||||
(concat "\\( \\|\t\\| \t\\)[ \t]*-?"
|
|
||||||
"\\([A-Z$€£_]+ *\\)?"
|
|
||||||
"\\(-?[0-9,]+?\\)"
|
|
||||||
"\\(.[0-9]+\\)?"
|
|
||||||
"\\( *[[:word:]€£_\"]+\\)?"
|
|
||||||
"\\([ \t]*[@={]@?[^\n;]+?\\)?"
|
|
||||||
"\\([ \t]+;.+?\\|[ \t]*\\)?$"))
|
|
||||||
|
|
||||||
(defsubst ledger-next-amount (&optional end)
|
(defsubst ledger-next-amount (&optional end)
|
||||||
"Move point to the next amount, as long as it is not past END.
|
"Move point to the next amount, as long as it is not past END.
|
||||||
Return the width of the amount field as an integer and leave
|
Return the width of the amount field as an integer and leave
|
||||||
point at beginning of the commodity."
|
point at beginning of the commodity."
|
||||||
;;(beginning-of-line)
|
;;(beginning-of-line)
|
||||||
(when (re-search-forward ledger-post-amount-regex end t)
|
(when (re-search-forward ledger-amount-regex end t)
|
||||||
(goto-char (match-beginning 0))
|
(goto-char (match-beginning 0))
|
||||||
(skip-syntax-forward " ")
|
(skip-syntax-forward " ")
|
||||||
(- (or (match-end 4)
|
(- (or (match-end 4)
|
||||||
(match-end 3)) (point))))
|
(match-end 3)) (point))))
|
||||||
|
|
||||||
(defvar ledger-post-account-regex
|
|
||||||
"\\(^[ \t]+\\)\\(.+?\\)\\( \\|\n\\)")
|
|
||||||
|
|
||||||
(defun ledger-next-account (&optional end)
|
(defun ledger-next-account (&optional end)
|
||||||
"Move point to the beginning of the next account, or status marker (!*), as long as it is not past END.
|
"Move point to the beginning of the next account, or status marker (!*), as long as it is not past END.
|
||||||
Return the column of the beginning of the account and leave point
|
Return the column of the beginning of the account and leave point
|
||||||
at beginning of account"
|
at beginning of account"
|
||||||
(if (> end (point))
|
(if (> end (point))
|
||||||
(when (re-search-forward ledger-post-account-regex (1+ end) t)
|
(when (re-search-forward ledger-account-any-status-regex (1+ end) t)
|
||||||
;; the 1+ is to make sure we can catch the newline
|
;; the 1+ is to make sure we can catch the newline
|
||||||
(goto-char (match-beginning 2))
|
(if (match-beginning 1)
|
||||||
|
(goto-char (match-beginning 1))
|
||||||
|
(goto-char (match-beginning 2)))
|
||||||
(current-column))))
|
(current-column))))
|
||||||
|
|
||||||
(defun ledger-post-align-postings (&optional beg end)
|
(defun ledger-post-align-postings (&optional beg end)
|
||||||
"Align all accounts and amounts within region, if there is no
|
"Align all accounts and amounts within region, if there is no
|
||||||
region align the posting on the current line."
|
region align the posting on the current line."
|
||||||
(interactive)
|
(interactive)
|
||||||
|
(assert (eq major-mode 'ledger-mode))
|
||||||
|
|
||||||
(save-excursion
|
(save-excursion
|
||||||
(if (or (not (mark))
|
(if (or (not (mark))
|
||||||
(not (use-region-p)))
|
(not (use-region-p)))
|
||||||
|
|
@ -254,7 +249,7 @@ BEG, END, and LEN control how far it can align."
|
||||||
(defun ledger-post-setup ()
|
(defun ledger-post-setup ()
|
||||||
"Configure `ledger-mode' to auto-align postings."
|
"Configure `ledger-mode' to auto-align postings."
|
||||||
(add-hook 'after-change-functions 'ledger-post-maybe-align t t)
|
(add-hook 'after-change-functions 'ledger-post-maybe-align t t)
|
||||||
(add-hook 'after-save-hook #'(lambda () (setq ledger-post-current-list nil))))
|
(add-hook 'after-save-hook #'(lambda () (setq ledger-post-current-list nil)) t t))
|
||||||
|
|
||||||
|
|
||||||
(defun ledger-post-read-account-with-prompt (prompt)
|
(defun ledger-post-read-account-with-prompt (prompt)
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,16 @@ reconcile-finish will mark all pending posting cleared."
|
||||||
:type 'boolean
|
:type 'boolean
|
||||||
:group 'ledger-reconcile)
|
:group 'ledger-reconcile)
|
||||||
|
|
||||||
|
(defcustom ledger-reconcile-default-date-format "%Y/%m/%d"
|
||||||
|
"Default date format for the reconcile buffer"
|
||||||
|
:type 'string
|
||||||
|
:group 'ledger-reconcile)
|
||||||
|
|
||||||
|
(defcustom ledger-reconcile-target-prompt-string "Target amount for reconciliation "
|
||||||
|
"Default prompt for recon target prompt"
|
||||||
|
:type 'string
|
||||||
|
:group 'ledger-reconcile)
|
||||||
|
|
||||||
|
|
||||||
(defun ledger-reconcile-get-cleared-or-pending-balance ()
|
(defun ledger-reconcile-get-cleared-or-pending-balance ()
|
||||||
"Calculate the cleared or pending balance of the account."
|
"Calculate the cleared or pending balance of the account."
|
||||||
|
|
@ -217,8 +227,7 @@ Return the number of uncleared xacts found."
|
||||||
(set-buffer-modified-p nil)
|
(set-buffer-modified-p nil)
|
||||||
(ledger-display-balance)
|
(ledger-display-balance)
|
||||||
(goto-char curpoint)
|
(goto-char curpoint)
|
||||||
;; (ledger-reconcile-visit t)
|
(ledger-reconcile-visit t))))
|
||||||
)))
|
|
||||||
|
|
||||||
(defun ledger-reconcile-finish ()
|
(defun ledger-reconcile-finish ()
|
||||||
"Mark all pending posting or transactions as cleared.
|
"Mark all pending posting or transactions as cleared.
|
||||||
|
|
@ -299,7 +308,7 @@ POSTING is used in `ledger-clear-whole-transactions' is nil."
|
||||||
(insert (format "%s %-4s %-30s %-30s %15s\n"
|
(insert (format "%s %-4s %-30s %-30s %15s\n"
|
||||||
(format-time-string (if date-format
|
(format-time-string (if date-format
|
||||||
date-format
|
date-format
|
||||||
"%Y/%m/%d") (nth 2 xact))
|
ledger-reconcile-default-date-format) (nth 2 xact))
|
||||||
(if (nth 3 xact)
|
(if (nth 3 xact)
|
||||||
(nth 3 xact)
|
(nth 3 xact)
|
||||||
"")
|
"")
|
||||||
|
|
@ -409,7 +418,7 @@ moved and recentered. If they aren't strange things happen."
|
||||||
(defun ledger-reconcile-change-target ()
|
(defun ledger-reconcile-change-target ()
|
||||||
"Change the target amount for the reconciliation process."
|
"Change the target amount for the reconciliation process."
|
||||||
(interactive)
|
(interactive)
|
||||||
(setq ledger-target (ledger-read-commodity-string "Set reconciliation target")))
|
(setq ledger-target (ledger-read-commodity-string ledger-reconcile-target-prompt-string)))
|
||||||
|
|
||||||
(define-derived-mode ledger-reconcile-mode text-mode "Reconcile"
|
(define-derived-mode ledger-reconcile-mode text-mode "Reconcile"
|
||||||
"A mode for reconciling ledger entries."
|
"A mode for reconciling ledger entries."
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,53 @@
|
||||||
(eval-when-compile
|
(eval-when-compile
|
||||||
(require 'cl))
|
(require 'cl))
|
||||||
|
|
||||||
(defvar ledger-date-regex
|
(defconst ledger-amount-decimal-comma-regex
|
||||||
"\\([0-9]+\\)[/-]\\([0-9]+\\)[/-]\\([0-9]+\\)")
|
"-?[1-9][0-9.]*[,]?[0-9]*")
|
||||||
|
|
||||||
|
(defconst ledger-amount-decimal-period-regex
|
||||||
|
"-?[1-9][0-9.]*[.]?[0-9]*")
|
||||||
|
|
||||||
|
(defconst ledger-other-entries-regex
|
||||||
|
"\\(^[~=A-Za-z].+\\)+")
|
||||||
|
|
||||||
|
;\\|^\\([A-Za-z] .+\\)\\)
|
||||||
|
|
||||||
|
(defconst ledger-comment-regex
|
||||||
|
"\\( \\| \\|^\\)\\(;.*\\)")
|
||||||
|
|
||||||
|
(defconst ledger-payee-any-status-regex
|
||||||
|
"^[0-9/.=-]+\\(\\s-+\\*\\)?\\(\\s-+(.*?)\\)?\\s-+\\(.+?\\)\\(\t\\|\n\\| [ \t]\\)")
|
||||||
|
|
||||||
|
(defconst ledger-payee-pending-regex
|
||||||
|
"^[0-9]+[-/.=][-/.=0-9]+\\s-\\!\\s-+\\(([^)]+)\\s-+\\)?\\([^*].+?\\)\\(;\\|$\\)")
|
||||||
|
|
||||||
|
(defconst ledger-payee-cleared-regex
|
||||||
|
"^[0-9]+[-/.=][-/.=0-9]+\\s-\\*\\s-+\\(([^)]+)\\s-+\\)?\\([^*].+?\\)\\(;\\|$\\)")
|
||||||
|
|
||||||
|
(defconst ledger-payee-uncleared-regex
|
||||||
|
"^[0-9]+[-/.=][-/.=0-9]+\\s-+\\(([^)]+)\\s-+\\)?\\([^*].+?\\)\\(;\\|$\\)")
|
||||||
|
|
||||||
|
(defconst ledger-init-string-regex
|
||||||
|
"^--.+?\\($\\|[ ]\\)")
|
||||||
|
|
||||||
|
(defconst ledger-account-any-status-regex
|
||||||
|
"^[ \t]+\\([*!]\\s-+\\)?\\([[(]?.+?\\)\\(\t\\|\n\\| [ \t]\\)")
|
||||||
|
|
||||||
|
(defconst ledger-account-pending-regex
|
||||||
|
"\\(^[ \t]+\\)\\(!.+?\\)\\( \\|$\\)")
|
||||||
|
|
||||||
|
(defconst ledger-account-cleared-regex
|
||||||
|
"\\(^[ \t]+\\)\\(\\*.+?\\)\\( \\|$\\)")
|
||||||
|
|
||||||
|
(defconst ledger-amount-regex
|
||||||
|
(concat "\\( \\|\t\\| \t\\)[ \t]*-?"
|
||||||
|
"\\([A-Z$€£_]+ *\\)?"
|
||||||
|
"\\(-?[0-9,]+?\\)"
|
||||||
|
"\\(.[0-9]+\\)?"
|
||||||
|
"\\( *[[:word:]€£_\"]+\\)?"
|
||||||
|
"\\([ \t]*[@={]@?[^\n;]+?\\)?"
|
||||||
|
"\\([ \t]+;.+?\\|[ \t]*\\)?$"))
|
||||||
|
|
||||||
|
|
||||||
(defmacro ledger-define-regexp (name regex docs &rest args)
|
(defmacro ledger-define-regexp (name regex docs &rest args)
|
||||||
"Simplify the creation of a Ledger regex and helper functions."
|
"Simplify the creation of a Ledger regex and helper functions."
|
||||||
|
|
@ -122,23 +167,23 @@
|
||||||
|
|
||||||
(put 'ledger-define-regexp 'lisp-indent-function 1)
|
(put 'ledger-define-regexp 'lisp-indent-function 1)
|
||||||
|
|
||||||
(ledger-define-regexp date
|
(ledger-define-regexp iso-date
|
||||||
(let ((sep '(or ?- (any ?. ?/)))) ; can't do (any ?- ?. ?/) due to bug
|
( let ((sep '(or ?- ?/)))
|
||||||
(rx (group
|
(rx (group
|
||||||
(and (? (= 4 num)
|
(and (group (? (= 4 num)))
|
||||||
(eval sep))
|
|
||||||
(and num (? num))
|
|
||||||
(eval sep)
|
(eval sep)
|
||||||
(and num (? num))))))
|
(group (and num (? num)))
|
||||||
|
(eval sep)
|
||||||
|
(group (and num (? num)))))))
|
||||||
"Match a single date, in its 'written' form.")
|
"Match a single date, in its 'written' form.")
|
||||||
|
|
||||||
(ledger-define-regexp full-date
|
(ledger-define-regexp full-date
|
||||||
(macroexpand
|
(macroexpand
|
||||||
`(rx (and (regexp ,ledger-date-regexp)
|
`(rx (and (regexp ,ledger-iso-date-regexp)
|
||||||
(? (and ?= (regexp ,ledger-date-regexp))))))
|
(? (and ?= (regexp ,ledger-iso-date-regexp))))))
|
||||||
"Match a compound date, of the form ACTUAL=EFFECTIVE"
|
"Match a compound date, of the form ACTUAL=EFFECTIVE"
|
||||||
(actual date)
|
(actual iso-date)
|
||||||
(effective date))
|
(effective iso-date))
|
||||||
|
|
||||||
(ledger-define-regexp state
|
(ledger-define-regexp state
|
||||||
(rx (group (any ?! ?*)))
|
(rx (group (any ?! ?*)))
|
||||||
|
|
@ -235,7 +280,7 @@
|
||||||
(macroexpand
|
(macroexpand
|
||||||
`(rx (* (+ blank)
|
`(rx (* (+ blank)
|
||||||
(or (and ?\{ (regexp ,ledger-commoditized-amount-regexp) ?\})
|
(or (and ?\{ (regexp ,ledger-commoditized-amount-regexp) ?\})
|
||||||
(and ?\[ (regexp ,ledger-date-regexp) ?\])
|
(and ?\[ (regexp ,ledger-iso-date-regexp) ?\])
|
||||||
(and ?\( (not (any ?\))) ?\))))))
|
(and ?\( (not (any ?\))) ?\))))))
|
||||||
"")
|
"")
|
||||||
|
|
||||||
|
|
@ -271,4 +316,12 @@
|
||||||
(amount full-amount)
|
(amount full-amount)
|
||||||
(note end-note))
|
(note end-note))
|
||||||
|
|
||||||
|
(defconst ledger-iterate-regex
|
||||||
|
(concat "\\(Y\\s-+\\([0-9]+\\)\\|" ;; Catches a Y directive
|
||||||
|
ledger-iso-date-regexp
|
||||||
|
"\\([ *!]+\\)" ;; mark
|
||||||
|
"\\((.*)\\)" ;; code
|
||||||
|
"\\(.*\\)" ;; desc
|
||||||
|
"\\)"))
|
||||||
|
|
||||||
(provide 'ldg-regex)
|
(provide 'ldg-regex)
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,8 @@
|
||||||
|
|
||||||
(defun ledger-next-record-function ()
|
(defun ledger-next-record-function ()
|
||||||
"Move point to next transaction."
|
"Move point to next transaction."
|
||||||
(if (re-search-forward
|
(if (re-search-forward ledger-payee-any-status-regex
|
||||||
(concat "^[0-9/.=-]+\\(\\s-+\\*\\)?\\(\\s-+(.*?)\\)?\\s-+"
|
nil t)
|
||||||
"\\(.+?\\)\\(\t\\|\n\\| [ \t]\\)") nil t)
|
|
||||||
(goto-char (match-beginning 0))
|
(goto-char (match-beginning 0))
|
||||||
(goto-char (point-max))))
|
(goto-char (point-max))))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ within the transaction."
|
||||||
(cadr exts)
|
(cadr exts)
|
||||||
(current-buffer) t nil)))
|
(current-buffer) t nil)))
|
||||||
(move-overlay ovl (car exts) (cadr exts)))
|
(move-overlay ovl (car exts) (cadr exts)))
|
||||||
(overlay-put ovl 'face 'ledger-font-highlight-face)
|
(overlay-put ovl 'face 'ledger-font-xact-highlight-face)
|
||||||
(overlay-put ovl 'priority 100))))
|
(overlay-put ovl 'priority 100))))
|
||||||
|
|
||||||
(defun ledger-xact-payee ()
|
(defun ledger-xact-payee ()
|
||||||
|
|
@ -76,6 +76,41 @@ within the transaction."
|
||||||
(ledger-context-field-value context-info 'payee)
|
(ledger-context-field-value context-info 'payee)
|
||||||
nil))))
|
nil))))
|
||||||
|
|
||||||
|
(defun ledger-xact-find-slot (moment)
|
||||||
|
"Find the right place in the buffer for a transaction at MOMENT.
|
||||||
|
MOMENT is an encoded date"
|
||||||
|
(catch 'found
|
||||||
|
(ledger-xact-iterate-transactions
|
||||||
|
(function
|
||||||
|
(lambda (start date mark desc)
|
||||||
|
(if (ledger-time-less-p moment date)
|
||||||
|
(throw 'found t)))))))
|
||||||
|
|
||||||
|
(defun ledger-xact-iterate-transactions (callback)
|
||||||
|
"Iterate through each transaction call CALLBACK for each."
|
||||||
|
(goto-char (point-min))
|
||||||
|
(let* ((now (current-time))
|
||||||
|
(current-year (nth 5 (decode-time now))))
|
||||||
|
(while (not (eobp))
|
||||||
|
(when (looking-at ledger-iterate-regex)
|
||||||
|
(let ((found-y-p (match-string 2)))
|
||||||
|
(if found-y-p
|
||||||
|
(setq current-year (string-to-number found-y-p)) ;; a Y directive was found
|
||||||
|
(let ((start (match-beginning 0))
|
||||||
|
(year (match-string 4))
|
||||||
|
(month (string-to-number (match-string 5)))
|
||||||
|
(day (string-to-number (match-string 6)))
|
||||||
|
(mark (match-string 7))
|
||||||
|
(code (match-string 8))
|
||||||
|
(desc (match-string 9)))
|
||||||
|
(if (and year (> (length year) 0))
|
||||||
|
(setq year (string-to-number year)))
|
||||||
|
(funcall callback start
|
||||||
|
(encode-time 0 0 0 day month
|
||||||
|
(or year current-year))
|
||||||
|
mark desc)))))
|
||||||
|
(forward-line))))
|
||||||
|
|
||||||
(defsubst ledger-goto-line (line-number)
|
(defsubst ledger-goto-line (line-number)
|
||||||
"Rapidly move point to line LINE-NUMBER."
|
"Rapidly move point to line LINE-NUMBER."
|
||||||
(goto-char (point-min))
|
(goto-char (point-min))
|
||||||
|
|
@ -106,17 +141,17 @@ within the transaction."
|
||||||
(extents (ledger-find-xact-extents (point)))
|
(extents (ledger-find-xact-extents (point)))
|
||||||
(transaction (buffer-substring-no-properties (car extents) (cadr extents)))
|
(transaction (buffer-substring-no-properties (car extents) (cadr extents)))
|
||||||
encoded-date)
|
encoded-date)
|
||||||
(if (string-match ledger-date-regex date)
|
(if (string-match ledger-iso-date-regexp date)
|
||||||
(setq encoded-date
|
(setq encoded-date
|
||||||
(encode-time 0 0 0 (string-to-number (match-string 3 date))
|
(encode-time 0 0 0 (string-to-number (match-string 4 date))
|
||||||
(string-to-number (match-string 2 date))
|
(string-to-number (match-string 3 date))
|
||||||
(string-to-number (match-string 1 date)))))
|
(string-to-number (match-string 2 date)))))
|
||||||
(ledger-find-slot encoded-date)
|
(ledger-xact-find-slot encoded-date)
|
||||||
(insert transaction "\n")
|
(insert transaction "\n")
|
||||||
(backward-paragraph)
|
(backward-paragraph 2)
|
||||||
(re-search-forward ledger-date-regex)
|
(re-search-forward ledger-iso-date-regexp)
|
||||||
(replace-match date)
|
(replace-match date)
|
||||||
(re-search-forward "[1-9][0-9]+\.[0-9]+")))
|
(ledger-next-amount)))
|
||||||
|
|
||||||
(provide 'ldg-xact)
|
(provide 'ldg-xact)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue