Like many people I work as a contractor, so I need to keep track of my hours and to generate monthly invoices. Org-mode already has a nice way to track hours and a way to export to a PDF. What's missing is a reasonable way to bridge those two functions, to generate invoices, and the way I do this is described in this post.

The main approach here is to maintain a timecard.org file that contains all the timing information. As I work, I clock in and out of tasks in this file. Then when an invoice needs to be prepared I simply export this file as a PDF, and I get out a finished invoice. Before I get into the details, here's a sample timecard.org:

#+STARTUP: showall
#+LaTeX_CLASS_OPTIONS: [letterpaper,10pt]
#+LATEX_HEADER: \usepackage[letterpaper,tmargin=0.5in,bmargin=1.0in,lmargin=1.0in,rmargin=1.0in,headheight=0in,headsep=0in,footskip=0.0in]{geometry}
#+LATEX_HEADER: \usepackage{lmodern}
#+LATEX_HEADER: \usepackage[labelformat=empty,textformat=empty]{caption}
#+LATEX_HEADER: \parindent 0in
#+LATEX_HEADER: \parskip 0.1in
#+LATEX_HEADER: \setlength\LTleft{0pt} \setlength\LTright\fill
#+OPTIONS: toc:nil num:nil
#+AUTHOR:
#+DATE:
#+TITLE: INVOICE


#+CONSTANTS: rate=100.0

#+BEGIN_LATEX
\thispagestyle{empty}
#+END_LATEX


* Invoice number: 1
* Invoice date: [2014-09-01 Mon]

| / | <                    | >                     |
|---+----------------------+-----------------------|
|   | *Contractor*         | *Client*              |
|---+----------------------+-----------------------|
|   | Billy Bob Johnson    | WidgetWorks Inc       |
|   | 21 N. First Ave      | 12 Main St.           |
|   | Widgettown, CA 91234 | Gadgetville, CA 91235 |
|   | william@isp.net      |                       |
|---+----------------------+-----------------------|

#+NAME: summary
| / |            |             |
|   | *Rate*     | *Total due* |
|   | $100.00    | _$941.67_   |
| ^ | ratetarget | totaltarget |
#+TBLFM: $ratetarget=$rate;$%.2f::$totaltarget=remote(clocktable, "@II$>");_$%.2f_


*Please make checks payable to _William Johnson_*

#+TBLNAME: clocktable
#+BEGIN: clocktable :maxlevel 3 :tcolumns 4 :scope file :block 2014-08 :narrow 60
#+CAPTION: Clock summary at [2014-09-01 Mon 09:44], for August 2014.
| <60>                                                         |        |      |      |           |
| Headline                                                     | Time   |      |      | Amount($) |
|--------------------------------------------------------------+--------+------+------+-----------|
| *Total time*                                                 | *9:25* |      |      |    941.67 |
|--------------------------------------------------------------+--------+------+------+-----------|
| Tasks                                                        | 9:25   |      |      |    941.67 |
| \__ Foo the bar                                              |        | 3:20 |      |    333.33 |
| \_____ Implementing foo                                      |        |      | 3:20 |    333.33 |
| \__ Frobnicate the baz                                       |        | 6:05 |      |    608.33 |
#+TBLFM: @3$5..@>$5=vsum($2..$4)*$rate;t::@2$5=string("Amount($)")
#+END:



* Tasks                                                            :noexport:
** Foo the bar
*** Meeting about the nature of bar
    CLOCK: [2014-09-07 Sun 00:24]--[2014-09-07 Sun 01:00] =>  0:36
*** Implementing foo
    CLOCK: [2014-08-28 Thu 19:40]--[2014-08-28 Thu 23:00] =>  3:20
** Frobnicate the baz
    CLOCK: [2014-08-25 Mon 10:55]--[2014-08-25 Mon 17:00] =>  6:05


* local lisp stuff                                                 :noexport:
Local Variables:
eval: (progn
  (set (make-local-variable 'org-time-clocksum-format)
       '(:hours "%d" :require-hours t :minutes ":%02d" :require-minutes t))
  (setq org-latex-tables-centered nil
        org-latex-default-table-environment "longtable")
  (local-set-key
   (kbd "<f5>")
   (lambda ()
     (interactive)
     ;;
     ;; Bump up the invoice number
     (beginning-of-buffer)
     (re-search-forward "Invoice number: \\([0-9]+\\)")
     (let ((n (string-to-number (match-string 1))))
       (kill-region (match-beginning 1) (match-end 1))
       (insert (format "%d" (1+ n))))
     ;;
     ;; Set the invoice date
     (beginning-of-buffer)
     (re-search-forward "Invoice date: *")
     (kill-region (point)
                  (save-excursion
                    (end-of-line) (point)))
     (org-insert-time-stamp (current-time) nil t)
     ;;
     ;;
     ;; Update the main clock table
     (beginning-of-buffer)
     (search-forward "#+BEGIN: clocktable")
     ;;
     ;; Here an advice is needed to make sure the Amount column is added
     ;; This advice is made unnecessary by this patch:
     ;; http://lists.gnu.org/archive/html/emacs-orgmode/2014-10/msg00002.html
     (unwind-protect
         (progn
           (defadvice org-table-goto-column
               (before
                always-make-new-columns
                (n &optional on-delim force)
                activate)
             "always adds new columns when we move to them"
             (setq force t))
           ;;
           (org-clocktable-shift 'right 1))
       ;;
       (ad-deactivate 'org-table-goto-column))
     ;;
     ;; Update the summary table
     (beginning-of-buffer)
     (search-forward "| totaltarget")
     (org-table-recalculate t))))
End:

As described in the manual, the time-tracking in org works with the user producing an outline of tasks and then clocking in and out of them. Org can then generate a table to summarize the hours spent on various tasks in a particular period.

The timecard.org has

  • some Latex commands to determine how the invoices look
  • some specific information for that invoice (invoice number, date, totals)
  • the clock-table that contains the time and $ summaries
  • the task outline that the user clocks in and out of
  • some emacs lisp to update all these things in unison

The contracting rate is defined in a #+CONSTANTS at the top of the file. The task outline itself is not exported, but the clock-table it produces is. The clock-table that org makes contains only timing information and nothing about money, so I have a formula in that table that adds a column about how much each task cost.

Every month before generating a PDF the data all needs to be updated, and the emacs-lisp at the bottom does that. It's a bit more complicated than I'd like it to be, but it works well. It exports the new clock-table, updates the totals, the data, invoice number, etc. Normally I simply hit F5, and this invokes the lisp and updates everything. The end-result currently looks like this.

Not the prettiest thing in the world, but it serves the purpose just fine. One could fine-tune the org exporter far more to generate prettier invoices if they care enough to do so.