Comment

Comment by TeMPOraL

I use proper org-mode functions to query the built-in agenda and then group entries by org-mode tags. My code expects a configuration that maps tags to their line item names (as on invoice), hourly rates and applicable tax. I use this to output a dynamic[0] table like this:

   | Tag             | Invoice line name     | Hours |  Rate |    Net | VAT % | VAT amount |  Total |
   |-----------------+-----------------------+-------+-------+--------+-------+------------+--------|
   | meeting         | Meetings              |    10 | 12.00 | 120.00 | NP    |       0.00 | 120.00 |
   | procrastinating | Important tasks       |    20 | 13.00 | 260.00 | NP    |       0.00 | 260.00 |
   | hn              | Other important tasks |    40 | 14.00 | 560.00 | NP    |       0.00 | 560.00 |
   | coding          | Occasional code       |     3 | 15.00 |  45.00 | NP    |       0.00 |  45.00 |
   |-----------------+-----------------------+-------+-------+--------+-------+------------+--------|
   | Totals          | -                     |    73 |     - | 985.00 | -     |       0.00 | 985.00 |
   #+TBLFM: @>$3=vsum(@2..@-1)::@>$5=vsum(@2..@-1);%.2f::@>$7=vsum(@2..@-1);%.2f::@>$8=vsum(@2..@-1);%.2f::$5=$3*$4;%.2f::$7=if(typeof($6) < 10, $5*$6, 0);%.2f::$8=$7+$5;%.2f
The #+TBLFM: line at the bottom is org-mode spreadsheet functionality; it ensures I can modify the table text in-place, and just press C-c C-c to recalculate all values in it. I generate a similar table with data for invoices; some taken from my configuration, some computed (e.g. invoice number is generated by looking at filenames of previous invoices; sale date and service period are estimated from current date and the time range you specified when generating line items table, etc.).

The code for generating above table is hand-crafted. While org-mode provides you a generator for agenda tables based on TODO item name - see [1], I had to write similar feature that aggregated time by org-mode tags. I used [2] as a reference, but essentially wrote my own implementation. Surprisingly, it turned out to be almost trivial.

Then, it's simply a matter of turning those two org-mode tables into an elisp list of keys and values. This I accomplish using a simple elisp that uses built-in Emacs calls:

  (defun trc-invoicing--named-table-to-lisp (name)
    "Convert a named org mode table in the current buffer into a list.
  The function finds a table named `NAME', passes it through `ORG-TABLE-TO-LISP',
  and return the result. Refer to the documentation of `ORG-TABLE-TO-LISP'
  for info about result's shape."
    (save-excursion
      (goto-char (point-min))
      (search-forward (format "#+NAME: %s" name))
      (forward-line)
      (org-table-to-lisp)))
Then it's a matter of populating a LaTeX template with extracted values, invoking pdflatex, and copying the resulting PDF in an appropriate place. That last part I yet have to finish.

It's the biggest piece of elisp I wrote since Nyan Mode, but all in all, I find it surprisingly easy to code for Emacs. Availability of source code, extensive in-editor documentation and built-in source-level debugger make this process pretty pleasant.

--

[0] - Org Mode has a concept of "dynamic blocks". You type in #+BEGIN: blockname some params, and then using C-c C-c on it causes Org Mode to invoke an elisp function named org-dblock-write:blockname. The called function is then responsible for outputting contents of the dynamic block.

[1] - http://orgmode.org/manual/The-clock-table.html

[2] - https://gist.github.com/tsu-nera/d9ffa6a51a6e7bdb957b

parent TeMPOraL 7 years ago

Discussion

Live from the official Hacker News API

A modern reader for Hacker News · SvelteKit + Skeleton