Most of your store is yours to design. The invoice is not. It is a legally binding document, and the moment one of your customers hands it to their accountant — or a tax inspector pulls it during an audit — every missing field becomes your problem, not theirs. A German invoice without a delivery date, a French one missing the recovery-cost indemnity, a Polish one over 15,000 PLN without the split-payment note: each is a defect that can get a VAT deduction rejected or trigger a penalty. The catch is that PrestaShop generates invoices automatically from a fixed PDF template, and that template was built to be generically correct, not correct for your jurisdiction. This guide is about the part you actually control: what the law requires on the printed invoice in the major EU markets, and exactly how to edit PrestaShop's PDF template to add those fields without losing the work at the next update.

A quick scoping note, because this cluster has neighbours. This post is about the PDF document — its layout, its fields, its compliance. It is not about how invoices are numbered (that has its own depth — see invoice and order number customization), nor about structured e-invoicing formats like Italy's SDI or France's Factur-X (covered in e-invoicing in Europe), nor about configuring the VAT rates themselves (tax configuration in PrestaShop). Here we stay on the document.

What PrestaShop actually does when it makes an invoice

Understanding the machinery first saves you from editing the wrong file later. When an order reaches a status whose "Invoice" flag is enabled, PrestaShop creates an order_invoice database row and assigns the sequential number. Which statuses generate an invoice is not fixed — it depends entirely on that per-status "Invoice" flag, configured under Shop Parameters → Order Settings → Statuses, and the defaults vary by PrestaShop version and configuration. Don't assume a given status invoices by default: open your own status list and verify which ones have the "Invoice" flag set. The PDF itself is not stored — it is rendered on demand every time someone clicks download, by a chain of three pieces:

  • The PHP class classes/pdf/HTMLTemplateInvoice.php — gathers the order, address, tax and product data and exposes it to the template. This is where you go to add a field PrestaShop doesn't already pass through.
  • The Smarty template pdf/invoice.tpl — the actual layout and markup of the document. This is where you change what appears and where.
  • The style template pdf/invoice.style-tab.tpl — the CSS. TCPDF (the library that turns the HTML into a PDF) supports only a limited CSS subset, so expect tables-and-inline-styles, not flexbox.

So what? Two practical consequences fall out of this design. First, because the PDF is regenerated from live data, an invoice you issued last March will silently change if you later edit the template or the order data — which is exactly why archiving a frozen copy matters (more below). Second, the split between the PHP class and the .tpl tells you which file to touch: if the data already exists in the order, you only edit the template; if you need data PrestaShop never loads, you edit the class too.

Editing the template without getting overwritten on update

The single most common mistake is editing core files directly — the next PrestaShop update or the next time someone restores a clean install wipes your legally-required fields without warning. There are two safe routes, and they apply to different files.

The template (.tpl) files are overridden cleanly by copying them into your active theme. PrestaShop looks in the theme's pdf/ directory before falling back to the core pdf/ folder:

  • Copy pdf/invoice.tpl to themes/YOUR_THEME/pdf/invoice.tpl and edit the copy.
  • The same works for invoice.style-tab.tpl, order-slip.tpl (the credit-slip / credit-note template) and delivery-slip.tpl.
  • Nothing in core is touched, so updates leave your version alone — but you do need to re-check the copy after a major version jump, in case the core template gained fields you'd want.

The PHP class uses PrestaShop's override system instead. Create override/classes/pdf/HTMLTemplateInvoice.php extending the core class, then delete the compiled class map so PrestaShop rebuilds it:

  • On 1.7/8/9 the cache file is var/cache/prod/class_index.php (and var/cache/dev/class_index.php if you're in dev mode). On older 1.6 stores it is cache/class_index.php.
  • Override getContent() to assign extra Smarty variables, then read them in your theme's invoice.tpl.

Keep a copy of both files outside the shop. Theme updates and aggressive "reset theme" actions can remove the pdf/ override, and a third-party module that ships its own override can collide with yours — so treat your customised templates as source you version-control, not as live edits you'll never see again.

Below is what must appear on the printed invoice in the markets most PrestaShop merchants sell into. This is the layout-and-fields view — the VAT-rate mechanics behind these numbers live in VAT in the EU: OSS, IOSS, and whether you show net or gross is its own rule set in price display rules in Europe. Always have a local accountant confirm; legislation shifts.

MarketOn-invoice fields PrestaShop's default often missesLegal basis
France (Facture)SIREN/SIRET, RCS registration city, legal form & share capital, late-payment penalty rate, and the fixed 40 EUR recovery-cost indemnity for B2BCode de commerce, Art. L441-9
Germany (Rechnung)Delivery / service date (distinct from the invoice date — most-missed field of all), Steuernummer or USt-IdNr, and the Kleinunternehmer note (§19 UStG) when VAT-exemptUStG §14 / §14a
Italy (Fattura)Codice Fiscale / Partita IVA of both parties; B2B is now SDI electronic-only, so the PDF is for B2C and records onlyFatturazione elettronica (SDI)
Spain (Factura)NIF/CIF, invoice series identifier, and Recargo de equivalencia surcharge line for resellers under that regimeRD 1619/2012
Poland (Faktura)NIP of both parties for B2B, and the "Mechanizm podzielonej płatności" (split-payment) note on invoices over 15,000 PLN for listed goods/servicesUstawa o VAT

The pattern across all of them: PrestaShop reliably prints the seller/buyer names, addresses, the product lines, and the VAT breakdown. What it routinely omits are the country-specific legal notices — the German delivery date, the French penalty clause, the Polish split-payment note. Those are what you add in the template.

Where each missing field actually comes from

Before you start coding, two of those notices need no template surgery at all — PrestaShop has a built-in slot for them:

  • Static legal text (payment terms, the French penalty clause, a fixed VAT-exemption note) goes in the Legal free text and Footer text fields under Orders → Invoices. These render on every invoice with no code. If your only gap is a constant block of legal wording, you may be done here.
  • Per-invoice dynamic fields (the German delivery date, a B2B purchase-order reference, IBAN/BIC for bank-transfer orders) are not constant, so they need the template route below.

For the dynamic ones, here is where the data lives so your override can fetch it:

  • Delivery date — the order's delivery_date field, or the date the order entered a shipped/delivered status from order_history. Pass it into the template in your HTMLTemplateInvoice.php override.
  • Customer VAT number — stored on the address (address.vat_number), not the customer. PrestaShop already exposes the address, so this is often a template-only change.
  • Bank details (IBAN/BIC) — these live in the Bank wire payment module's configuration, not the order; the cleanest approach is to read the module config in your override and print it only when the order's payment method was bank wire.
  • B2B order reference — if you capture a customer PO number, surface it from the order and display it near the header where accountants expect it.

Credit slips and delivery slips: same machinery, separate documents

Refunds and shipments produce their own PDFs, built the same way and customised the same way — but they are legally distinct documents, so don't assume an invoice change carries over.

  • Credit slips (credit notes / avoirs / Gutschriften) are legal documents in their own right. They need their own sequential numbering, separate from invoices, a reference back to the original invoice number, and the refunded amounts with the VAT broken out. PrestaShop calls this document the "order slip" internally, so the template you customise is themes/YOUR_THEME/pdf/order-slip.tpl (class HTMLTemplateOrderSlip), even though it prints with a "Credit slip" heading.
  • Delivery slips (bons de livraison) carry no pricing — they are warehouse and proof-of-delivery documents. Customise via themes/YOUR_THEME/pdf/delivery-slip.tpl. If you add the German delivery date to invoices, the delivery slip is often where the underlying date originates, so it's worth aligning the two.

The archiving trap nobody notices until an audit

This is the one that bites quietly. Because PrestaShop regenerates each PDF from the database on download, an invoice is never frozen. Edit your template to add the delivery date, and every historical invoice silently gains that field — including ones issued before you were required to. Change a product name or a tax setting, and last quarter's invoices re-render differently. For day-to-day use that's harmless. For compliance it is a real exposure: France requires invoices kept in an unalterable format for 10 years, and Germany's GoBD demands the same retention with immutability.

The fix is to capture a frozen copy of each invoice at the moment it is issued, rather than relying on regeneration. Practically:

  • Archive at the moment the invoice is first created and numbered — not on every PDF render. Trigger your archival from the invoice-generating order-status transition (or a dedicated invoice-creation hook/service), so you capture the document once, when its number is assigned. Hooking the PDF render flow (e.g. actionPDFInvoiceRender) is the wrong anchor: that fires on download and regeneration, so it can miss invoices that are never re-downloaded and can overwrite your archive with later, re-rendered output. Write that exact first-rendered PDF to disk or object storage.
  • Store it somewhere immutable — cloud storage with a write-once-read-many (WORM) policy gives you both durability and the unalterability GoBD and French law expect.
  • Never let the archived copy be the one that re-renders; the whole point is that it can't change after issue.

The problems that actually generate support tickets

When invoices misbehave on PrestaShop, it's almost always one of these, and the fix is rarely in the template:

  • Wrong company address on the invoice. It is built from the shop name and address (PS_SHOP_NAME, PS_SHOP_ADDR1 and the rest), which you set in the shop-address block under Shop Parameters → Contact, not from your general store/email contact details. Update it there.
  • Seller VAT number missing. There is no surprise here: PrestaShop's default invoice does not print a seller VAT/tax number at all — the seller block is built only from the shop name and address (the PS_SHOP_* values), with no VAT field, and International → Taxes configures only tax rates and rules, not a seller identifier. To make your VAT number appear, add it as constant text in the Legal free text / Footer text fields under Orders → Invoices, or print it from your template override. The buyer's number is separate: it only prints if the customer entered it on their address.
  • Special characters mangled in the PDF (Polish ł/ą/ę, Czech diacritics, the euro sign rendering as boxes). TCPDF needs a font that contains those glyphs — switch the invoice font to a Unicode-complete one (e.g. a DejaVu/freefont family) in the PDF settings rather than the default.
  • PDF generation fails on large orders. Usually PHP's memory_limit — invoices with many lines and a heavy template can exceed it. Raise it to 256M+ on the PHP side.
  • Numbering gaps. Often a cancelled order that already generated an invoice. Some countries tolerate gaps; France does not. The cure is a numbering strategy, not a template tweak — that's the subject of invoice and order number customization.

If you run several storefronts in a multishop install, each needs its own invoice identity — different company name, different legal footer, often a different language. PrestaShop handles most of this contextually: switch to the specific shop in the back-office shop selector before editing the invoice settings, and the prefix, legal free text and footer apply to that shop only. The danger is editing in All shops context, where every change cascades to all of them. The template files themselves are shared from the theme, so if two shops need genuinely different layouts, drive the difference with a shop-id conditional inside the .tpl rather than maintaining two forked templates.

Where a module saves you the override work

Everything above is achievable by hand, and for a single shop in a single country it may be the right call. The override route gets expensive when you're maintaining custom PHP and theme templates across PrestaShop upgrades, multiple languages and several legal jurisdictions at once — that's exactly when a maintained module earns its place, because the compatibility burden moves off your plate.

Where this commonly bites first is numbering rather than layout: yearly resets, separate series for invoices and credit notes, and formats like FA-2029-0001 that several jurisdictions effectively expect. Our mprinvoicenumber module handles that from the back office — automatic yearly resets, multi-series numbering, and custom formats with year/month and zero-padded sequences — so you're not hand-editing the counter every January or risking the gaps France won't forgive. So what's the benefit? The numbering stays compliant and the configuration lives in the admin, not in an override file that the next update could overwrite. The full reasoning on why default numbering falls short is in why default numbers are not enough.

The honest summary: PrestaShop's invoice engine is solid, but its default template is deliberately generic, and "generic" is not the same as "legal in your country." Customise the .tpl in your theme, override the PHP class only for fields PrestaShop doesn't already load, put your constant legal text in the built-in free-text slots, and — the part most merchants skip until it's too late — archive a frozen copy of every invoice the day it's issued. Get those right once, and invoicing becomes the invisible, boring part of your store it's supposed to be.

Share this post:
David Miller

David Miller

Over a decade of hands-on PrestaShop expertise. David builds high-performance e-commerce modules focused on SEO, checkout optimization, and store management. Passionate about clean code and measurable results.

Enjoyed this article?

Get our latest tips, guides and module updates delivered to your inbox.

Comments

No comments yet. Be the first!

Be the first to ask a question or share useful feedback.

Loading...
Back to top