Mise en page pdf

Rebonjour Ă  tous,
Après plusieurs séries d’essai, je n’arrive toujours pas à résoudre ce problème de mise en page pdf.
Dès que j’ai un article trop grand, il n’arrive pas bien à se couper en 2.

J’ai déjà réussi à résoudre le problème avec un saut de page à la fin de l’article précédent, mais quand j’ai un article qui fait à lui tout seul plus d’une page, j’ai l’affichage sur le titre du table comme illustré ci-dessous.

Je vous met aussi mon format personnalisé.

Merci pour vos avis et conseils

<!Doctype HTML>
<div id="header-html" class="print-heading">
    <div class="row print-heading">
        <div class="col-xs-6 text-left">
            {% if not no_letterhead and footer %}
                {{ letter_head }}
            {% endif %}
        </div>
        <div class="col-xs-6">
            <table class="pull-right" style="margin-top:-10px;">
                <tbody>
                    <tr>
                        <td colspan="2"
                            style="margin:0px; text-transform: uppercase; text-align: center; font-size:20px; font-weight: bold; color: grey;">
                            {{ doc.select_print_heading or (doc.print_heading if doc.print_heading != None else
                            _(doc.doctype)) }} </td>
                    </tr>
                    <tr style="color: grey;">
                        <th>Référence</th>
                        <th>Date</th>
                    </tr>
                    <tr>
                        <td>{{ doc.sub_heading if doc.sub_heading != None else doc.name }}</td>
                        <td>{{ doc.get_formatted("transaction_date") }}</td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
</div>
<div id="main-html">
    {% set account_manager = frappe.db.get_value("Customer", doc.party_name, "account_manager") %} 
    {% set user = frappe.get_doc("User", account_manager) %} 
    <div class="row">
        <div class="col-xs-12">
            <b>{{ _("Contact") }}: </b> {{ user.first_name or ''}} {{ user.last_name or '' }}<br>
            <b>{{ _("Email") }}: </b> {{ user.email or ''}}<br> <b>{{ _("Phone") }}: </b> {{ user.mobile_no or ''}}
        </div>
    </div>
    <div class="row" style="margin:40px;">
<!-- debut status -->
        <div class="col-xs-6">
            <div class="row"> 
                {%- if doc.meta.is_submittable and doc.docstatus==0 and (print_settings==None or print_settings.add_draft_heading) -%}
                    <div class="col-xs-12 text-center" document-status="draft">
                        <h4>{{ _("DRAFT") }}</h4>
                    </div>
                {%- endif -%}
                {%- if doc.meta.is_submittable and doc.docstatus==2-%} 
                    <div class="col-xs-12 text-center" document-status="cancelled">
                        <h4>{{ _("CANCELLED") }}</h4>
                    </div> 
                {%- endif -%}
            </div>
        </div>
<!-- fin status -->
<!-- debut coordonne client -->
        <div class="col-xs-6">
            <div class="row"> 
                {% set salutation = frappe.db.get_value("Customer", doc.customer, "salutation") %}
                <div class="col-xs-12 text-left" style="font-size:1.2em">
                    <strong>{{ salutation or '' }} {{ doc.customer_name }}</strong>
                </div>
            </div> 
            {% if doc.customer_address %}
                {% set client_address = frappe.get_doc("Address", doc.customer_address) %}
                <div class="row">
                    <div class="col-xs-12 text-left"> 
                        {{ client_address.address_line1 }}<br>
                        {% if client_address.address_line2 %}{{ client_address.address_line2 }}<br>{% endif %}
                        {{ client_address.pincode }} {{ client_address.city }}<br> 
                        {{ client_address.country }}
                    </div>
                </div>
            {% endif %}
        </div>
    </div>
<!-- fin coordonne client -->
<!-- debut details items -->
    <div class="row">
        <div id="texte_presentation" class="col-xs-12">
            <p>{{ doc.get_formatted("presentation_devis") }}</p>
        </div>
    </div>
    {%- if doc.page_garde -%} <div class="page-break"></div> {%- endif -%}
    <div class="row">
        <div class="col-xs-12">
            <table id="list_articles" class="table table-condensed table-bordered">
                <thead>
                    <tr>
                        <th>{{ _("Description") }}</th>
                        <th class="text-right">{{ _("Rate") }}</th>
                        <th>{{ _("Quantity") }}</th>
                        <th class="text-right">{{ _("Amount") }} HT</th>
                    </tr>
                </thead>
                <tbody> 
                {%- for row in doc.items -%} 
                    <tr class="keep-together">
                        <td style="width: 60%;">
                            <h1 id="item_name">{{ row.item_name or '' }}</h1>
                            <div style="padding-left:20px">{{ row.description }}</div>
                        </td>
                        <td style="width: 15%; text-align: right;">{{ row.get_formatted("rate", doc) or ''}}</td>
                        <td style="width: 10%; text-align: left;">{{ row.get_formatted("qty") }} {{ row.uom }}</td>
                        <td style="width: 15%; text-align: right;">{{ row.get_formatted("amount", doc) or ''}}</td>
                    </tr> 
                    {%- if row.page_break -%} 
                </tbody>
            </table>
        </div>
    </div>
    <div class="page-break"></div>
    <div class="row">
        <div class="col-xs-12">
            <table id="list_articles" class="table table-condensed table-bordered">
                <thead>
                    <tr>
                        <th>{{ _("Description") }}</th>
                        <th class="text-right">{{ _("Rate") }}</th>
                        <th>{{ _("Quantity") }}</th>
                        <th class="text-right">{{ _("Amount") }} HT</th>
                    </tr>
                </thead>
                <tbody> 
                    {%- endif -%}
                {%- endfor -%}
                </tbody>
            </table>
        </div>
    </div>
<!-- fin details items -->
<!-- debut sign client + total + acompte -->
    <div class="row" style="margin-top: 0px; page-break-inside:avoid;">
        <div class="col-xs-7 text-left">L'offre est valable jusqu'au <ins>{{doc.get_formatted("valid_till")}}</ins></div>
        <div class="col-xs-5">
            <div class="row" style="border: 1px solid #d1d8dd; margin-right: auto;">
                <div class="col-xs-12">
                    <div class="row" style="margin-top:10px; margin-left:5px;">
                        <div class="col-xs-6 text-left"><label>{{ _("Total") }}</label></div>
                        <div class="col-xs-6 text-right">{{ doc.get_formatted("total") or '' }}</div>
                    </div> 
                    {%- if (doc.discount_amount and doc.apply_discount_on == "Net Total") -%} 
                        <div class="row" style="margin-left:5px;"> 
                            {%- if (doc.additional_discount_percentage) -%} <div class="col-xs-6 text-left"><label>{{ _("Discount") }} ({{ doc.get_formatted("additional_discount_percentage") }}%)</label></div> 
                            {%- else -%} <div class="col-xs-6 text-left"><label>{{ _("Discount") }}</label></div>{%- endif -%} 
                            <div class="col-xs-6 text-right">- {{ doc.get_formatted("discount_amount") }}</div>
                        </div>
                        <div class="row" style="margin-left:5px;">
                            <div class="col-xs-6 text-left"><label>{{ _("Net Total") }}</label></div>
                            <div class="col-xs-6 text-right">{{ doc.get_formatted("net_total") or '' }}</div>
                        </div>
                    {%- endif -%} 
                    {%- for row in doc.taxes -%} 
                        {%- if row.tax_amount_after_discount_amount -%} 
                            <div class="row" style="margin-left:5px;">
                                <div class="col-xs-6 text-left"><label>{{ row.description }}</label></div>
                                <div class="col-xs-6 text-right">{{ row.get_formatted("tax_amount_after_discount_amount", doc) }}</div>
                            </div>
                        {%- endif -%}
                    {%- endfor -%} 
                    {%- if doc.taxes or doc.discount_amount -%} 
                        <div class="row" style="margin-left:5px;">
                            <div class="col-xs-6 text-left"><label>{{ _("Grand Total") }}</label></div>
                            <div class="col-xs-6 text-right"><strong>{{ doc.get_formatted("grand_total") }}</strong></div>
                        </div>
                    {% endif %}
                </div>
            </div>
            {% if doc.payment_schedule and doc.acompte %}
            <div class="row" style="margin-right: auto;">
                <div class="col-xs-12">
                    <div class="row" style="margin-top:0px;">
                        <div class="col-xs-6 text-left"><em>Acompte {{ doc.payment_schedule[0].invoice_portion or "" }}%</em></div>
                        <div class="col-xs-6 text-right"><em>{{ frappe.utils.fmt_money(doc.payment_schedule[0].payment_amount or '', currency=doc.currency) }}</em></div>
                    </div>
                </div>
            </div>
            {%- endif -%}
        </div>
    </div>
    <div class="row" style="margin-top: 10px;">
        <div class="col-xs-12 text-left">
            <div class="row">
                <div class="col-xs-12 text-justify"> <b>Le client, par l'acceptation du devis, reconnaît avoir pris connaissance des conditions générales de ventes.</b></div>
            </div>
            <div class="row" style="margin-top: 10px;">
                <div class="col-xs-6">
                    <div class="col-xs-2 text-left">le</div>
                    <div class="col-xs-3 text-right">/</div>
                    <div class="col-xs-3 text-right">/</div>
                </div>
            </div>
            <div class="row text-left"><!--</div> style="margin-top: 10px;"-->
                <div class="col-xs-12 text-left"> Signature du client précédé de la mention manuscrite:<br> "Bon pour Accord et exécution" </div>
            </div>
        </div>
    </div>
<!-- fin sign client + total + acompte -->
<!-- debut conditions generales de vente -->
    {% if doc.terms != '' %}
        <div class="row page-break"></div>
        <div class="row">
            <div class="col-xs-12 text-center">
                <h1 style="font-size: 16px;">Conditions générales de Vente de St Jo Energies</h1>
            </div>
        </div>
        <div id="terms" class="row">
            <div class="col-xs-12 text-justify"> {{ doc.terms }} </div>
        </div>
    {% endif %}
<!-- fin conditions generales de vente -->
</div>
<div id="footer-html">
    <div class="print-footer">
        <div class="text-right small page-number"> {{ _("Page {0} of {1}").format('<span class="page"></span>', '<span class="topage"></span>') }} </div>
        {% if not no_letterhead and footer %} 
            <div> {{ footer }} </div>
        {% endif %}
    </div>
</div>

Bonjour @oryxr,

C’est un problème courant lié à la librairie de génération de PDF Wkhtmltopdf.
Il existe diverses solutions, notamment via du css, pour Ă©viter cela.
Voici une conversation sur ce sujet:

Le plus simple est d’essayer d’empêcher le logiciel de découper la ligne avec un css comme celui-ci:

tr {
     page-break-inside: avoid;
}

Vous pouvez ajouter du css dans le style d’impression (applicable à tous les formats d’impressions) ou dans chaque format d’impression.

Bonne soirée

Très bien, je m’en doutais.
Par contre est il possible et comment faire pour ajouter un saut de page au milieu d’un article.
Pour l’instant entre 2 articles j’utilise la case saut de ligne en bas de l’article.
Et j’introduit ce code dans le template jinja:

<div class="row">
        <div class="col-xs-12">
            <table id="list_articles" class="table table-condensed table-bordered">
                <thead>
                    <tr>
                        <th>{{ _("Description") }}</th>
                        <th class="text-right">{{ _("Rate") }}</th>
                        <th>{{ _("Quantity") }}</th>
                        <th class="text-right">{{ _("Amount") }} HT</th>
                    </tr>
                </thead>
                <tbody> 
                {%- for row in doc.items -%} 
                    <tr class="keep-together">
                        <td style="width: 60%;">
                            <h1 id="item_name">{{ row.item_name or '' }}</h1>
                            <div style="padding-left:20px">{{ row.description }}</div>
                        </td>
                        <td style="width: 15%; text-align: right;">{{ row.get_formatted("rate", doc) or ''}}</td>
                        <td style="width: 10%; text-align: left;">{{ row.get_formatted("qty") }} {{ row.uom }}</td>
                        <td style="width: 15%; text-align: right;">{{ row.get_formatted("amount", doc) or ''}}</td>
                    </tr> 
                    {%- if row.page_break -%} 
                </tbody>
            </table>
        </div>
    </div>
    <div class="page-break"></div>
    <div class="row">
        <div class="col-xs-12">
            <table id="list_articles" class="table table-condensed table-bordered">
                <thead>
                    <tr>
                        <th>{{ _("Description") }}</th>
                        <th class="text-right">{{ _("Rate") }}</th>
                        <th>{{ _("Quantity") }}</th>
                        <th class="text-right">{{ _("Amount") }} HT</th>
                    </tr>
                </thead>
                <tbody> 
                    {%- endif -%}
                {%- endfor -%}
                </tbody>
            </table>
        </div>
    </div>

Peut-on faire la même chose avec un saut au milieu de la description de l’article et comment le déclarer ?

Oui c’est possible, voici un exemple de format qui saute une page toutes les 30 lignes:

La clé est d’introduire une balise du type <div style="page-break-before: always;"></div> à l’endroit du saut de page.

Bonne journée

Merci, sinon je travail avec l’éditeur de texte, donc je ne peux pas lui injecter du html
J’ai vu que je pouvais modifier le texte éditeur :Text Editor | Frappe UI.
comment le faire concrètement ?

Merci d’avance
Pierre

Bonjour @oryxr,

Il n’est pas possible de modifier l’éditeur de texte standard de Dokos (sauf à se lancer dans le développement d’une application).
Frappe UI est une librairie permettant de créer des applications en VueJS sur le framework Frappe/Dodock mais n’est pas compatible avec les composants d’interface utilisateur existants.
Voici des exemples d’applications créées avec Frappe UI. Certaines peuvent s’installer en plus de Dokos sur votre site:

C’est un nouvel écosystème d’applications, encore en développements, donc à utiliser avec précautions pour le moment.

Par contre l’éditeur de texte de Dokos génère bien du HTML, donc il est possible d’utiliser la technique présentée dans le tutoriel ci-dessus (GitHub - rtdany10/erpnext_print_format: ERPNext Print Format guide.) pour découper le contenu de chaque page en fonction du nombre de lignes de texte.