0

I will try to explain my problem as precise as I can, feel free to ask me more details:

  • I'm currently working on a invoice generator with LateX and template toolkit, I know it's not optimal but it's legacy code and we can't create a new one ;

  • I want to add a banner on top of a particular part of the pdf document which are table, when the table is long enought to create a new page, so I need this banner on top of the table second page ;

  • I got multiple template which depend of the services/type of goods, so the debug is quite complicated because I never know in which file or line the erros really occurs (yes I used -file-line-error debug but it show the compiling temporary files in var/tmp and not the files I'm currently working on) ;

Question : Is this even possible ? For me LaTex was only for designing scientific documents, not for invoice, and the compiling process with file included seems working badly.

What I tried : To make a table that incorporate the different template part, with a table header that looks like a banner. Problem is the different template part are also table and I got multiple error I didn't manage to solve due to the architecture of this invoice generator because debug point things in temporary compiling pdf and not in the working files I'm on. Errors like :

  1. Underfull \hbox (badness 10000) in paragraph at lines xxx--xxx ;

  2. ./tempi6163.tex:723: Too many }'s.

Any hints, work around and advice are more than welcome, I litteraly know noone who have ever use LaTeX except for thesis document but in a very basic way.

Heres the code, warning it may scares you :

Invoice layout skeleton :

\documentclass[a4paper,10pt]{article}
\usepackage[utf8]{inputenc}
\usepackage[frenchb]{babel}
\usepackage[T1]{fontenc}
\usepackage{textcomp}
\usepackage{underscore}
\usepackage{color}
\usepackage{colortbl}
\usepackage{xcolor}
\usepackage{transparent}
\usepackage{wallpaper}
\usepackage{atbegshi,picture}
\usepackage{lastpage}
\usepackage{longtable}
%\usepackage[top=[%- document_config.document_top -%],left=0.15cm,bottom=2.5cm,right=0.3cm]{geometry}

% Use this package below to modify document layout : https://texdoc.org/serve/geometry.pdf/0 \usepackage[top=2.5cm,left=0.15cm,bottom=2.5cm,right=0.3cm]{geometry} \usepackage{setspace} \usepackage{array} \usepackage{avant} \usepackage{hyperref} \usepackage{booktabs} \usepackage{multicol} \usepackage{multirow} \usepackage{makecell} \usepackage{hhline} \usepackage[nomessages]{fp} \definecolor{labelcolor}{HTML}{E8E8E8} [%- IF document_config.document_watermark %] \usepackage{draftwatermark} [%- END %] [%- IF document_config.entity_ref == 'DCFORDATA' %] \hypersetup{colorlinks=true,urlcolor=[HTML]{49906D},linkcolor=black} [%- ELSE %] \hypersetup{colorlinks=true,urlcolor=blue,linkcolor=black} [%- END %] % }}} % {{{ definition de commandes specifiques \newcounter{period} [%- IF document_datas.info_client.bill_period_num %] \setcounter{period}{[%- document_datas.info_client.bill_period_num -%]} [%- ELSE %] \setcounter{period}{1} [%- END %] % % chargement des fichiers multi-langues % \input{[%- document_config.template_path -%]/jn_pdf_intl_defs.tex} [% IF document_datas.headcell_type.head_type == 1 %] \newcommand{\JNHeadCellTitle}{\Fr{Frais d'acc`{e}s au Service}\En{Non-reccuring}} [% ELSIF document_datas.headcell_type.head_type == 2 %] \newcommand{\JNHeadCellTitle}{\Fr{N'{e}goce}\En{Resale}} [% ELSIF document_datas.headcell_type.head_type == 3 %] \newcommand{\JNHeadCellTitle}{\Fr{FAS / N'{e}goce}\En{Non-reccuring / Resale}} [% ELSE %] \newcommand{\JNHeadCellTitle}{\Fr{Frais d'acc`{e}s au Service}\En{Non-reccuring}} [% END %] % % definitions de commandes % %\renewcommand{\familydefault}{\sfdefault} \renewcommand{\rmdefault}{phv} % Arial \renewcommand{\sfdefault}{phv} % Arial \renewcommand\textrangle{>} \renewcommand\textlangle{<} \renewcommand\arraystretch{1} \renewcommand\footnotemark{} \renewcommand\footnoterule{} \newcommand{\HRule}{\rule{\linewidth}{0.1mm}} \newcommand{\tabularnewline}{\} \newcommand{\tabularendbloc}{

\medskip
}

\newcommand{\csp}[1]{\hspace{#1mm}} \newcommand{\cell}{&} \newcommand{\hcell}[1]{\cell \hspace{#1mm}} \newcommand{\price}[1]{\footnotesize #1} \newcommand{\rprice}[1]{\multicolumn{1}{r|}{\footnotesize #1}} \newcommand{\infoline}[2]{\multicolumn{#1}{c}{\footnotesize\textit{#2} } } \newcommand{\infoprice}[2]{\multicolumn{#1}{r}{\footnotesize\textit{#2} } } \newcommand{\infototalprice}[2]{\multicolumn{#1}{r}{#2} } \newcommand{\emptyline}[1]{\multicolumn{#1}{r}{}} \providecommand{\bloctitle}[1]{ {\renewcommand{\arraystretch}{1.3} \footnotesize \setlength{\tabcolsep}{1mm} \begin{tabular}{|>{\raggedright}p{19.12cm}|} \hline \multicolumn{1}{|>{\columncolor{mycellcolor}}l|}{ \tabulartitlebox{19.12cm}{\LARGE \textbf{#1}} } \tabularnewline \hline \end{tabular} \tabularendbloc } } \providecommand{\blocsubtitle}[1]{ {\renewcommand{\arraystretch}{1} \footnotesize \setlength{\tabcolsep}{1mm} \begin{tabular}{|>{\raggedright}p{19.12cm}|} \multicolumn{1}{l}{\multirow{-1}{19.12cm}{\large{\textbf{#1}} \dotfill}} \ \end{tabular} \tabularendbloc } } \providecommand{\tabulartitlebox}[2]{ \makebox{ \parbox{#1}{ \vspace{0.1cm} \large{#2} \vspace{0.1cm} } } } %taille de la ram \newcommand{\ramsize}[1]{ \FPeval\sram{round((#1)/1024,0)}% \sram } %calcul prixperiode \newcommand{\PPerPeriod}[2]{ \FPeval\pperiod{round(\theperiod(#1)(#2),2)}% \pperiod } %calcul prixperiode \newcommand{\QPerPeriod}[1]{ \FPeval\pperiod{round(\theperiod(#1),0)}% \pperiod } \newcommand{\PPerQuantity}[2]{ \FPeval\qprice{round((#1)(#2),2)}% \qprice } %prix unitaire 0.2f \newcommand{\UnitPrice}[2][2]{ \FPeval\pperiod{round(1(#2),#1)}% \pperiod } %prix unitaire à 5 chiffres 0.5f \newcommand{\UnitPriceFiveDigits}[2][5]{ \FPeval\pperiod{round(1(#2),#1)}% \pperiod } %total 0.2f \newcommand{\TotalPrice}[2][2]{ \FPeval\pperiod{round(1*(#2),#1)}% \pperiod } %ligne vide \newcommand{\DSLineEmpty}{ \cell \cell \cell \cell \cell \cell \tabularnewline } %ligne titre, nom Service sans prix) \newcommand{\DSLineTitle}[1]{ \rowcolor{mycellcolor} \normalsize{\textbf{#1}} \cell \cell \cell \cell \cell \cell \tabularnewline } %ligne rubrique (options, extremite etc..., sans prix) \newcommand{\DSLineSubTitle}[2]{ \csp{#1} \footnotesize{\textbf{#2}} \cell \cell \cell \cell \cell \cell \tabularnewline } %ligne texte (sans prix) \newcommand{\DSLineSimpleText}[2]{ \csp{#1} \footnotesize{#2} \cell \cell \cell \cell \cell \cell \tabularnewline } %ligne texte LABEL (sans prix) \newcommand{\DSLineSimpleLabel}[2]{ \cellcolor{labelcolor}\csp{#1} \footnotesize{#2} \cell \cell \cell \cell \cell \cell \tabularnewline } %ligne texte (prix fas) \newcommand{\DSLineFasPrice}[4]{ \csp{#1} \footnotesize{#2} \dotfill \cell \footnotesize{#3} \cell \footnotesize{\UnitPrice{#4}} \cell \footnotesize{\PPerQuantity{#3}{#4}} \cell \cell \cell \tabularnewline } \newcommand{\DSLineFasPriceFiveDigits}[4]{ \csp{#1} \footnotesize{#2} \dotfill \cell \footnotesize{#3} \cell \footnotesize{\UnitPriceFiveDigits{#4}} \cell \footnotesize{\PPerQuantity{#3}{#4}} \cell \cell \cell \tabularnewline } %ligne texte (prix rec) \newcommand{\DSLineRecPrice}[4]{ \csp{#1} \footnotesize{#2} \dotfill \cell \dotfill \cell \dotfill \cell \dotfill \cell \footnotesize{#3} \cell \footnotesize{\UnitPrice{#4}} \cell \footnotesize{\PPerQuantity{#3}{#4}} \tabularnewline } \newcommand{\DSLineRecPriceFiveDigits}[4]{ \csp{#1} \footnotesize{#2} \dotfill \cell \dotfill \cell \dotfill \cell \dotfill \cell \footnotesize{#3} \cell \footnotesize{\UnitPriceFiveDigits{#4}} \cell \footnotesize{\PPerQuantity{#3}{#4}} \tabularnewline } %ligne texte (prix rec) \newcommand{\DSLineFasRecPrice}[6]{ \csp{#1} \footnotesize{#2} \dotfill \cell \footnotesize{#3} \cell \footnotesize{\UnitPrice{#4}} \cell \footnotesize{\PPerQuantity{#3}{#4}} \cell \footnotesize{#5} \cell \footnotesize{\UnitPrice{#6}} \cell \footnotesize{\PPerQuantity{#5}{#6}} \tabularnewline } \newcommand{\DSLineFasRecPriceFiveDigits}[6]{ \csp{#1} \footnotesize{#2} \dotfill \cell \footnotesize{#3} \cell \footnotesize{\UnitPriceFiveDigits{#4}} \cell \footnotesize{\PPerQuantity{#3}{#4}} \cell \footnotesize{#5} \cell \footnotesize{\UnitPriceFiveDigits{#6}} \cell \footnotesize{\PPerQuantity{#5}{#6}} \tabularnewline } \definecolor{mycellcolor}{gray}{0.95} \setlength\arrayrulewidth{0.7pt} \arrayrulecolor{black} % }}} % {{{ definition fond de page \newcommand\BackgroundPic{ \put(0,0){ \parbox[b][\paperheight]{\paperwidth}{ \vfill \centering [% IF NOT document_config.document_watermark -%] \includegraphics[width=\paperwidth,height=\paperheight,keepaspectratio]{[%- document_config.background_path -%]/[%- document_config.background_file -%]}% [%- END %] \vfill } } }

\newcommand{\margin}[2] { \hspace{#1}\makebox{ \parbox[t]{21cm}{ #2 } } } % }}} % {{{ entete et pied de page en mode galerien \AtBeginShipout{ \AtBeginShipoutUpperLeftForeground{ %link \small [%- IF document_config.header_url %] \put(16.2cm,-1.5cm){ \makebox{ [%- IF NOT document_config.document_watermark -%] \href{[%- document_config.document_head_url_link -%]}{[%- document_config.document_head_url_name -%]} [%- END -%] } } [%- END %] %foot address [%- IF document_config.footer ; IF document_datas.pdf_langage != 'FR' ; document_config.document_foot_address_infos = document_config.document_foot_address_infos.replace('au capital de', 'with a capital of'); END %] \put(0.6cm,-\dimexpr\paperheight-1cm){ \makebox{ \parbox{\paperwidth-1cm}{ [%- IF NOT document_config.document_watermark -%] \centering \color[gray]{0.60} \footnotesize \singlespacing{ [% document_config.document_foot_address -%] \ [% document_config.document_foot_address_infos %] } [%- END -%] } } } %End of 'foot address' block [%- END %] %pager \put(0.8cm,-\dimexpr\paperheight-1.1cm){ \makebox[19.5cm][r]{ \color{black} \small page \thepage / \pageref{LastPage} } } %End of 'pager' block %foot engagement \put(0.6cm,-\dimexpr\paperheight-2cm){ \makebox{ \parbox{\paperwidth-1cm}{ \centering \footnotesize \singlespacing{ [%- IF NOT document_config.document_watermark -%] % [%- IF document_datas.entity_contract == '' -%] % [% "bdc.cgv_accept" | trans %]\Fr{ [%- document_datas.header.engagment_entity -%]}\En{}.\ [% "bdc.valid_30_days" | trans %] % [%- ELSE -%] % ~ \ [% "bdc.contract_accept" | trans %] \textbf{[%- document_datas.entity_contract -%]}. % [%- END %] % [%- END %] } } } } %End of 'foot engagement' block %foot paraph \put(19.5cm, -29.4cm){ \makebox{ \normalfont{ \tiny \transparent{0} doc_PR } } } %End of 'foot paraph' block } } %End of 'AtBeginShipoutUpperLeftForeground' block % }}} % {{{ document pdf info et watermark \pdfinfo{ /Author ([%- document_config.document_author -%]) /Title ([%- document_config.document_title -%]) /CreationDate (D:[%- document_config.document_date -%]) /Subject ([%- document_config.document_subject -%]) /Keywords ([%- document_config.document_keywords -%]) /Creator (JNTools) /Producer ([%- document_config.document_author -%]) } [% IF document_config.document_watermark -%] %document_wathermark \SetWatermarkText{[%- document_config.document_watermark_text -%]} \SetWatermarkScale{[%- document_config.document_watermark_size -%]} \SetWatermarkAngle{[%- document_config.document_watermark_angle -%]} [% END -%] % }}} % {{{ contenu document \begin{document} \AddToShipoutPicture{\BackgroundPic} \pagestyle{empty} \singlespacing \small [% INCLUDE $document_config.body_to_include %] \end{document} % }}}

Invoice body :

# Customer information
INCLUDE 'bdc/informations.tt' ;
global.voice_grid_list = [] ;
global.storage_is_autosized = 0;
# List of services
INCLUDE 'bdc/description_services.tt' ;
# Sign bloc
IF NOT document_config.document_watermark ;
INCLUDE 'bdc/signature_engagement.tt' ;
END ;
# Grilles voix
IF global.voice_grid_list.size > 0 ;
    global.voice_grid_list_unique = global.voice_grid_list.unique;
    INCLUDE 'bdc/generics/voice_grid.tt' ;
END ;
# Grilles v5
IF document_datas.grids.size > 0 ;
    INCLUDE 'bdc/generics/grids.tt' ;
END ;
IF NOT document_config.document_watermark ;
    # SEPA Mandate
    IF (document_datas.entity_status == 'prospect' && document_datas.info_client.bill_type_payment == 'PRVLT' && document_datas.mandat.rendering == 'auto') || (document_datas.mandat.rendering == 'yes') ;
        INCLUDE 'bdc/mandat_prelevement.tt' ;
    END ;
    IF document_datas.lic_mobility == 'yes' ;
        INCLUDE 'bdc/lic_mobility.tt' ;
    END ;
    # CGV
    IF  ((document_datas.entity_status == 'prospect'
          && document_datas.info_client.bill_type_payment == 'PRVLT'
          && document_datas.cgv.rendering == 'auto')
         || (document_datas.cgv.rendering == 'yes'))
        && (document_datas.entity_contract == '');
        cgvfile = 'bdc/cgv_' _ document_datas.cgv.version _ '.tt' ;
        INCLUDE $cgvfile ;
        INCLUDE 'bdc/cgv_signature.tt' ;
    END ;
END ;
-%]

The grid part included in invoice body which I want to add the banner describe above, foreach INCLUDE 'grids/grid.tt' I want to add the banner on top of it except on first page :

[%# collect voice groups informations %]
[%
SET global.has_voice_groups = 0;
SET global.voice_group_defs = {};
SET global.voice_uuid_names = {};

FOREACH topgrid IN document_datas.grids; SET grid = topgrid.info; SET additional_info = topgrid.additional_info;

INCLUDE 'grids/informations.tt';

%] \vspace{0.1cm} [% IF grid.rendering_mode == 'child'; %] \bloctitle{[% grid.grid_ref %] : [% "grids.type.${grid.grid_line_type_ref}" | trans %]} [% ELSE; %] \bloctitle{[% grid.grid_ref %] : [% grid.name %]} [% END; %]

[% SET additional_info = topgrid.additional_info;

IF grid.rendering_mode == 'meta';
    engagement_coefs = grid.engagement_coefs;
    FOREACH grid IN topgrid.childs;
        SET additional_info = grid.additional_info;
        INCLUDE 'grids/grid.tt';
        IF engagement_coefs.${grid.grid_line_type_ref} ;
            INCLUDE 'grids/engagement_coefs.tt';
        END;
     END;
ELSE;
    INCLUDE 'grids/grid.tt';
END;

END;

IF global.has_voice_groups > 0; INCLUDE 'grids/generic/voice_groups.tt'; END;

IF NOT document_datas.is_preview; INCLUDE 'grids/signature.tt'; END; -%]

Inside the INCLUDE 'grids/grid.tt' this what I tried :

% The banner I want to include on table format
\vspace{0.1cm}
{\renewcommand{\arraystretch}{1.2}
    \setlength{\LTleft}{0.50cm}
    \setlength{\tabcolsep}{1mm}
    \begin{longtable}{|>{\raggedright}p{11.1cm}|>{\raggedleft}p{0.65cm}|>{\raggedleft}p{1.3cm}|>{\raggedleft}p{1.5cm}|>{\raggedleft}p{0.65cm}|>{\raggedleft}p{1.3cm}|>{\raggedleft}p{1.5cm}|}

% I choose 4 column to test but is there a way to detect how many column are going to be included ?

\multicolumn{4}{c}{ [%- IF NOT document_config.document_watermark -%] \parbox[t]{19.55cm}{\HRule\[0cm] \parbox[t]{7.7cm}{\footnotesize{\textbf{~\Fr{Responsable de compte}\En{Account Manager}~:~[%- document_datas.header.commercial -%]} } } \parbox[t]{5cm}{\vrule\footnotesize{\textbf{~\Fr{Date d''{e}mission}\En{Date}~:~}{[%- document_datas.header.date_emission -%]} } } [%- IF (document_datas.doctype == 'bdc') -%] \parbox[t]{6cm}{\vrule\footnotesize{\textbf{~\Fr{R'{e}f'{e}rence de commande}\En{PO Number}~:~}{[%- document_datas.header.bdc_ref -%]} } } [%- ELSE -%] [%- IF (document_datas.type_ent == 'client') AND (grid.is_signed) -%] \parbox[t]{6cm}{\vrule\footnotesize{\textbf{~\Fr{Date de signature}\En{Signature date}~:~}{[%- grid.signature_date -%]} } } [%- END -%] [%- END -%] \[-0.2cm]\HRule\ } [%- END -%] }

%the different template which depend on type of services/goods [% INCLUDE "grids/grid/${grid.grid_line_type_ref}.tt" %]

\endhead \end{longtable}

And here's a sample of a template (${grid.grid_line_type_ref}.tt) I want to integrate inside which are also table :

{\renewcommand{\arraystretch}{1.5}
    \setlength{\LTleft}{0.50cm}
    \setlength{\tabcolsep}{1mm}
        \begin{longtable}{|c|r|r|r|}
        \hline
        \rowcolor{mycellcolor}
        \multicolumn{1}{|c|}{\multirow{-1}{7cm}{\Fr{Type de ligne}\En{Line type}}}
        & \multicolumn{1}{r|}{\multirow{-1}{2cm}{[%- "grids.type.sfr-adsl.fas"|trans -%]}}
        & \multicolumn{1}{r|}{\multirow{-1}{2cm}{[%- "grids.type.sfr-adsl.abo"|trans -%]}}
        & \multicolumn{1}{r|}{\multirow{-1}{2cm}{[%- "grids.type.sfr-adsl.gtrs1"|trans -%]}}
        \\ [0.7ex]
        \hline
        \endfirsthead
        \hline
        \endfoot
        \hline
        \rowcolor{mycellcolor}
        \multicolumn{1}{|c|}{\multirow{-1}{7cm}{\Fr{Type de ligne}\En{Line type}}}
        & \multicolumn{1}{r|}{\multirow{-1}{2cm}{[%- "grids.type.sfr-adsl.fas"|trans -%]}}
        & \multicolumn{1}{r|}{\multirow{-1}{2cm}{[%- "grids.type.sfr-adsl.abo"|trans -%]}}
        & \multicolumn{1}{r|}{\multirow{-1}{2cm}{[%- "grids.type.sfr-adsl.gtrs1"|trans -%]}}
        \\ [0.7ex]
        \hline
        \endhead
        \infoprice{4}{[%- "grids.infos.all_prices_euros_ht"|trans -%]}
        \endlastfoot
        [% FOREACH object IN grid.objects %]
            \multicolumn{1}{|l|}{[% object.description %]}
            & \multicolumn{1}{r|}{\UnitPrice[2]{[%-object.fas-%]}}
            & \multicolumn{1}{r|}{\UnitPrice[2]{[%-object.abo-%]}}
            [% IF (object.gtrs1 == 0) %]
            & \multicolumn{1}{r|}{\Fr{Incluse}\En{Included}}
            [% ELSE %]
            & \multicolumn{1}{r|}{\UnitPrice[2]{[%-object.gtrs1-%]}}
            [% END %]
            \\
        [% END %]
        \hline
    \end{longtable}
}
David Carlisle
  • 757,742
Sarp
  • 1
  • I have no idea what templating language you are using to generate the latex source, but that probably doesn't matter. In your longtable code if you replace \\[-0.2cm]\HRule\\ by \\[-0.2cm]\HRule\endhead then that part will be repeated every page, which seems to be your main question. – David Carlisle Nov 08 '21 at 11:29
  • for the underfull hbox warning see https://tex.stackexchange.com/questions/334246/what-does-the-phrase-underfull-hbox-badness-10000-in-paragraph-actually-mea/334249#334249 – David Carlisle Nov 08 '21 at 11:35
  • You haven't said what programming language IF NOT document_config.document_watermark ; is (and I don't recognise it) but you would find it easier to get help here if you removed all this template code from the question and just show one generated latex file. Once you have the generated latex edited to do what you want then you should be able to go back and modify your templating script to generate that target document. – David Carlisle Nov 08 '21 at 12:27
  • Hi David, thank's for your reply. Actually I said it, the programming language you mention is template toolkit (quite unknown), and I added \endhead at the end of my try : "And here's a sample of a template (${grid.grid_line_type_ref}.tt) I want to integrate inside which are also table :" but it didn't work as expected because my templates doesn't have a fixed column number. I will try to reconstruct a one LaTeX file but I don't think it's relevant because there is almost 25 different template with different construction... Anyway I'll try ! – Sarp Nov 08 '21 at 13:19
  • Hello David, I try to reconstruct the latex file without the template toolkit part (which include some perl as well, I forgot to mention it) but like I said, there is 15 grid template with different column number so I can't make a generic multicolumn header with the banner. LaTeX is powerful but not for that kind of use, I will rework the invoice system based on html/css to pdf with python which is way more easier to maintain for future developer. Thank's for your time and have a nice journey. – Sarp Nov 09 '21 at 10:45

0 Answers0