4.3 ⁂Building Your Own Invoice using longtable and
datatool
It may be that the layouts produced by the available classes or
packages don't suit your requirements or perhaps you want to use
isodoc but the \itable
layout doesn't have enough
columns for your needs. Since invoices typically involve aligning
data in rows and columns, you can just use the tabular
environment described in Volume 1
if it can fit within
a single page. If the tabulated data exceeds a page, then you need
to use a multi-paged tabular-like environment, such as the
longtable
environment provided by the longtable
package [11].
The syntax for longtable
is similar to tabular
:
Unlike tabular
the optional argument specifies the
horizontal alignment instead of the vertical alignment since
longtable
isn't intended for in-line positioning. The
horizontal alignment may be one of l (left), c
(centre) or r (right). The default is c.
The ⟨column specs⟩ are the same as for tabular
and
the rows and columns are separated in the same way using
\\ and &. For example:
produces:
However, unlike tabular
, you can also specify a caption
as well as header and footer information. This is done at the start
of the longtable
environment:
\begin
{longtable}{
⟨column specs⟩}
\caption
{
⟨first page caption⟩}
\label
{
⟨table label⟩}
\\
⟨code for first page header row⟩
\endfirsthead
\caption
{
⟨continuation caption⟩}
⟨code for the header row⟩
\endhead
⟨code for last page footer row⟩
\endlastfoot
⟨code for the footer row⟩
\endfoot
⟨contents⟩
\end
{longtable}
longtable
environment correctly aligns the columns.
The header and footer code will typically need to include
& and \\ as per the column
alignment specifications. You can use the starred version of \caption
to suppress the numbering for example:
Recall from Example 4 that datatool's \DTLdisplaylongdb
command could be used to display the contents of a large database
over multiple pages. This command internally uses the
longtable
environment. We could instead explicitly use
that environment to display the data from the sample country-codes.csv file
or countries SQL table:
\begin{longtable}{cl} \bfseries Country Code & \bfseries Country Name\\ \endhead \multicolumn{2}{r}{\emph{Continued on next page}} \endfoot \endlastfoot \DTLforeach*{countries}{\Code=code,\Name=name}{\Code & \Name\\}% \end{longtable}
This sets the same header for all the pages and sets the footer to the text “Continued on next page”. Since this isn't required for the final page, the last footer is set to empty. This produces a seven page table (without a caption). The beginning of the table looks like:
The end of the first page looks like:
The final page of the table is shown in Figure 4.3. You can download or view this example document.
Remember that you can use the booktabs package [24] if you want
horizontal rules and you can use \DTLiflastrow
to suppress the
final \\ which would otherwise create unnecessary
extra vertical space at the end of the table. For example:
\begin{longtable}{cl} \bfseries Country Code & \bfseries Country Name\\ \midrule \endhead \bottomrule \multicolumn{2}{r}{\emph{Continued on next page}} \endfoot \bottomrule \endlastfoot \DTLforeach*{countries}{\Code=code,\Name=name}% {\Code & \Name\DTLiflastrow{}{\\}}% \end{longtable}
Alternatively you can use \DTLiffirstrow
:
\begin{longtable}{cl} \bfseries Country Code & \bfseries Country Name\\ \midrule \endhead \bottomrule \multicolumn{2}{r}{\emph{Continued on next page}} \endfoot \bottomrule \endlastfoot \DTLforeach*{countries}{\Code=code,\Name=name}% {\DTLiffirstrow{}{\\}\Code & \Name}% \end{longtable}
As with tabular
, the column specifiers may include
p{
⟨width⟩}
for a column with multilined cells. Recall
from Volume 1
that the array package [58] can be used to insert
a declaration before each cell in a given column via:
directly before the column specifier. This means that if you have a column for the description of an invoiced item, you can have ragged line wrapping, which looks better than the default fully-justified paragraphs in a narrow column context.
Example
Suppose my invoice needs four columns: the item description, the quantity ordered, the unit price and the quantity times price. The last three columns can just use the r specifier, but the first column may need a paragraph cell in the event of a long description:
\begin{longtable}{>{\raggedright}p{0.3\linewidth}rrr} \bfseries Item & \bfseries Quantity & \bfseries Unit Price (\pounds) & \bfseries Price (\pounds)\\ \midrule \endhead ``\,`Duck and Goose': an allegory for modern times?'' (hardback) & 1 & 59.99 & 59.99\\ ``My Friend is a Duck'' (paperback) & 20 & 14.99 & 299.80\\ ``Annotated Notes on the `Duck and Goose' Chronicles'' (ebook) & 1 & 8.99 & 8.99\\ ``The Adventures of Duck and Goose'' (hardback) & 1 & 18.99 & 18.99\\ \midrule \multicolumn{3}{r}{\bfseries Sub-Total} & 368.78\\ \multicolumn{3}{r}{\bfseries Postage and Packaging} & 20.00\\ \multicolumn{3}{r}{\bfseries Promotional Discount} & $-2.50$\\ \midrule \multicolumn{3}{r}{\bfseries Total} & 386.28 \end{longtable}
This produces:
Adapt Exercise 12 so that it uses
longtable
instead of \itable
, and make it have
separate columns for the quantity and unit price (as in the example
above). In addition, let's now suppose the book prices exclude VAT.
The physical books are zero-rated, but the ebooks are standard-rated
at 20%. Add an extra column that indicates the VAT rating and add
a row for the VAT after the subtotal.
You can download
or
view
a solution.
For the More Adventurous
As with the more adventurous part of Exercise 12, fetch the information from the sample CSV files or SQL database. You can download or view a solution for the CSV files or download or view a solution for the SQL data.
This book is also available as A4 PDF or 12.8cm x 9.6cm PDF or paperback (ISBN 978-1-909440-07-4).