branch: elpa/csv2ledger
commit aa55c01cc1693016880d0277112027d28887a5c2
Author: Joost Kremers <joostkrem...@fastmail.fm>
Commit: Joost Kremers <joostkrem...@fastmail.fm>

    New user option c2l-transaction-modify-functions.
    
    This replaces c2l-transaction-modify-function and subsumes 
c2l-title-function
    and c2l-amount-function. It also includes c2l-account-function, which was
    hard-coded before.
---
 csv2ledger.el | 195 +++++++++++++++++++++++-----------------------------------
 1 file changed, 76 insertions(+), 119 deletions(-)

diff --git a/csv2ledger.el b/csv2ledger.el
index d3ffdea79c..aa112116d4 100644
--- a/csv2ledger.el
+++ b/csv2ledger.el
@@ -73,11 +73,6 @@ instead of the payee as the title of the entry."
   :type 'string
   :group 'csv2ledger)
 
-(defun c2l-set-options ()
-  "Set the Csv2Ledger options dependent on `c2l-csv-columns'.
-Currently, there is only one such option: `c2l-amount-function'."
-  (mapc #'custom-reevaluate-setting '(c2l-amount-function)))
-
 (defcustom c2l-csv-columns '(date posted description sender payee amount)
   "List of columns in the CSV file.
 The data in the CSV file is extracted based on this list.  The
@@ -98,36 +93,38 @@ Valid column names are the following:
 
 Other column names can be added, but they cannot be used directly
 in the transaction.  They may be used in the option
-`c2l-target-match-fields' or in custom functions for the options
-`c2l-title-function' and `c2l-amount-function', however.
-
-It is assumed that a CSV file contains either `payee' and
-`sender' columns or a `counterpart' column, but not both, and
-similarly, that it contains either an `amount' column or `credit'
-and `debit' columns.  You should set `c2l-title-function' and
-`c2l-amount-function' to match what is valid for your CSV files.
-
-The option `c2l-amount-function' is dependent on the value of
-this variable.  If you update this variable in your init file
-without using Customize, make sure to call `c2l-set-options' to
-set the dependent variable as well, or set it directly."
+`c2l-target-match-fields' or in custom functions for
+`c2l-transaction-modify-functions', however."
   :type '(repeat symbol)
-  :initialize 'custom-initialize-default
-  :set (lambda (symbol value)
-         (unless (equal value (eval symbol))
-           (custom-set-default symbol value)
-           (c2l-set-options)))
   :group 'csv2ledger)
 
-(defcustom c2l-transaction-modify-function #'identity
-  "Function to modify a transaction.
-This should be a single function that takes an alist representing
-a transaction and returns a modified alist.  Any kind of
-modification is possible, including modifying, adding or deleting
-fields.  Importantly, because the function is passed the entire
-entry, it is possible to modify or create a field based on the
-values of other fields."
-  :type 'function
+(defvar c2l-transaction-modifier nil
+  "The function that modifies a CSV transaction before creating a ledger entry.
+This is the composite function created with the functions in
+`c2l-transaction-modify-functions'.")
+
+(defun c2l-compose-transaction-modifier (fns)
+  "Function to set the variable `c2l-transaction-modifier'.
+FNS is a list of functions, which is reversed and then composed
+into a single function taking a transaction alist as argument and
+returning a modified transaction alist."
+  (setq c2l-transaction-modifier (apply #'-compose (reverse fns))))
+
+(defcustom c2l-transaction-modify-functions '(c2l-create-title 
c2l-create-amount c2l-create-account)
+  "List of functions applied to the transaction before creating an entry.
+The functions are applied in the order in which they appear in
+the list.  Each function should take an alist representing a
+transaction as argument and should return the modified
+transaction.
+
+These functions are composed into a single function which is then
+stored in the variable `c2l-transaction-modifier'.  If you set
+this variable outside of Customize, make sure to call the
+function `c2l-set-transaction-modifier ' as well."
+  :type '(repeat function)
+  :set (lambda (var val)
+         (c2l-compose-transaction-modifier val)
+         (set-default var val))
   :group 'csv2ledger)
 
 (defcustom c2l-field-modify-functions nil
@@ -139,26 +136,6 @@ for the field in question."
   :type '(repeat (cons (symbol :tag "Field") function))
   :group 'csv2ledger)
 
-(defcustom c2l-title-function #'c2l-payee-or-sender
-  "Function to create a title.
-The function should take as argument an entry alist of
-field-value pairs and should return a string.  The string
-returned is used as the title of the ledger entry."
-  :type 'function
-  :group 'csv2ledger)
-
-(defcustom c2l-amount-function
-  (if (memq 'amount c2l-csv-columns)
-      #'c2l-amount-is-amount
-    #'c2l-amount-is-credit-or-debit)
-  "Function to create the amount.
-The function should take as argument an entry alist of
-field-value pairs and should return a string.  The string
-returned is used as the amount of the ledger entry."
-  :type 'function
-  :group 'csv2ledger
-  :set-after '(c2l-csv-columns))
-
 (defcustom c2l-entry-function #'c2l-compose-entry
   "Function to create a ledger entry.
 This should be a function that takes an alist of field-value
@@ -236,53 +213,47 @@ format, it just splits DATE on the separator, reverses 
the date
 parts and joins them again, using a hyphen as separator."
   (string-join (nreverse (split-string date "[./-]" t "[[:space:]]")) "-"))
 
-(defun c2l-payee-or-sender (transaction)
-  "Return payee or sender based on `c2l-account-holder'.
-This function is for use as the value of `c2l-title-function'.
-TRANSACTION should be an alist containing field-value pairs and
-should contain values for `payee' and `sender'.  If the value of
-`c2l-account-holder' matches the payee, the sender is returned,
-otherwise the payee is returned."
-  (let ((payee (alist-get 'payee transaction ""))
-        (sender (alist-get 'sender transaction "")))
-    (cond
-     ((and (string-empty-p payee)
-           (string-empty-p sender))
-      "Unknown payee")
-     ((string-empty-p payee) sender)
-     ((string-empty-p sender) payee)
-     ((and (stringp c2l-account-holder)
-           (string-match-p c2l-account-holder payee))
-      sender)
-     (t payee))))
-
-(defun c2l-amount-is-amount (transaction)
-  "Return the amount of an entry.
-This function is for use as the value of `c2l-amount-function'.
-TRANSACTION should be an alist containing field-value pairs and
-should contain a value for `amount', which is the return value.
-If `amount' does not contain a value, this function returns
-\"0.00\"."
-  (let ((amount (alist-get 'amount transaction)))
-    (if (c2l--amount-p amount)
-        amount
-      "0.00")))
-
-(defun c2l-amount-is-credit-or-debit (transaction)
-  "Return the amount of an entry.
-This function is for use as the value of `c2l-amount-function'.
-TRANSACTION should be an alist containing field-value pairs and
-should contain values for `credit' and `debit'.  Whichever of
-these two looks like an amount (using `c2l--amount-p') is
-returned.  If neither contains an amount, the value \"0.00\" is
-returned.
-
-Note that a debit amount should have a negative value, i.e., it
-should be preceded by a minus sign.  If this is not the case,
-make sure to add one using `c2l-field-parse-functions'."
-  (or (c2l--amount-p (alist-get 'credit transaction ""))
-      (c2l--amount-p (alist-get 'debit transaction ""))
-      "0.00"))
+(defun c2l-create-title (transaction)
+  "Create a title for TRANSACTION.
+Return the modified transaction."
+  (let* ((payee (alist-get 'payee transaction ""))
+         (sender (alist-get 'sender transaction ""))
+         (title (cond
+                 ((and (string-empty-p payee)
+                       (string-empty-p sender))
+                  "Unknown payee")
+                 ((string-empty-p payee) sender)
+                 ((string-empty-p sender) payee)
+                 ((and (stringp c2l-account-holder)
+                       (string-match-p c2l-account-holder payee))
+                  sender)
+                 (t payee))))
+    (push (cons 'title title) transaction)
+    transaction))
+
+(defun c2l-create-amount (transaction)
+  "Create the amountfor TRANSACTION.
+Return the modified transaction."
+  (unless (c2l--amount-p (alist-get 'amount transaction ""))
+    (let ((amount (or (c2l--amount-p (alist-get  'credit transaction ""))
+                      (c2l--amount-p (alist-get  'debit transaction ""))
+                      "0.00")))
+      (if (alist-get 'amount transaction)
+          (setf (alist-get 'amount transaction) amount)
+        (push (cons 'amount amount) transaction))))
+  transaction)
+
+(defun c2l-create-account (transaction)
+  "Create the account for TRANSACTION."
+  (let ((account (or (-some #'c2l--match-account (mapcar #'cdr (--filter (memq 
(car it) c2l-target-match-fields) transaction)))
+                     c2l-fallback-account
+                     (completing-read (format "Account for transaction %s, %s 
«%.75s» "
+                                              (alist-get 'title transaction 
"Unknown payee")
+                                              (alist-get 'amount transaction 
"0.00")
+                                              (alist-get 'description 
transaction "?"))
+                                      c2l--accounts))))
+    (push (cons 'account account) transaction)
+    transaction))
 
 ;;; Helper functions
 
@@ -377,33 +348,19 @@ found, the value of `c2l-fallback-account' is used.  If 
that
 option is unset, the user is asked for an account.
 
 This function first creates an alist of field-value pairs,
-passes it to `c2l-transaction-modify-function' and subsequently
 applies the functions in `c2l-field-modify-functions' to the
-individual fields.  After that, the `title' and `account' fields
-are added.  Additionally, the `amount' field is added or, if
-already, present, its value is updated."
-  (let* ((fields (funcall c2l-transaction-modify-function (--remove (eq (car 
it) '_) (-zip-pair c2l-csv-columns row))))
+individual fields and then passes the transaction through
+`c2l-transaction-modify-functions' before calling
+`c2l-entry-function' to create the actual entry."
+  (let* ((fields (--remove (eq (car it) '_) (-zip-pair c2l-csv-columns row)))
          (modified-fields (mapcar (lambda (item)
                                     (let ((field (car item))
                                           (value (cdr item)))
                                       (cons field
                                             (funcall (alist-get field 
c2l-field-modify-functions #'identity) value))))
                                   fields))
-         (title (funcall c2l-title-function modified-fields))
-         (amount (funcall c2l-amount-function modified-fields))
-         (account (or (-some #'c2l--match-account (mapcar #'cdr (--filter 
(memq (car it) c2l-target-match-fields) modified-fields)))
-                      c2l-fallback-account
-                      (completing-read (format "Account for transaction %s, %s 
«%.75s» "
-                                               (funcall c2l-title-function 
modified-fields)
-                                               (alist-get 'amount 
modified-fields)
-                                               (alist-get 'description 
modified-fields))
-                                       c2l--accounts))))
-    (push (cons 'account account) modified-fields)
-    (push (cons 'title title) modified-fields)
-    (if (assq 'amount modified-fields)
-        (setf (alist-get 'amount modified-fields) amount)
-      (push (cons 'amount amount) modified-fields))
-    (funcall c2l-entry-function modified-fields)))
+         (transaction (funcall c2l-transaction-modifier modified-fields)))
+    (funcall c2l-entry-function transaction)))
 
 (defun c2l--get-current-row ()
   "Read the current line as a CSV row.

Reply via email to