emacs-orgmode
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Orgmode] org-mode and ledger cli accounting


From: Ben Alexander
Subject: [Orgmode] org-mode and ledger cli accounting
Date: Wed, 17 Dec 2008 22:29:40 +0000

*** org-mode and ledger cli accounting - two great tastes that taste great together
    :PROPERTIES:
:COLUMNS: %16LEDGER_DATE %5LEDGER_REFERENCE %10ITEM %25LEDGER_ACCOUNT %10AMOUNT
    :END:

***** TODO Email the org-mode and ledger mailing lists

I'm starting to use 'ledger' (http://www.newartisans.com/software/ledger.html ). There is an emacs mode for editing the native plain text file format, but I really like the org-mode UI. Date handling is great (imho) and column mode holds some promise (although I find it awkward to add a new headline while in column mode). And remember templates could ease data entry, too

I think it would be great to use 'ledger' backend computation and org- mode's UI together!

After a quick exchange of emails where I asked about the future plans of 'ledger' I was told that a new file format for 'ledger' could be added, if the org-mode community decided on a common format.

I'd like to suggest a format, get some feedback on it, and get a sense of how many others are interested. I'm hoping to interest org- mode users in ledger, and ledger users in org-mode!

As a temporary measure, I've used the org-map-entries API to iterate over entries looking for properties of the form "LEDGER_.*" to extract the appropriate details, format them like the current plain text 'ledger' file.

Since there's a lot about 'ledger' I don't know (as well as elisp, org-mode, git, programming in general, accounting) I'd expect that my code will be ineffecient, inelegant, and dangerous. It comes with no warrenty. If you have some programming skills, I'd appreciate feedback on style, design, tests and documentation.

For example, I'd like to use a :LEDGER: drawer instead of polluting the :PROPERTIES: namespace, but then I'd miss out on the easy org-entry-get API. Any suggestions?

Here's an example tree, with a column-mode definition above, and a single minimal entry below. Here's how to use it at the moment (caveat, I wrote this assuming you already have ledger (the shell program) downloaded and ledger.el in your loaded into emacs.

***** How to use the sample code and data
1. In the file bens-org-ledger.el, modify the variable bens-org-ledger- file-name to point to a file you don't mind overwriting without warning. (Did I mention that my code might be dangerous?)
2. Load bens-org-ledger.el
3. Run the defun (bens-org-ledger) while the current buffer contains this org-tree 4. Now (find-file bens-org-ledger-file-name). In my setup, the extension automaticaly loads ledger.el and the file is in ledger-mode. 5. C-c C-o C-r reg <RET> in a ledger-mode buffer (visting a native ledger file) will run a basic register report. Try C-c C-o C-r bal <RET> for a basic balance report.


***** Wegman's
      :PROPERTIES:
      :LEDGER: entry
      :LEDGER_DATE: [2008-11-07 Fri]
      :LEDGER_REFERENCE: chq 1001
      :END:
*******
        :PROPERTIES:
        :LEDGER: transaction
        :LEDGER_ACCOUNT: Expenses:Food and Drink:Groceries
        :LEDGER_AMOUNT: -£10.00
        :END:
        Since all text except the headline and specific properties is
        ignored, I can comment my transactions with body text!
******* Assets:Checking
        :PROPERTIES:
        :LEDGER: transaction
        :LEDGER_DATE: [2008-11-13 Thu]
        :END:

        Instead of using the :LEDGER_ACCOUNT: property, I can use the
        headline.  Properties come with completion (big win!) but
        headlines are easier to see outside of column mode.

        Also, in this case, the :LEDGER_DATE: property is ignored.
        Maybe it should become the 'effective date' in the future?


*** This is my lisp code, from a file named bens-org-ledger.el
It starts from the comments below to the end of the message. I'm sorry to be wordy but I thought having something that could be used (even awkwardly) would jump start the conversation

;; I'm naming every function I can with the bens-org-ledger prefix,
;; because these are temporary, pre-alpha attempts, intended to
;; elict comments from real programmers
;;
;; This code is copyright Ben Alexander
;; You have license to use, copy, and create derivative works
;; under the terms of the General Public License (version 2 or later).
;;
(defvar bens-org-ledger-file-name "bens-org-ledger-sample.ledger"
"This variable is the name of a file that will be overwritten during the processing of an org-mode file. OVERWRITTEN WITHOUT WARNING!") ;; except for this warning here


(defun bens-org-ledger-create-entry ()
   "this function retreives the LEDGER_DATE, LEDGER_REFERENCE (if it
   exists), and the LEDGER_DESCRIPTION properites of the current
   headline and formats them as the beginning of a new ledger entry"

   (concat
      "\n" ;; a blank line separates ledger entries
      (format-time-string "%Y/%m/%d"
(org-time-string-to-time (org-entry-get (point) "LEDGER_DATE") ) t) ;; Wouldn't it be nice if the timestamps repeat intervals were converted ;; to appropriate ledger syntax here? Does org-mode have some timestamp
      ;; property retrievals I could use?
      ;;
      ;; Also, I need to add support for "effective dates"

      (if (> (length (org-entry-get (point) "LEDGER_REFERENCE")) 0)
         (concat " (" (org-entry-get (point) "LEDGER_REFERENCE") ") ")
         " ")
(or (org-entry-get (point) "LEDGER_DESCRIPTION") (org-get- heading))
      "\n" ;; I assume the cursor starts on a newline -- make it so!
   )
)

(defun bens-org-ledger-create-transaction ()
"this function retreives the LEDGER_ACCOUNT (or headline), LEDGER_AMOUNT
   and formats them as a continuation of the existing ledger entry"

   (concat
      " "  ;; ledger transactions must begin with whitespace
      (or (org-entry-get (point) "LEDGER_ACCOUNT")
          (org-get-heading))
      "  "  ;; two spaces separate account name from amount
      (org-entry-get (point) "LEDGER_AMOUNT")
      "\n" ;; I assume the cursor starts on a newline -- make it so!
   )
)


(defun bens-org-ledger-map-entries-helper ()
"this function passed to org-map-entries to format headlines to ledger style
   plain text.  It chooses bens-org-ledger-create-entry or
bens-org-ledger-create-transation based on the LEDGER property. Those functions
   return the actual data that org-map-entries collects"
(cond ((string= (org-entry-get (point) "LEDGER") "entry") (bens-org- ledger-create-entry)) ((string= (org-entry-get (point) "LEDGER") "transaction") (bens- org-ledger-create-transaction))
))


(defun bens-org-ledger ()
"this function generates a properly formatted ledger file from the org-mode headlines based on properties stored in the org-mode property drawer. The file refered to by bens-org-ledger-file-name is OVERWRITTEN WITHOUT WARNING. (you have been warned, again)

The properties used are of the form LEDGER_.*

:LEDGER: entry|transaction => entries (headings) contain transactions (sub-headings)
For ledger entries, the following properties are used
:LEDGER: entry => Means this is the beginning of a new ledger entry. Entries (in the parlance of ledger) may contain
                         multiple transactions

:LEDGER_DATE: <2009-01-01 Thu> => An org-mode timestamp.

:LEDGER_REFERENCE: chq 101 => There's a place for this in the ledger syntax.
                              Any string is legal.

:LEDGER_DESCRIPTION: => if this is missing, the org-mode headline is used

For ledger transactions, the follwoing properties are used

:LEDGER: transaction  => Means this headline is a transaction.
                         It should be a sub-heading of some 'entry'

:LEDGER_ACCOUNT:      => a colon:delimited:account:as:used:by:ledger
:LEDGER_AMOUNT: £10  => Any legal amount for ledger.
Any currency symbol or code is considered legal, e.g.
:LEDGER_AMOUNT: 10 GBP
:LEDGER_AMOUNT: $10
:LEDGER_AMOUNT: 10 STICKS_OR_STONES
"

  (interactive)
(let ((ledger-text (org-map-entries 'bens-org-ledger-map-entries- helper "+LEDGER={transaction\\\|entry}"))) (with-temp-file bens-org-ledger-file-name (dolist (str ledger-text) (insert str)))
  ))







reply via email to

[Prev in Thread] Current Thread [Next in Thread]