Looking for a german invoice template

Hi there,

I just installed InvoiceNinja on my homeserver and want to use it for writing and sending invoices (In german: Freiberufler)

Sadly enough, there is no template which matches our needs in Germany included in the self-hosted package. Tried also a web search, without luck.

I´ve seen some people on the forum buildung such a template for use in Germany …
is anybody willing to share his/her template?

I am lacking the skills to do this on my own.
(got only basic html knowledge)

using: v5.5.99-C110, Proxmox, CT Container

2 Likes

Hey,

Unfortunately, the templates in Invoice Ninja are still unusable for Germany.

Have you found a solution?

Best regards,
Benjamin

How to Customize Labels in Invoice Ninja

You have two methods to translate/customize labels:

Method 1: System-Wide Language Setting (Easiest)

  1. Go to SettingsLocalization
  2. Set Language to “Deutsch” (German)
  3. Invoice Ninja has built-in German translations for most standard labels

This automatically translates:

  • Invoice → Rechnung
  • From → Von
  • To → An
  • Date → Datum
  • Amount Due → Fälliger Betrag
  • etc.

Method 2: Custom Labels (For Specific Overrides)

In Settings → Localization → Custom Labels, you can manually override any label text used in your documents Invoice Ninja.

How it works:

  1. Go to SettingsLocalizationCustom Labels
  2. Click “Add Custom” button
  3. You need to enter the key name (not the visible text)
  4. Click “Labels” to see the complete dictionary of available keys
  5. Example keys:
  • invoice_date → Change to “Rechnungsdatum”
  • balance_due → Change to “Fälliger Betrag”
  • from_label → Change to “Von”
  • to_label → Change to “An”

Example:

  • Find the key: invoice_number
  • Add it to custom labels
  • It will show as “Invoice Number” in the list
  • Change the value to “Rechnungsnummer”

Method 3: Custom Fields (For Additional German-Specific Fields)

Go to SettingsInvoice SettingsCustom Fields to add:

  • Tax Number (Steuernummer)
  • VAT ID (USt-IdNr)
  • Legal disclaimers for Kleinunternehmer status
  • Any other German-specific requirements

What You Need to Do

  1. Set system language to German (easiest baseline)
  2. Add custom fields for German legal requirements (Tax Number, VAT ID)
  3. Use Custom Labels to override any translations that aren’t perfect
  4. The HTML template stays as-is - using those $variables pull from your language/label settings
1 Like

@SmokinB

I’d be happy to provide you with my template here, DIN 5008-B

Here is the export

{"header":"<div class=\"repeating-header\" id=\"header\"></div>","body":"<div id=\"body\">\n   <div class=\"company-logo-wrapper\">\n      <img class=\"company-logo\" src=\"$company.logo\" alt=\"$company.name logo\">\n      <div style=\"float:right;\">\n         <div id=\"company-address\"></div>\n                  <p>&nbsp;</p>\n                  <p>Telefon: $company.phone</p>\n                  <p>&nbsp;</p>\n                  <p>E-Mail: $company.email</p>\n                  <p>$company.website</p>\n      </div>\n   </div>\n   <div class=\"client-entity-wrapper\">\n      <div class=\"wrapper-left-side\">\n         <div class=\"text-with-client\">\n            <div id=\"vermerkzone\">$company.name - $company.address1 - $company.postal_code $company.city</div>\n            <div id=\"anschriftzone\">\n               <div id=\"client-details\"></div>\n                 <!--\n                 <p>$client.name</p>\n                 <p>$contact.titel $contact.full_name</p>\n                 <p>$address1</p>\n                 <p>$postal_city_state</p>\n                 -->\n               </div>\n            </div>\n         </div>\n         <div class=\"wrapper-right-side\">\n            <table id=\"entity-details\" cellspacing=\"0\" dir=\"$dir\">\n               <tr><td>Kundennummer</td><td>$client.number</td></tr>\n            </table>\n            <div data-state=\"encoded-html\" class=\"sepa_qr\">$sepa_qr_code</div>\n         </div>\n      </div>\n   </div>\n   <p class=\"entity-label\">$entity_label</p>\n   <table id=\"product-table\" cellspacing=\"0\" data-ref=\"table\"></table>\n   <table id=\"task-table\" cellspacing=\"0\" data-ref=\"table\"></table>\n   <table id=\"delivery-note-table\" cellspacing=\"0\" data-ref=\"table\"></table>\n   <table id=\"statement-invoice-table\" cellspacing=\"0\" data-ref=\"table\"></table>\n   <div id=\"statement-invoice-table-totals\" data-ref=\"statement-totals\"></div>\n   <table id=\"statement-payment-table\" cellspacing=\"0\" data-ref=\"table\"></table>\n   <div id=\"statement-payment-table-totals\" data-ref=\"statement-totals\"></div>\n   <table id=\"statement-aging-table\" cellspacing=\"0\" data-ref=\"table\"></table>\n   <div id=\"statement-aging-table-totals\" data-ref=\"statement-totals\"></div>\n   <div id=\"table-totals\" cellspacing=\"0\"></div>\n   <div class=\"entity-footer\" data-ref=\"total_table-footer\">$entity_footer</div>\n   <div class=\"repeating-footer-space\">&nbsp;</div>\n</div>","footer":"<div class=\"repeating-footer\" id=\"footer\">\n   <p>Bankverbindung: $company.custom3</p>\n   <p>IBAN $company.custom1 · BIC $company.custom2</p>\n   <p>USt-Id: $company.vat_number</p>\n        <script>\n            // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present.\n            document.addEventListener('DOMContentLoaded', () => {\n                let tables = [\n                    'product-table', 'task-table', 'delivery-note-table',\n                    'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals',\n                    'statement-invoice-table-totals', 'statement-payment-table-totals', 'statement-aging-table',\n                    'client-details','vendor-details'\n                ];\n\n                tables.forEach((tableIdentifier) => {\n                    console.log(document.getElementById(tableIdentifier));\n\n                    document.getElementById(tableIdentifier)?.childElementCount === 0\n                        ? document.getElementById(tableIdentifier).style.setProperty('display', 'none', 'important')\n                        : '';\n                });\n            });\n            const taxCells = document.querySelectorAll('[data-ref*=\"tax\"][data-ref*=\"-td\"]');\n                taxCells.forEach(cell => {\n                    if (cell.textContent.trim() === '0%') {\n                        cell.style.display = 'none';\n                    }\n                });\n        </script>\n</div>","product":"","task":"","includes":"<style id=\"style\">\n    @font-face {\n   font-family: 'Roboto';\n   font-style: normal;\n   font-weight: 400;\n   src: url('./fonts/Roboto-Regular.woff2') format('woff2'),\n        url('./fonts/Roboto-Regular.woff') format('woff'),\n        url('./fonts/Roboto-Regular.ttf') format('truetype');\n}\n\n    :root {\n        --primary-color: $primary_color;\n        --secondary-color: $secondary_color;\n        --line-height: 1em;\n    }\n\n    html {\n        width: 210mm;\n        height: 297mm;     \n    }    \n\n    body {\n        -webkit-font-smoothing: antialiased;\n        -moz-osx-font-smoothing: grayscale;\n        font-family: $font_name, Helvetica, sans-serif;\n        font-size: $font_size !important;\n        zoom: 80%;\n    }\n\n    @page {\n        margin: $global_margin;\n        size: $page_size $page_layout;\n    }\n    p {\n        margin: 0;\n        padding: 0;\n    }\n\n    .company-logo {\n        max-width: 55%;\n        max-height: 45mm;\n    }\n.column {\n  float: left;\n  width: 25%;\n}\n\n/* Clear floats after the columns */\n.row:after {\n  content: \"\";\n  display: table;\n  clear: both;\n}\n    .company-logo-wrapper {\n        height: 45mm;\n    }\n\n    .client-entity-wrapper {\n        height: 45mm;\n        display: flex;\n        margin-top: 1rem;\n        margin-left: 20mm;\n        line-height: var(--line-height);\n    }\n   #vermerkzone {\n    font-size: 7pt;\n    height: 17.7mm;\n    vertical-align: bottom;\n    display: table-cell;\n    border-bottom:1px solid;\n    }\n    #anschriftzone { margin-top: 3mm;  }\n    #anschriftzone p {font-size: 14pt; line-height: var(--line-height); }\n    #entity-details { margin-right: 20px; }\n\n   .entity-label { margin-top:8.46mm; margin-left: 20mm; font-size: 20pt; font-weight:bold; color: $primary_color;}\n\n    .client-entity-wrapper .wrapper-info-text {\n        display: block;\n        font-size: 1.5rem;\n        font-weight: normal;\n    }\n\n    .client-entity-wrapper .wrapper-left-side {\n        display: grid;\n        grid-template-columns: 1fr 1fr;\n    }\n\n     .text-with-client  { width: 100mm; }\n\n\n    .client-entity-wrapper .wrapper-left-side #client-details,\n    .client-entity-wrapper .wrapper-left-side #company-details,\n    .client-entity-wrapper .wrapper-left-side #company-address {\n        display: flex;\n        flex-direction: column;\n    }\n\n    .client-entity-wrapper .wrapper-right-side,\n    .client-entity-wrapper .wrapper-right-side,\n    .client-entity-wrapper .wrapper-right-side {\n        display: flex;\n        flex-direction: column;\n        min-width: 40%;\n    }\n\n    .client-entity-wrapper .wrapper-left-side .company-info {\n        padding-left: 1rem;\n    }\n\n    .client-entity-wrapper #entity-details {\n        text-align: left;\n        margin-top: 0.5rem;\n        min-width: 100%;\n        font-weight: bold;\n    }\n  \n  .sepa_qr { \n    align-self: end;\n    margin-top: .25em;\n    }\n  .sepa_qr svg {\n    width: 150;\n    height: 150;\n  }\n  .sepa_qr svg rect {\n    fill: #ffffff;\n  }\n    [data-ref=\"table\"] {\n        margin-top: 1rem;\n        margin-bottom: .5rem;\n        margin-left: 16mm;\n        padding-right: 10mm;\n        min-width: 100%;\n        table-layout: ;\n        overflow-wrap: break-word;\n    }\n\n    .task-time-details {\n        display: block;\n        margin-top: 5px;\n        color: grey;\n    }\n\n    [data-ref=\"table\"] > thead {\n        text-align: left;\n    }\n\n    [data-ref=\"table\"] > thead > tr > th {\n        font-size: 1.2rem;\n        padding: 1rem;\n        background: var(--primary-color);\n        color: white;\n        font-weight: bold;\n    }\n\n    [data-ref=\"table\"] > thead tr > th:first-child {\n        border-top-left-radius: 5px;\n        border-bottom-left-radius: 5px;\n    }\n    [data-ref=\"table\"] > thead > tr > th:last-child {\n        border-top-right-radius: 5px;\n        border-bottom-right-radius: 5px;\n        text-align: right;\n    }\n\n    [data-ref=\"table\"] > tbody > tr {\n        vertical-align: top;\n    }\n\n    [data-ref=\"table\"] > tbody > tr > td {\n        /**border-bottom: 1px solid var(--primary-color);**/\n        padding: 1rem;\n        page-break-inside: avoid;\n    }\n\n    [data-ref=\"table\"] > tbody > tr:first-child > td {\n        padding: 1rem;\n    }\n\n    [data-ref=\"table\"] > tbody > tr > td:last-child {\n        text-align: right;\n    }\n\n    #table-totals {\n        margin-top: 0.5rem;\n        display: grid;\n        grid-template-columns: 2fr 1fr;\n        padding-top: 0.5rem;\n        padding-left: 1rem;\n        margin-right: -.5rem;\n        margin-left: 20mm;\n        gap: 80px;\n        page-break-inside:auto;\n        overflow: visible !important;\n    }\n\n    #table-totals .totals-table-right-side>* {\n        display: grid;\n        grid-template-columns: 1fr 1fr;\n    }\n\n    #table-totals>.totals-table-right-side>*> :nth-child(1) {\n        text-align: $dir_text_align;\n        margin-top: .75rem;\n    }\n\n    #table-totals>.totals-table-right-side> * > :not([hidden]) ~ :not([hidden]) {\n        --tw-space-y-reverse: 0;\n        margin-top: calc(.75rem * calc(1 - var(--tw-space-y-reverse)));\n        margin-bottom: calc(.75rem * var(--tw-space-y-reverse));\n    }\n\n    #table-totals>.totals-table-right-side>*> :nth-child(2) {\n        text-align: right;\n    }\n\n    #table-totals\n    > *\n    [data-element='product-table-balance-due-label'],\n    #table-totals\n    > *\n    [data-element='product-table-balance-due'] {\n        font-weight: bold;\n    }\n\n    #table-totals\n    > *\n    [data-element='product-table-balance-due'] {\n        color: var(--primary-color);\n    }\n\n    #table-totals > * > :last-child {\n        text-align: right;\n        padding-right: 0.5rem;\n    }\n\n    [data-ref=\"total_table-footer\"] {\n        padding-left: 1rem\n        page-break-before: avoid;\n    }\n\n    .entity-footer {\n        margin-left: 20mm;\n        width: 100%;\n    }\n\n    #footer {\n          bottom: 8mm;\n          width: 100%;\n          font-size: 10pt;\n          text-align: center;\n    }\n\n    /** Markdown-specific styles. **/\n    [data-ref=\"table\"] h3 {\n        font-size: 1rem;\n        margin-bottom: 0;\n    }\n\n    [data-ref=\"statement-totals\"] {\n        margin-top: 1rem;\n        text-align: right;\n        margin-right: .75rem;\n    }\n\n    [data-ref*=\".line_total-td\"] {\n        white-space: nowrap;\n    }\n\n    .repeating-header,\n    .repeating-header-space,\n    .repeating-footer,\n    .repeating-footer-space {\n        height: 1rem;\n    }\n    .repeating-header {\n        position: fixed;\n        top: 0;\n    }\n    .repeating-footer {\n        position: fixed;\n        bottom: 5;\n    }\n    [data-element='.description-td'], td {\n        min-width:100%;\n        max-width: 600px;\n        overflow-wrap: break-word; \n\n    }\n   #terms{\n        margin-left: 20mm;\n        text-align: left;\n   }\n    \n    /** Useful snippets, uncomment to enable. **/\n\n    /** Hide company logo **/\n    /* .company-logo { display: none } */\n\n    /* Hide company details */\n    /* #company-details > * { display: none } */\n\n    /* Hide company address */\n    /* #company-address > * { display: none } */\n\n    /* Hide public notes */\n    /* [data-ref=\"total_table-public_notes\"] { display: none } */\n\n    /* Hide terms label */\n     [data-ref=\"total_table-terms-label\"] { display: none } \n     /*[data-ref=\"total_table-terms\"]  { display: none } */\n\n\n    /* Hide totals table */\n    /* #table-totals { display: none } */\n\n    /* Hide totals table left side */\n    /* #table-totals div:first-child > * { display: none !important }*/\n\n    /* Hide totals table right side */\n   /*.totals-table-right-side { display: none }*/\n\n    /** For more info, please check our docs: https://invoiceninja.github.io **/\n    /** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/\n</style>"}

Here is the style.css

<style id="style">
    @font-face {
   font-family: 'Roboto';
   font-style: normal;
   font-weight: 400;
   src: url('./fonts/Roboto-Regular.woff2') format('woff2'),
        url('./fonts/Roboto-Regular.woff') format('woff'),
        url('./fonts/Roboto-Regular.ttf') format('truetype');
}

    :root {
        --primary-color: $primary_color;
        --secondary-color: $secondary_color;
        --line-height: 1em;
    }

    html {
        width: 210mm;
        height: 297mm;     
    }    

    body {
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        font-family: $font_name, Helvetica, sans-serif;
        font-size: $font_size !important;
        zoom: 80%;
    }

    @page {
        margin: $global_margin;
        size: $page_size $page_layout;
    }
    p {
        margin: 0;
        padding: 0;
    }

    .company-logo {
        max-width: 55%;
        max-height: 45mm;
    }
.column {
  float: left;
  width: 25%;
}

/* Clear floats after the columns */
.row:after {
  content: "";
  display: table;
  clear: both;
}
    .company-logo-wrapper {
        height: 45mm;
    }

    .client-entity-wrapper {
        height: 45mm;
        display: flex;
        margin-top: 1rem;
        margin-left: 20mm;
        line-height: var(--line-height);
    }
   #vermerkzone {
    font-size: 7pt;
    height: 17.7mm;
    vertical-align: bottom;
    display: table-cell;
    border-bottom:1px solid;
    }
    #anschriftzone { margin-top: 3mm;  }
    #anschriftzone p {font-size: 14pt; line-height: var(--line-height); }
    #entity-details { margin-right: 20px; }

   .entity-label { margin-top:8.46mm; margin-left: 20mm; font-size: 20pt; font-weight:bold; color: $primary_color;}

    .client-entity-wrapper .wrapper-info-text {
        display: block;
        font-size: 1.5rem;
        font-weight: normal;
    }

    .client-entity-wrapper .wrapper-left-side {
        display: grid;
        grid-template-columns: 1fr 1fr;
    }

     .text-with-client  { width: 100mm; }


    .client-entity-wrapper .wrapper-left-side #client-details,
    .client-entity-wrapper .wrapper-left-side #company-details,
    .client-entity-wrapper .wrapper-left-side #company-address {
        display: flex;
        flex-direction: column;
    }

    .client-entity-wrapper .wrapper-right-side,
    .client-entity-wrapper .wrapper-right-side,
    .client-entity-wrapper .wrapper-right-side {
        display: flex;
        flex-direction: column;
        min-width: 40%;
    }

    .client-entity-wrapper .wrapper-left-side .company-info {
        padding-left: 1rem;
    }

    .client-entity-wrapper #entity-details {
        text-align: left;
        margin-top: 0.5rem;
        min-width: 100%;
        font-weight: bold;
    }
  
  .sepa_qr { 
    align-self: end;
    margin-top: .25em;
    }
  .sepa_qr svg {
    width: 150;
    height: 150;
  }
  .sepa_qr svg rect {
    fill: #ffffff;
  }
    [data-ref="table"] {
        margin-top: 1rem;
        margin-bottom: .5rem;
        margin-left: 16mm;
        padding-right: 10mm;
        min-width: 100%;
        table-layout: ;
        overflow-wrap: break-word;
    }

    .task-time-details {
        display: block;
        margin-top: 5px;
        color: grey;
    }

    [data-ref="table"] > thead {
        text-align: left;
    }

    [data-ref="table"] > thead > tr > th {
        font-size: 1.2rem;
        padding: 1rem;
        background: var(--primary-color);
        color: white;
        font-weight: bold;
    }

    [data-ref="table"] > thead tr > th:first-child {
        border-top-left-radius: 5px;
        border-bottom-left-radius: 5px;
    }
    [data-ref="table"] > thead > tr > th:last-child {
        border-top-right-radius: 5px;
        border-bottom-right-radius: 5px;
        text-align: right;
    }

    [data-ref="table"] > tbody > tr {
        vertical-align: top;
    }

    [data-ref="table"] > tbody > tr > td {
        /**border-bottom: 1px solid var(--primary-color);**/
        padding: 1rem;
        page-break-inside: avoid;
    }

    [data-ref="table"] > tbody > tr:first-child > td {
        padding: 1rem;
    }

    [data-ref="table"] > tbody > tr > td:last-child {
        text-align: right;
    }

    #table-totals {
        margin-top: 0.5rem;
        display: grid;
        grid-template-columns: 2fr 1fr;
        padding-top: 0.5rem;
        padding-left: 1rem;
        margin-right: -.5rem;
        margin-left: 20mm;
        gap: 80px;
        page-break-inside:auto;
        overflow: visible !important;
    }

    #table-totals .totals-table-right-side>* {
        display: grid;
        grid-template-columns: 1fr 1fr;
    }

    #table-totals>.totals-table-right-side>*> :nth-child(1) {
        text-align: $dir_text_align;
        margin-top: .75rem;
    }

    #table-totals>.totals-table-right-side> * > :not([hidden]) ~ :not([hidden]) {
        --tw-space-y-reverse: 0;
        margin-top: calc(.75rem * calc(1 - var(--tw-space-y-reverse)));
        margin-bottom: calc(.75rem * var(--tw-space-y-reverse));
    }

    #table-totals>.totals-table-right-side>*> :nth-child(2) {
        text-align: right;
    }

    #table-totals
    > *
    [data-element='product-table-balance-due-label'],
    #table-totals
    > *
    [data-element='product-table-balance-due'] {
        font-weight: bold;
    }

    #table-totals
    > *
    [data-element='product-table-balance-due'] {
        color: var(--primary-color);
    }

    #table-totals > * > :last-child {
        text-align: right;
        padding-right: 0.5rem;
    }

    [data-ref="total_table-footer"] {
        padding-left: 1rem
        page-break-before: avoid;
    }

    .entity-footer {
        margin-left: 20mm;
        width: 100%;
    }

    #footer {
          bottom: 8mm;
          width: 100%;
          font-size: 10pt;
          text-align: center;
    }

    /** Markdown-specific styles. **/
    [data-ref="table"] h3 {
        font-size: 1rem;
        margin-bottom: 0;
    }

    [data-ref="statement-totals"] {
        margin-top: 1rem;
        text-align: right;
        margin-right: .75rem;
    }

    [data-ref*=".line_total-td"] {
        white-space: nowrap;
    }

    .repeating-header,
    .repeating-header-space,
    .repeating-footer,
    .repeating-footer-space {
        height: 1rem;
    }
    .repeating-header {
        position: fixed;
        top: 0;
    }
    .repeating-footer {
        position: fixed;
        bottom: 5;
    }
    [data-element='.description-td'], td {
        min-width:100%;
        max-width: 600px;
        overflow-wrap: break-word; 

    }
   #terms{
        margin-left: 20mm;
        text-align: left;
   }
    
    /** Useful snippets, uncomment to enable. **/

    /** Hide company logo **/
    /* .company-logo { display: none } */

    /* Hide company details */
    /* #company-details > * { display: none } */

    /* Hide company address */
    /* #company-address > * { display: none } */

    /* Hide public notes */
    /* [data-ref="total_table-public_notes"] { display: none } */

    /* Hide terms label */
     [data-ref="total_table-terms-label"] { display: none } 
     /*[data-ref="total_table-terms"]  { display: none } */


    /* Hide totals table */
    /* #table-totals { display: none } */

    /* Hide totals table left side */
    /* #table-totals div:first-child > * { display: none !important }*/

    /* Hide totals table right side */
   /*.totals-table-right-side { display: none }*/

    /** For more info, please check our docs: https://invoiceninja.github.io **/
    /** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/
</style>

I have the font installed locally—just so you know.

If you need a more customized solution, we’d be happy to discuss it.

1 Like

By the way, I have another question for @hillel

Isn’t there some way to pull the bank information—which we’ve already entered there for e-invoices—into the invoice templates as well? That would save us from having to use custom fields in the company :wink:

I’m not sure if it’s supported, feel free to ask in a discussion on GitHub.

In my last clean installation this was acutally the case. I have no separate fields for iban and bic.