Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I am trying to build an invoice generation system in LaTeX which will be easy and maintainable. Right now we are using longtable and a preprocessing layer. I would like to change this to a .sty and a set of macros so that templates can be more heavily separated from the data entered into them.

What I would like is an API like:

\begin{invoicetable}{r|llrr|r}{runningnumber,description,qty,sellprice,linetotal}
\heading{runningnumber=\#,partnumber=Partnumber,description=Description,qty=Qty,sellprice=Price,linetotal=Total}
\line{runningnumber=1,partnumber=A-12232,description={Test part, some data},qty=4,sellprice=14.99,linetotal=59.96}
\end{invoicetable}

This would be a macro for something like (pretty formatting added here for clarity):

\begin{longtable}{r|llrr|r}
\# & Partnumber & Description          & Qty & Sellprice & linetotal \\
\endhead
 1 & A-12232    & Test part, some data &   4 &     14.99 &     59.96 \\
\end{longtable}

A few reasons for doing this is that it would allow me to drop columns in the environment declaration and have relevant keys ignored in the line items, and it would free up key ordering in the addition of lines themselves (partnumber=A-12232,qty=4 would be the same regardless of ordering).

At this point I have a pretty good idea of how the \line macro would work in that case (I may name it \invoiceline) and if I have trouble I can ask more questions there, but my immediate question is with the argument to the \begin macro. I need to be able to figure out roughly how to internally take the comma separated values in, break them up into an ordered list and a counter, something like a counter of \invoice@cols as a counter, and \invoice@col@current as another one. I figure there would need to be a macro for each col, like \invoice@col1 returning the name of the column.

share|improve this question
add comment

2 Answers

up vote 5 down vote accepted

Here is a solution involving xtring:

\documentclass{article}
\usepackage{xstring,longtable}
\makeatletter
\def\line#1{%
    \global\let\tab@keys@i\tab@keys
    \gdef\line@content{#1,}%
    \line@i
}

\def\line@i{%
    \StrCut\tab@keys@i,\current@key\tab@keys@i
    \global\let\tab@keys@i\tab@keys@i
    \IfSubStr\line@content\current@key
        {\StrBehind\line@content\current@key[\current@key]%
        \StrBetween\current@key=,%
        }
        \relax
    \ifx\@empty\tab@keys@i\\%
    \else&\expandafter\line@i
    \fi
}

\def\heading#1{\line{#1}\endhead}

\newenvironment{invoicetable}[2]%
    {\edef\tab@keys{#2,}%
    \expandarg\noexploregroups
    \longtable{#1}%
    }
    \endlongtable

\makeatother
\begin{document}
\begin{invoicetable}{r|llrr|r}{runningnumber,description,qty,sellprice,linetotal}
    \heading{runningnumber=\#,partnumber=Partnumber,description=Description,qty=Qty,sellprice=Price,linetotal=Total}
    \line{runningnumber=1,partnumber=A-12232,description={Test part, some data},qty=4,sellprice=14.99,linetotal=59.96}
\end{invoicetable}
\end{document}
share|improve this answer
    
If I'm not mistaken, this requires that the keys in \line (by the way, \line is already a LaTeX command) are in the same order as in the argument. –  egreg Feb 23 '13 at 13:44
    
No, try \line{qty=4,linetotal=59.96,description={Test part, some data},sellprice=14.99,partnumber=A-12232,runningnumber=1} and the rtesult is the same. If \line is already defined, juste replace it by anything else, for example \myline and use \newcommand, which is safer than \def. –  unbonpetit Feb 23 '13 at 14:07
    
I think this is the approach I will go with for now. I plan to change \line to \invoiceline –  Chris Travers Mar 3 '13 at 2:48
add comment

Here's an implementation with LaTeX3 macros.

\documentclass{article}
\usepackage{longtable}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentEnvironment{invoicetable}{mm} % #1 = columns, #2 = column names
 {
  \travers_invoicetable:n { #2 }
 }
 {
  \begin{longtable}{#1}
  \l_travers_tablehead_tl
  \hline
  \endhead
  \l_travers_table_tl
  \end{longtable}
 }

\NewDocumentCommand{\theading}{m}
 {
  \travers_makeline:Nn \l_travers_tablehead_tl { #1 }
 }

\NewDocumentCommand{\tline}{m}
 {
  \travers_makeline:Nn \l_travers_table_tl { #1 }
 }


\tl_new:N \l_travers_tablehead_tl
\tl_new:N \l_travers_table_tl
\tl_new:N \l__travers_linetemp_tl
\tl_new:N \l_travers_lastcol_tl
\seq_new:N \l_travers_colnames_seq

%%% what to do with unknown keys    
\keys_define:nn { travers/invoice }
 {
  unknown .code:n =
 }

% absorb the list of column names and define the keys
\cs_new_protected:Npn \travers_invoicetable:n #1
 {
  \seq_set_split:Nnn \l_travers_colnames_seq { , } { #1 }
  \seq_map_inline:Nn \l_travers_colnames_seq
   {
    \keys_define:nn { travers/invoice }
     {
      ##1 .tl_set:c = { l__travers_name_##1_tl }
     }
   }
  % detach the last column
  \seq_pop_right:NN \l_travers_colnames_seq \l_travers_lastcol_tl
 }

% each line processes the key-value pairs and adds to the table
\cs_new_protected:Npn \travers_makeline:Nn #1 #2
 {
  \tl_clear:N \l__travers_linetemp_tl
  \keys_set:nn { travers/invoice } { #2 }
  \seq_map_inline:Nn \l_travers_colnames_seq
   {
    \tl_put_right:Nv \l__travers_linetemp_tl { l__travers_name_##1_tl }
    \tl_put_right:Nn \l__travers_linetemp_tl { & }
   }
  \tl_put_right:Nv \l__travers_linetemp_tl { l__travers_name_\l_travers_lastcol_tl _tl }
  \tl_put_right:Nn \l__travers_linetemp_tl { \\ }
  \tl_put_right:NV #1 \l__travers_linetemp_tl
 }

\cs_generate_variant:Nn \tl_put_right:Nn { Nv }

\ExplSyntaxOff

\begin{document}
\begin{invoicetable}{r|llr|r}{
  runningnumber,
  description,
  qty,
  sellprice,
  linetotal
}
\theading{
  runningnumber=\#,
  partnumber=Part number,
  description=Description,
  qty=Qty,
  sellprice=Price,
  linetotal=Total
}
\tline{
  runningnumber=1,
  partnumber=A-12232,
  description={Test part, some data},
  qty=4,
  sellprice=14.99,
  linetotal=59.96
}
\end{invoicetable}

\begin{invoicetable}{r|llrr|r}{
  runningnumber,
  partnumber,
  description,
  qty,
  sellprice,
  linetotal
}
\theading{
  runningnumber=\#,
  partnumber=Part number,
  description=Description,
  qty=Qty,
  sellprice=Price,
  linetotal=Total
}
\tline{
  runningnumber=1,
  partnumber=A-12232,
  description={Test part, some data},
  qty=4,
  sellprice=14.99,
  linetotal=59.96
}
\end{invoicetable}

\begin{invoicetable}{r}{
  linetotal
}
\theading{
  runningnumber=\#,
  partnumber=Part number,
  description=Description,
  qty=Qty,
  sellprice=Price,
  linetotal=Total
}
\tline{
  runningnumber=1,
  partnumber=A-12232,
  description={Test part, some data},
  qty=4,
  sellprice=14.99,
  linetotal=59.96
}
\end{invoicetable}

\end{document}

enter image description here

share|improve this answer
    
I am not sure I can count on LaTeX3 for this. How hard is it to get this to work with LaTeX-2E? –  Chris Travers Feb 23 '13 at 12:06
    
@ChrisTravers You just need a fairly recent TeX distribution, which contains the expl3 bundle. –  egreg Feb 23 '13 at 12:57
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.