Articles on how to create OpenDocument invoices already exist but almost always they require you to start and use OpenOffice manually each time. Here, instead, I’ll show how to have your computer to do all your OpenDocument work for you.

How to automatically create OpenDocument invoices without OpenOffice /img/odf_scripting_sample_invoice.png

The script explained below takes an ODF template like the one of the left and generates an ODF text file like the one you see on the right below, which is ready to be printed or sent via email (follow the links to download the template or the resulting ODF invoice).

How to automatically create OpenDocument invoices without OpenOffice /img/odf_scripting_customized_invoice.png

The advantages of creating the invoice with a script rather than with macros are explained in detail in the “Why and how ODF can save you a lot of time” page: in a nutshell, once you have created and saved with any ODF compliant word processor the initial template, the whole process is completely automatic, so it could run unattended and be integrated with other backend systems even where OpenOffice isn’t installed.

The script takes two arguments, the template name and an invoice data file:

odf_invoice_generator.sh odf_scripting_sample_invoice.odt my_data.sh

then opens the template, replaces placeholder data or strings like e.g. __Customer_name with the proper values from the invoice data file and finally saves everything as a separate OpenDocument text file. The data file has an extremely simple format, since it’s only variable assignments in shell script syntax:

  marco => cat my_data.sh
  INVOICE_DATE='2010/05/15'
  VENDOR_CODE='007'
  PO_NUMBER='Purchase Order #1'
  TOTAL=10
  ISSUE=150
  DESCRIPTION='Here is your invoice'

and can be automatically generated on the spot by querying a database, by a Web server or in many other ways.

Here is the complete script, followed by an explanation:

       0 #!/bin/bash
       1  WORK_DIR=odt_invoice_generator_temp_dir
       2
       3  rm -rf $WORK_DIR
       4  mkdir  $WORK_DIR
       5  FILENAME=`basename $1 .odt`
       6
       7  cp     $1 $WORK_DIR/my_template.odt
       8  cp     $2 $WORK_DIR/my_data.sh
       9
      10  ## preparation
      11  cd     $WORK_DIR
      12  mkdir  work
      13  mv     my_template.odt work
      14  cd     work
      15  source ../my_data.sh
      16  unzip  my_template.odt > /dev/null
      17  rm     my_template.odt
      18
      19  ## replace text strings
      20  sed "s|__INVOICE_DATE|$INVOICE_DATE|"  content.xml  
      21  | sed "s|__VENDOR_CODE|$VENDOR_CODE|"               
      22  | sed "s|__PO_NUMBER|$PO_NUMBER|"                   
      23  | sed "s|__TOTAL|$TOTAL|g"                          
      24  | sed "s|__ISSUE_NUMBER|$ISSUE|"                    
      25  | sed "s|__DESCRIPTION|$DESCRIPTION|"               
      26  | sed "s|__Customer_name|$Customer_name|"           
      27  > custom_content.xml
      28  mv custom_content.xml content.xml
      29
      30  ## zip everything, rename it as .odt file and clean up
      31  find . -type f -print0 | xargs -0 zip ../$FILENAME > /dev/null
      32  cd ..
      33  mv $FILENAME.zip ../new_$FILENAME.odt
      34  cd ..
      35  rm -rf $WORK_DIR

The lines from 1 to 17 don’t do anything difficult: create a temporary working directory (WORK_DIR) copy the template inside it, unzip the template, and load from the data file (line 15) the values that must fill the template.

The (relatively) tricky part are lines 19 to 26: this is where a series of sed commands replaces each placeholder string in the content.xml file which contains the template text with its value loaded from my_data.sh.

If you change the template you must add here one sed command for each string you want to substitute; the order is not important. Just remember that if a string occurs multiple times, as is the case with the __TOTAL price, you must add the g (global) option to sed (cfr line 23), otherwise the script will only replace the first occurrence of that string.

If you have any question or suggestion about this script, please email me.