A Little compatibility insurance.
[emoney] / emoney.el
index 407fcea..0de6565 100644 (file)
--- a/emoney.el
+++ b/emoney.el
@@ -1,6 +1,6 @@
 ;; emoney.el --- A home finance package.
 
-;; Copyright (C) 2003 - 2011 Steve Youngs
+;; Copyright (C) 2003 - 2017 Steve Youngs
 
 ;; Author:        Steve Youngs <steve@sxemacs.org>
 ;; Maintainer:    Steve Youngs <steve@sxemacs.org>
 ;;  <bnewell@alum.mit.edu>.
 ;;
 ;;  eMoney tries to give the user a reasonably self-contained finance
-;;  solution.  The target audience is the home user and possibly the
-;;  small (very small) business operator.  It probably will never be
-;;  compatible with any commercial finance packages available.
+;;  solution.  The target audience is the home user.  It probably
+;;  will never be compatible with any commercial finance packages
+;;  available.
 ;;
 ;;  Right now, eMoney is nothing more than a very simple cash book
-;;  program.  Use it to balance your cheque book.  See the "Todo:"
-;;  section for a partial list of possible upcoming features.
+;;  program.  Use it to balance your cheque book.  See the TODO file
+;;  for a partial list of possible upcoming features.
 ;;
 ;;  Installation (from source):
 ;;
@@ -71,7 +71,7 @@
 ;;  `.emy' extension.
 ;;
 ;; Please note that I use SXEmacs exclusively, I have no idea whether
-;; or not this will run or even byte-compile with GNU/Emacs (it _will_
+;; or not this will run or even byte-compile with GNU/Emacs (it _should_
 ;; work fine with XEmacs).  Also, I have no desire or intentions of
 ;; making this package portable between XEmacs and GNU/Emacs.  Harsh
 ;; words?  No, not really, I simply don't have the time or resources to
 ;;
 ;; Another note: This package uses correct English spelling wherever
 ;; possible.  For example, "cheque" instead of "check", "summarise"
-;; instead of "summarize".  Although I welcome and encourage _ALL_
-;; contributions, patches that s/que/ch/ or s/ise/ize/ will go straight
-;; to /dev/null.
+;; instead of "summarize".
 
 ;;; ChangeLog:
 ;;
 ;;  This is just a place holder so `emoney-commentary' will work
-;;  properly.  See the ChangeLog file for changes.
+;;  properly.  See the ChangeLog files in ChangeLog.d and git log for
+;;  changes.
 
 ;;; Code:
+;; A little compatibility insurance
+(globally-declare-fboundp 
+ '(mapfam dllist-car dllist-lrotate dllist-rrotate))
+
+(defvar emoney-emacs-is-sexy-enough (and (featurep 'dllist)
+                                        (fboundp #'mapfam))
+  "Non-nil when emacs has some needed extra features.
+
+Currently, that is support for dllists and #'mapfam.  Don't worry, if
+you're not sexy enough you can still use eMoney, you just won't be
+able to \"walk\" through accounts, but you can still switch to any
+account directly.
+
+See: `emoney-walk-accounts'.")
 
 ;; Drag in what we need.
 (eval-and-compile
@@ -167,7 +180,7 @@ This is the account that has the focus when you start eMoney."
 
 (defcustom emoney-credit-transaction-types
   '("autotellcr" "atmcr" "bankcredit" "bcr" "deposit" "dep" "directcr"
-    "dcr" "internetcr" "netcr" "phonecr" "phcr")
+    "dcr" "gencr" "internetcr" "netcr" "phonecr" "phcr")
   "*List of valid credit transaction types.
 
 The default types are:
@@ -184,6 +197,9 @@ The default types are:
             directcr -- Direct credit transactions
                  dcr -- short version of 'directcr'
 
+               gencr -- General credit for things that don't fit like
+                        reversals etc
+
           internetcr -- Internet credit transactions
                netcr -- short version of 'internetcr'
 
@@ -193,8 +209,9 @@ The default types are:
   :group 'emoney)
 
 (defcustom emoney-debit-transaction-types
-  '("autotelldb" "atmdb" "bankfee" "fee" "directdb" "ddb" "eftpos"
-    "eft" "internetdb" "netdb" "phonedb" "phdb" "withdrawal" "wdl")
+  '("autotelldb" "atmdb" "bankfee" "fee" "bpay" "cc" "directdb" "ddb"
+    "eftpos" "eft" "gendb" "internetdb" "netdb" "paypal" "phonedb"
+    "phdb" "venddb" "withdrawal" "wdl")
   "*List of valid debit transaction types.
 
 The default types are:
@@ -205,18 +222,29 @@ The default types are:
              bankfee -- Bank fees
                  fee -- short version of 'bankfee'
 
+                bpay -- BillPayments
+
+                  cc -- Credit card
+
             directdb -- Direct debit transactions
                  ddb -- short version of 'directdb'
 
               eftpos -- Electronic Funds Transfer Point Of Sale
                  eft -- short version of 'eftpos'
 
+               gendb -- For general debits that don't fit other types
+                        like reversals.
+
           internetdb -- Internet transactions
                netdb -- short version of 'internet'
 
+              paypal -- PayPal transactions
+
              phonedb -- Telephone transactions
                 phcr -- short version of 'phone'
 
+              venddb -- Vending machine transactions
+
           withdrawal -- Over the counter withdrawal.
                  wdl -- short version of 'withdrawal'"
   :type '(repeat string)
@@ -452,20 +480,16 @@ See `emoney-recalculate-before-hook' for doing things before recalculating."
 (defun emoney-version (&optional arg)
   "*Display the current version information for eMoney.
 
-Optional prefix Argument ARG, print version information at point
+Prefix Argument ARG, print version information at point
 in the current buffer."
   (interactive "P")
-  (if emoney-is-beta
-      (if arg
-         (insert (format "eMoney: %s, \"%s\" [Beta]"
-                         emoney-version emoney-codename))
-       (message (format "eMoney: %s, \"%s\" [Beta]"
-                        emoney-version emoney-codename)))
+  (let ((fmt-string "eMoney: %s, \"%s\""))
+    (when emoney-is-beta
+      (setq fmt-string (concat fmt-string " [Beta]")))
     (if arg
-       (insert (format "eMoney: %s, \"%s\""
-                       emoney-version emoney-codename))
-      (message (format "eMoney: %s, \"%s\""
-                      emoney-version emoney-codename)))))
+       (insert (format fmt-string emoney-version emoney-codename))
+      (message fmt-string emoney-version emoney-codename))))
+
 
 ;;;###autoload
 (defun emoney-commentary ()
@@ -517,24 +541,23 @@ in the current buffer."
 (defvar emoney-current-account-name 
   (file-name-sans-extension emoney-default-account))
 
-(defun emoney-switch-to-account (&optional account)
+(defun emoney-switch-to-account (account)
   "Switch to account, ACCOUNT."
-  (interactive)
-  (let ((switch-acc (or account
-                       (emoney-completing-read "Switch to A/C: "
-                                               emoney-chart-of-accounts nil t))))
-    (select-window (get-buffer-window
-                   (concat emoney-current-account-name ".emy")))
-    (switch-to-buffer switch-acc)
-    (goto-char (point-max))
-    (setq emoney-current-account-name
-         (file-name-sans-extension switch-acc))
-    (select-window (get-buffer-window emoney-header-buffer))
-    (emoney-setup-header-buffer)
-    (switch-to-buffer emoney-header-buffer)
-    (select-window (get-buffer-window
-                   (concat emoney-current-account-name ".emy")))
-    (run-hooks 'emoney-switch-account-hook)))
+  (interactive
+   (list (emoney-completing-read "Switch to A/C: "
+                                emoney-chart-of-accounts nil t)))
+  (select-window (get-buffer-window
+                 (concat emoney-current-account-name ".emy")))
+  (switch-to-buffer account)
+  (goto-char (point-max))
+  (setq emoney-current-account-name
+       (file-name-sans-extension account))
+  (select-window (get-buffer-window emoney-header-buffer))
+  (emoney-setup-header-buffer)
+  (switch-to-buffer emoney-header-buffer)
+  (select-window (get-buffer-window
+                 (concat emoney-current-account-name ".emy")))
+  (run-hooks 'emoney-switch-account-hook))
 
 (defun emoney-mouse-switch-to-account (event)
   "Switch to account under EVENT."
@@ -554,6 +577,46 @@ in the current buffer."
                  (concat emoney-current-account-name ".emy")))
   (run-hooks 'emoney-switch-account-hook))
 
+(defun emoney-goto-default-account ()
+  "Switch to `emoney-default-account'."
+  (interactive)
+  (emoney-switch-to-account emoney-default-account))
+
+;:*=======================
+;:* Walk accounts
+(defun emoney-walk-accounts (direction)
+  "Move to account in direction, DIRECTION."
+  (let ((dl-acc-list (mapfam #'identity
+                            emoney-chart-of-accounts
+                            :result-type 'dllist)))
+    ;; Line up the dllist's car with the current account
+    (while (not (equal (dllist-car dl-acc-list) 
+                      (concat emoney-current-account-name ".emy")))
+      (dllist-rrotate dl-acc-list))
+    (cond
+     ((eq direction 'next)
+      (progn
+       (dllist-lrotate dl-acc-list)
+       (emoney-switch-to-account (dllist-car dl-acc-list))))
+     ((eq direction 'previous)
+      (progn
+       (dllist-rrotate dl-acc-list)
+       (emoney-switch-to-account (dllist-car dl-acc-list))))
+     (t (error 'invalid-argument)))))
+
+(defun emoney-walk-accounts-next ()
+  "Switch to the 'next' account in the chart of accounts."
+  (interactive)
+  (and emoney-emacs-is-sexy-enough
+       (emoney-walk-accounts 'next)))
+
+
+(defun emoney-walk-accounts-previous ()
+  "Switch to the 'previous' account in the chart of accounts."
+  (interactive)
+  (and emoney-emacs-is-sexy-enough
+       (emoney-walk-accounts 'previous)))
+
 (defconst emoney-accounts-buffer-map
   (let* ((map (make-sparse-keymap 'emoney-accounts-buffer-map)))
     (define-key map [button2] #'emoney-mouse-switch-to-account)
@@ -701,11 +764,16 @@ transactions.")
     (define-key map [(control c) (control t)] #'emoney-append-transaction)
     (define-key map [(control c) (control x)] #'emoney-transfer-funds)
     (define-key map [tab]                     #'emoney-forward-field)
+    (define-key map [iso-left-tab]            #'emoney-backward-field)
     (define-key map [(control c) b]           #'emoney-go-to-bank)
     (define-key map [(control c) c]           #'emoney-calc)
     (define-key map [(control c) s]           #'emoney-switch-to-account)
+    (define-key map [(control c) d]           #'emoney-goto-default-account)
     (define-key map [(control c) q]           #'emoney-quit)
     (define-key map [(control c) (control q)] #'emoney-recalc-and-exit)
+    (when emoney-emacs-is-sexy-enough
+      (define-key map [(meta n)]              #'emoney-walk-accounts-next)
+      (define-key map [(meta p)]              #'emoney-walk-accounts-previous))
     map)
   "Keymap for emoney buffer.")
 
@@ -844,15 +912,13 @@ HISTORY, DEFAULT are as per `completing-read'."
   (interactive)
   (move-to-tab-stop))
 
-(defun emoney-go-to-bank (&optional url)
+(defun emoney-go-to-bank ()
   "Open your bank's URL with `browse-url'."
   (interactive)
-  (let ((url (or url
-                emoney-bank-url)))
-    (if (or (equal url "unset")
-           (not url))
-       (message-or-box "Please customise `emoney-bank-url'.")
-      (browse-url url))))
+  (if (or (equal emoney-bank-url "unset")
+         (not emoney-bank-url))
+      (message-or-box "Please customise `emoney-bank-url'.")
+    (browse-url emoney-bank-url)))
 
 (defun emoney-calc ()
   "Wrapper around `calc' to get around \"window-edges bug\"."
@@ -1109,28 +1175,23 @@ transaction type column.  Loses if you write cheques out of order."
 
 (defvar emoney-transfer-account-history nil)
 
-(defun emoney-transfer-funds (&optional from to amount)
+(defun emoney-transfer-funds (from to amount)
   "Transfer funds from one eMoney account to another.
 
 Argument FROM is the account to transfer from.
 Argument To is the account to transfer to.
 Argument AMOUNT is how much to transfer."
-  (interactive)
-  (let ((from (or from
-                 (emoney-completing-read "Transfer from: "
-                                         emoney-chart-of-accounts
-                                         nil
-                                         t
-                                         (concat emoney-current-account-name
-                                                 ".emy")
-                                         emoney-transfer-account-history)))
-       (to (or to
-               (emoney-completing-read "Transfer to: "
-                                       emoney-chart-of-accounts nil t nil
-                                       emoney-transfer-account-history)))
-       (amount (or amount
-                   (read-number "Amount: ")))
-       (current-ac (concat emoney-current-account-name ".emy")))
+  (interactive
+   (list (emoney-completing-read "Transfer from: "
+                                emoney-chart-of-accounts nil t
+                                (concat emoney-current-account-name
+                                        ".emy")
+                                emoney-transfer-account-history)
+        (emoney-completing-read "Transfer to: "
+                                emoney-chart-of-accounts nil t nil
+                                emoney-transfer-account-history)
+        (read-number "Amount: ")))
+  (let ((current-ac (concat emoney-current-account-name ".emy")))
     (with-current-buffer from
       (emoney-append-transaction 
        emoney-db-transfer-transaction-type
@@ -1450,28 +1511,26 @@ the offending cheques.  Return the count of sequence breaks found."
   (emoney-summarise-cheques start end)
   (emoney-recalculate (point-min) (point-max)))
 
-(defun emoney-new-account (&optional name balance)
-  "*Create a new A/C named NAME for use with eMoney."
-  (interactive)
-  (let ((new-acc (concat (or name
-                            (read-string "New A/C Name: ")) ".emy"))
-       (bal (or balance
-                (read-number "Initial Balance: " nil 0))))
-    (find-file-noselect 
-     (expand-file-name new-acc emoney-accounts-directory))
-    (setq emoney-chart-of-accounts
-         (push new-acc emoney-chart-of-accounts))
-    (select-window (get-buffer-window 
-                   (concat emoney-current-account-name ".emy")))
-    (switch-to-buffer new-acc)
-    (setq emoney-current-account-name 
-         (file-name-sans-extension new-acc))
-    (emoney-append-transaction "init" "Opening Balance" bal)
-    (emoney-recalculate-buffer)
-    (emoney-show-buffers)
-    (switch-to-buffer new-acc)
-    (goto-char (point-max))
-    (run-hooks 'emoney-new-account-hook)))
+(defun emoney-new-account (new-acc bal)
+  "*Create a new A/C named NEW-ACC with initial balance BAL."
+  (interactive
+   (list (concat (read-string "New A/C Name: ") ".emy")
+        (read-number "Initial Balance: " nil 0)))
+  (find-file-noselect 
+   (expand-file-name new-acc emoney-accounts-directory))
+  (setq emoney-chart-of-accounts
+       (push new-acc emoney-chart-of-accounts))
+  (select-window (get-buffer-window 
+                 (concat emoney-current-account-name ".emy")))
+  (switch-to-buffer new-acc)
+  (setq emoney-current-account-name 
+       (file-name-sans-extension new-acc))
+  (emoney-append-transaction "init" "Opening Balance" bal)
+  (emoney-recalculate-buffer)
+  (emoney-show-buffers)
+  (switch-to-buffer new-acc)
+  (goto-char (point-max))
+  (run-hooks 'emoney-new-account-hook))
 
 (defun emoney-setup-control-buffer ()
   "Set up the eMoney \"Control\" buffer."
@@ -1482,7 +1541,7 @@ the offending cheques.  Return the count of sequence breaks found."
       (set-buffer (get-buffer-create buf))
       (widget-create 'push-button
                     :notify (lambda (&rest ignore)
-                              (emoney-new-account))
+                              (call-interactively 'emoney-new-account))
                     :help-echo "Create a new eMoney account."
                     " New A/C ")
       (widget-insert " ")
@@ -1525,7 +1584,7 @@ Also recalculates the buffer."
                               (save-excursion
                                 (set-buffer
                                  (concat emoney-current-account-name ".emy"))
-                                (emoney-transfer-funds)))
+                                (call-interactively 'emoney-transfer-funds)))
                     :help-echo "Transfer funds between accounts."
                     "Transfer ")
       (widget-insert " ")