tobybatch / kimai2

Docker containers for the kimai2 web application including docker-compose and kubernetes/helm deployment.
MIT License
183 stars 97 forks source link

Question about custom invoice template on 2.0.10 #500

Closed klangborste closed 1 year ago

klangborste commented 1 year ago

Hello together,

I updated my Kimai from "1.30.11-prod" to "2.0.10-prod" running on docker-compose. Before the update ("1.30.11-prod" ) a custom default twig template was used and it worked well, after the update it seems that either I run into a fallback to the new default twig template or the location where to store the custom templates changed.

docker-compose.yml:

# Kimai - Time Tracking
  kimai:
    container_name: kimai
    image: kimai/kimai2:apache-latest
    restart: always
    networks:
      - kimai_net
    volumes:
      - $DOCKERDIR/kimai/templates:/opt/kimai/var/invoices
    ports:
      - "8001:8001"
    depends_on:
        - kimai-mariadb
    environment:
      - DATABASE_URL=$DB_URL
      - TRUSTED_HOSTS=$TRUSTED_HOSTS

  kimai-mariadb:
    container_name: kimai-mariadb
    image: mariadb:latest
    restart: always
    networks:
      - kimai_net
    security_opt:
      - no-new-privileges:true
    volumes:
      - $DOCKERDIR/kimai/mariadb:/var/lib/mysql
    secrets:
      - db_kimai_database
      - db_kimai_user
      - db_kimai_password
      - db_kimai_rootpass
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
      - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_kimai_rootpass
      - MYSQL_USER_FILE=/run/secrets/db_kimai_user
      - MYSQL_PASSWORD_FILE=/run/secrets/db_kimai_password
      - MYSQL_DATABASE_FILE=/run/secrets/db_kimai_database

Tried to explain my issue in another bug report, but was properly misunderstood: https://github.com/tobybatch/kimai2/issues/482

Is there in the new 2.0.x version of Kimai something changed for the custom templates what I have to keep in my mind? The documentation didn't help with that either: https://www.kimai.org/documentation/invoices.html#create-your-own-invoice-document. I tried also to reverse engineering a little bit and did some try & error to minimize the error vector, but at the moment I got a little bit lost. Is there somebody can give me a hint or push me in the right direction? Thanks in advance

kevinpapst commented 1 year ago

but was properly misunderstood

No, you wrote you are mapping - $DOCKERDIR/kimai/templates:/opt/kimai/templates/invoice/renderer/ which clearly ignores the docs that say:

Bildschirm­foto 2023-04-01 um 19 42 29

Now you are mapping - $DOCKERDIR/kimai/templates:/opt/kimai/var/invoices which is the correct location.

You are still not giving the full picture. You do not state the name of your template, you do not share a screenshot of your invoice template.

klangborste commented 1 year ago

Hey,

yes Kevin that is true, I did not gave the full picture. I used before my update of Kimai to the newer 2.0.x version all the time in my "docker-compose.yml" the volume mapping to the invoices with:

    volumes:
      - $DOCKERDIR/kimai/templates:/opt/kimai/var/invoices

I realized after my update to a 2.x.x version that my custom twig invoice template did not work anymore. So I tried some things and one of them was to use another path that is used by Kimai by default, that was just an attempt - sorry for the confusion then.

One hint from my side: The documentation says something about don't place new custom documents under "templates/invoice/renderer/", because it will be overwritten with the next update. A docker release always removes the old container and places a complete new one, so this part describe in the documentation doesn't matter for a docker deployment, because it places the new files anyways again there after every container restart.

I just edited a little bit the default invoice template and did overwrite the original one in Kimai 1.x.x with my custom default template. I called it on the older version where it worked: default.pdf.twig

It's the default invoice template and I just changed to see the time range in the first column for the invoice:

image

That is the twig I edited from the original default invoice one from 1.x.x:

<!DOCTYPE html>
{% set fallback = app.request is not null ? app.request.locale : 'en' %}
{% set language = model.template.language|default(fallback) %}
{% set isDecimal = model.template.decimalDuration|default(false) %}
{% set currency = model.currency %}
<html lang="{{ language }}">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}{{ model.invoiceNumber }}-{{ model.customer.company|default(model.customer.name)|u.snake }}{% endblock %}</title>
    <style type="text/css">
        {{ encore_entry_css_source('invoice-pdf')|raw }}
    </style>
</head>
<body>
<!--mpdf
<htmlpageheader name="header">
    <table class="header">
        <tr>
            <td class="title">
                {{ model.template.title }}
            </td>
            <td class="date text-right">
                {{ 'label.date'|trans }}: {{ model.invoiceDate|date_short }}
            </td>
        </tr>
    </table>
</htmlpageheader>
<sethtmlpageheader name="header" page="ALL" value="on" show-this-page="1" />
<htmlpagefooter name="footer">
    <table class="footer">
        <tr>
            <td>
                <strong>{{ 'label.contact'|trans }}</strong>:
                {{ model.template.contact|nl2str(' &ndash; ') }}
                <br>
                <strong>{{ 'label.invoice_bank_account'|trans }}</strong>:
                {{ model.template.paymentDetails|nl2str(' &ndash; ') }}
            </td>
            <td align="right">
                {{ 'export.page_of'|trans({'%page%': '{PAGENO}', '%pages%': '{nb}'}) }}
            </td>
        </tr>
    </table>
</htmlpagefooter>
<sethtmlpagefooter page="ALL" value="on" name="footer" show-this-page="1" />
mpdf-->
<div class="wrapper">
    <table class="addresses">
        <tr>
            <td style="width:60%">
                {{ 'invoice.to'|trans }}
                <br>
                <strong>{{ model.customer.company|default(model.customer.name) }}</strong>
                <br>
                {{ model.customer.address|nl2br }}
                {% if model.customer.vatId is not empty %}
                    <br>
                    {{ 'label.vat_id'|trans }}: {{ model.customer.vatId }}
                {% endif %}
            </td>
            <td>
                {{ 'invoice.from'|trans }}
                <br>
                <strong>{{ model.template.company }}</strong>
                <br>
                {{ model.template.address|trim|nl2br }}
                {% if model.template.vatId is not empty %}
                    <br>
                    {{ 'label.vat_id'|trans }}:
                    {{ model.template.vatId }}
                {% endif %}
            </td>
        </tr>
    </table>

    <p>
        {{ 'invoice.number'|trans }}:
        {{ model.invoiceNumber }}

        <br>
        {{ 'invoice.due_days'|trans }}:
        {{ model.dueDate|date_short }}

        {% if model.customer.number is not empty %}
            <br>
            {{ 'label.number'|trans }}:
            {{ model.customer.number }}
        {% endif %}

        {% if model.query.project is not empty and model.query.project.orderNumber is not empty %}
            <br>
            {{ 'label.orderNumber'|trans }}:
            {{ model.query.project.orderNumber }}
        {% endif %}
    </p>

    <table class="items">
        <thead>
            <tr>
                <th class="first"><strong>{{ 'label.daterange'|trans }}</strong></th>
                <th><strong>{{ 'label.description'|trans }}</strong></th>
                <th class="text-right"><strong>{{ 'label.unit_price'|trans }}</strong></th>
                <th class="text-right"><strong>{{ 'label.amount'|trans }}</strong></th>
                <th class="last text-right"><strong>{{ 'label.total_rate'|trans }}</strong></th>
            </tr>
        </thead>
        <tbody>
        {% for id, entry in model.calculator.entries %}
            <!-- CONTENT_PART -->
            {% set duration = entry.duration|duration(isDecimal) %}
            {% if entry.fixedRate %}
                {% set rate = entry.fixedRate %}
                {% set duration = entry.amount|amount %}
            {% else %}
                {% set rate = entry.hourlyRate %}
            {% endif %}
            <tr{# class="{{ cycle(['odd', 'even'], id) }}"#}>
                <td nowrap class="first text-nowrap">
                    {% set begin = model.query.begin|date('d.m.Y') %}
                    {% set end = model.query.end|date('d.m.Y') %}
                    {% set range = begin ~ ' - ' ~ end %}
                    {{ range }}
                </td>
                <td contenteditable="true">
                {{ entry.project.name }}
                </td>
                <td nowrap class="text-nowrap text-right">{{ rate|money(currency) }}</td>
                <td nowrap class="text-nowrap text-right">{{ duration }}</td>
                <td nowrap class="last text-nowrap text-right">{{ entry.rate|money(currency) }}</td>
            </tr>
        {% endfor %}
        </tbody>
        <tfoot>
            {% if model.calculator.tax > 0 or not model.isHideZeroTax() %}
            <tr>
                <td colspan="4" class="text-right">
                    {{ 'invoice.subtotal'|trans }}
                </td>
                <td class="last text-right">{{ model.calculator.subtotal|money(currency) }}</td>
            </tr>
            <tr>
                <td colspan="4" class="text-right">
                    {{ 'invoice.tax'|trans }} ({{ model.calculator.vat }}%)
                </td>
                <td class="last text-right">{{ model.calculator.tax|money(currency) }}</td>
            </tr>
            {% endif %}
            <tr>
                <td colspan="4" class="text-right text-nowrap">
                    <strong>{{ 'invoice.total'|trans }}</strong>
                </td>
                <td class="last text-right">
                    <strong>{{ model.calculator.total|money(currency) }}</strong>
                </td>
            </tr>
        </tfoot>
    </table>

    {% if model.template.paymentTerms is not empty %}
    <div class="paymentTerms">
        {{ model.template.paymentTerms|md2html }}
    </div>
    {% endif %}
</div>
</body>
</html>
kevinpapst commented 1 year ago

You do not state the name of your template, you do not share a screenshot of your invoice template.

???

See https://github.com/kimai/kimai/tree/main/templates/invoice/renderer

You can use every document name only once: so having kimai.html.twig and kimai.docx will lead to unpredictable results (the first file to be found takes precedence)

Do NOT change the default templates, but copy the file and save it (with a new filename) at var/invoices/

Enjoy Kimai! I am unfortunately out of ideas, because I can't really help if questions and documentation is ignored.

klangborste commented 1 year ago

Thanks, I enjoy it and already donated. Did you really read my whole message? I did provide the name and a screenshot from the part I changed... The name from the template I used is "default.pdf.twig" as already said in my last comment. And the screenshot is the things i dropped there in my message it's only this part that changed (everything else is the default from Kimai original invoice template):

image

Only the red marked area was changed for my Kimai 1.x.x

I copied the original template to "var/invoices/" what you already saw in my initial message from this issue here. I mapped it to:

    volumes:
      - $DOCKERDIR/kimai/templates:/opt/kimai/var/invoices

So this should be the correct path if you pin me to the documentation...

And again: I understand what the documentation says, but that is not the problem as described several times now. I did already had an working custom twig invoice template integrated with the docker container and mapped it successfully with the old version "1.30.11-prod" and it stopped working with "2.0.10-prod". Only the version changed, I did not chang anything then the docker version then it stopped working and everything else I did was attempt to fix it, OK?

kevinpapst commented 1 year ago

You still ignore the documentation. What can I do? The first issue you opened clearly violated the path rule, this issue still violates the filename rule.

kevinpapst commented 1 year ago

BTW and for everyone reading this: that is the screenshot of an invoice template:

Bildschirm­foto 2023-04-01 um 22 42 50

If you are reading something else than your templates filename in the "Invoice document" dropdown, you have chosen to name your file like one of the built-in templates, which can lead to problems => that's why the docs clearly say "rename the file".

The link I posted above https://github.com/kimai/kimai/tree/main/templates/invoice/renderer shows all built-in filenames.