diff options
-rw-r--r-- | ledger.cc | 2 | ||||
-rw-r--r-- | ledger.el | 146 | ||||
-rw-r--r-- | reports.cc | 30 |
3 files changed, 108 insertions, 70 deletions
@@ -63,7 +63,7 @@ void transaction::print(std::ostream& out, bool display_quantity, void entry::print(std::ostream& out, bool shortcut) const { char buf[32]; - std::strftime(buf, 31, "%Y.%m.%d ", std::localtime(&date)); + std::strftime(buf, 31, "%Y/%m/%d ", std::localtime(&date)); out << buf; if (cleared) @@ -37,16 +37,66 @@ (defvar ledger-version "1.1" "The version of ledger.el currently loaded") +(defun ledger-iterate-entries (callback) + (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-find-slot (moment) + (catch 'found + (ledger-iterate-entries + (function + (lambda (start date mark desc) + (if (time-less-p moment date) + (throw 'found t))))))) + (defun ledger-add-entry (entry) (interactive - (list (read-string "Entry: " - (format-time-string "%m.%d " (current-time))))) - (let ((args (mapcar 'shell-quote-argument (split-string entry)))) - (shell-command - (concat "ledger entry " - (mapconcat 'identity args " ")) t) - (delete-char 5) - (exchange-point-and-mark))) + (list (read-string "Entry: " (format-time-string "%m/%d ")))) + (let ((args (mapcar 'shell-quote-argument (split-string entry))) + date entry) + (with-temp-buffer + (shell-command + (concat "ledger entry " + (mapconcat 'identity args " ")) t) + (setq date (buffer-substring (point) (+ (point) 10))) + (setq entry (buffer-substring (+ (point) 5) (point-max)))) + (if (string-match "\\([0-9]+\\)/\\([0-9]+\\)/\\([0-9]+\\)" date) + (setq date (encode-time 0 0 0 (string-to-int (match-string 3 date)) + (string-to-int (match-string 2 date)) + (string-to-int (match-string 1 date))))) + (ledger-find-slot date) + (insert entry))) + +(defun ledger-expand-entry () + (interactive) + (ledger-add-entry (prog1 + (buffer-substring (line-beginning-position) + (line-end-position)) + (delete-region (line-beginning-position) + (1+ (line-end-position)))))) (defun ledger-toggle-current () (interactive) @@ -64,57 +114,37 @@ (define-derived-mode ledger-mode text-mode "Ledger" "A mode for editing ledger data files." - (setq comment-start ";" comment-end nil) + (setq comment-start ";" comment-end nil + indent-tabs-mode nil) (let ((map (current-local-map))) - (define-key map [(control ?c) (control ?n)] 'ledger-add-entry) - (define-key map [(control ?c) (control ?c)] - 'ledger-toggle-current))) - -(defun ledger-parse-entries (account) - (let* ((now (current-time)) - (current-year (nth 5 (decode-time now))) - (then now) - entries) - ;; `then' is 45 days ago - (setq then (time-subtract then (seconds-to-time (* 45 24 60 60)))) - (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)) - total when) - (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))) - (setq when (encode-time 0 0 0 day month - (or year current-year))) - (when (or (not mark) (time-less-p then when)) - (forward-line) - (setq total 0.0) - (while (looking-at - (concat "\\s-+\\([A-Za-z_].+?\\)\\(\\s-*$\\| \\s-*" - "\\([^0-9]+\\)\\s-*\\([0-9.]+\\)\\)")) - (let ((acct (match-string 1)) - (amt (match-string 4))) - (if amt - (setq amt (string-to-number amt) - total (+ total amt))) - (if (string= account acct) - (setq entries - (cons (list (copy-marker start) - mark when desc (or amt total)) - entries)))) - (forward-line))))))) - (forward-line)) + (define-key map [(control ?c) (control ?a)] 'ledger-add-entry) + (define-key map [(control ?c) (control ?c)] 'ledger-toggle-current))) + +(defun ledger-parse-entries (account &optional all-p) + ;; `then' is 45 days ago + (let ((then (time-subtract (current-time) + (seconds-to-time (* 45 24 60 60)))) + total entries) + (ledger-iterate-entries + (function + (lambda (start date mark desc) + (when (or all-p (not mark) (time-less-p then date)) + (forward-line) + (setq total 0.0) + (while (looking-at + (concat "\\s-+\\([A-Za-z_].+?\\)\\(\\s-*$\\| \\s-*" + "\\([^0-9]+\\)\\s-*\\([0-9.]+\\)\\)")) + (let ((acct (match-string 1)) + (amt (match-string 4))) + (if amt + (setq amt (string-to-number amt) + total (+ total amt))) + (if (string= account acct) + (setq entries + (cons (list (copy-marker start) + mark date desc (or amt total)) + entries)))) + (forward-line)))))) (nreverse entries))) (define-derived-mode ledger-reconcile-mode text-mode "Reconcile" @@ -369,7 +369,7 @@ void add_new_entry(int index, int argc, char **argv) assert(index < argc); if (! parse_date(argv[index++], &added.date)) { - std::cerr << "Error: Bad add date: " << argv[index - 1] + std::cerr << "Error: Bad entry date: " << argv[index - 1] << std::endl; std::exit(1); } @@ -377,7 +377,7 @@ void add_new_entry(int index, int argc, char **argv) added.cleared = show_cleared; if (index == argc) { - std::cerr << "Error: Too few arguments to 'add'." << std::endl; + std::cerr << "Error: Too few arguments to 'entry'." << std::endl; std::exit(1); } @@ -396,7 +396,7 @@ void add_new_entry(int index, int argc, char **argv) added.desc = matching ? matching->desc : regexps.front().pattern; if (index == argc) { - std::cerr << "Error: Too few arguments to 'add'." << std::endl; + std::cerr << "Error: Too few arguments to 'entry'." << std::endl; std::exit(1); } @@ -437,11 +437,13 @@ void add_new_entry(int index, int argc, char **argv) mask acct_regex(argv[index++]); account * acct = NULL; + commodity * cmdty = NULL; for (std::list<transaction *>::iterator x = matching->xacts.begin(); x != matching->xacts.end(); x++) { if (acct_regex.match((*x)->acct->as_str())) { acct = (*x)->acct; + cmdty = (*x)->cost->commdty(); break; } } @@ -458,16 +460,18 @@ void add_new_entry(int index, int argc, char **argv) } if (index == argc) { - std::cerr << "Error: Too few arguments to 'add'." << std::endl; + std::cerr << "Error: Too few arguments to 'entry'." << std::endl; std::exit(1); } xact->cost = create_amount(argv[index++]); + if (! xact->cost->commdty()) + xact->cost->set_commdty(cmdty); added.xacts.push_back(xact); } - if ((index + 1) < argc && std::string(argv[index]) == "-from") + if ((index + 1) < argc && std::string(argv[index]) == "-from") { if (account * acct = main_ledger->re_find_account(argv[++index])) { transaction * xact = new transaction(); xact->acct = acct; @@ -475,15 +479,21 @@ void add_new_entry(int index, int argc, char **argv) added.xacts.push_back(xact); } + } else { + transaction * xact = new transaction(); + xact->acct = matching->xacts.back()->acct; + xact->cost = NULL; + added.xacts.push_back(xact); + } } if (added.finalize()) added.print(std::cout); } -// Print out the entire ledger that was read in, sorted by date. -// This can be used to "wash" ugly ledger files. It's written here, -// instead of ledger.cc, in order to access the static globals above. +// Print out the entire ledger that was read in. This can be used to +// "wash" ugly ledger files. It's written here, instead of ledger.cc, +// in order to access the static globals above. void book::print(std::ostream& out, regexps_map& regexps, bool shortcut) const @@ -491,9 +501,7 @@ void book::print(std::ostream& out, regexps_map& regexps, for (entries_list_const_iterator i = entries.begin(); i != entries.end(); i++) { - if ((! show_cleared && (*i)->cleared) || - ! matches_date_range(*i) || - ! (*i)->matches(regexps)) + if (! matches_date_range(*i) || ! (*i)->matches(regexps)) continue; (*i)->print(out, shortcut); |