Skip to content

ERP print templates — what each one is and how to change it

The reference behind SOP-029 Print Templates. The SOP is the procedure; this page is the map of the templates themselves — what exists, what each one prints, and the field names that actually work.

Sources: ~/erp-config/templates/ — FileBrowser home/erp-config/templates, the NAS share ERP-Templates, or the symlink ~/torrentvpn/inventree/templates-src. Pushed to EmbeddedEra/erp-config (private). Uploaded by Production tools → Refresh print templates.

The templates

File Prints for Size What it shows
ee_purchase_order.html a purchase order Letter Supplier and ship-to blocks, order/target dates, supplier reference, the order's Description, six-column line table (# · part number = IPN \| name · description = our part description · qty · unit price · total), extra lines (shipping, fees), total, Notes, a standing delivery instruction
ee_invoice.html a sales order Letter Bill-to/ship-to, dates and references, lines, totals with the zero-tax row and the no-tax sentence, payment block, notes
ee_proposal.html a sales order Letter For/prepared-by/date, scope from the order description and notes, lines and total, "what happens next", 30-day validity
ee_packing_slip.html a shipment Letter Customer and ship-to, order and shipment references, tracking number, lines with serial numbers. No prices
ee_work_order_traveler.html a build A4 Build reference and QR, kit list with pick boxes, a 16-operation sign-off grid, deviations, closing counts
ee_stock_item_label.html a stock item 62 × 29 mm QR, IPN, description, quantity, batch, location, date
ee_serial_label.html a stock item 25 × 25 mm QR, assembly IPN and revision, serial number
Zebra 2 × 1.5 in stock / location / part labels stock item, location, part 50.8 × 38.1 mm The GX430t set; sources still in ~/Notes/hardware/labels/ (see the open item in mrp)

Field names that work (and the ones that look right but don't)

A template receives the model object. The API flattens names for convenience and those flattened names are not available in a template. Everything below was proven on a real PDF.

You want Write Not
Supplier SKU on a PO line line.part.SKU line.SKU
Manufacturer part number line.part.manufacturer_part.MPN line.part.manufacturer_part_number
Our part number line.part.part.IPN line.ipn
Our part name / description line.part.part.name, line.part.part.description line.description
Text of an extra line (shipping, fees) line.description line.reference, line.notes
The order's own note order.notes (add \|linebreaksbr so typed line breaks survive)
The order's short description order.description
Supplier contact (the PO's own) order.contact.name, .role, .email, .phone supplier.contact, which is the company default, not this order's

A blank column is nearly always a wrong field name, not missing data. Check the record in the ERP first, then compare with InvenTree's own shipped templates inside the container (report/templates/report/).

Output file names

Each template has a filename pattern, set on the template record, not in the HTML. They were all output.pdf, which is why downloads looked random. Now:

Template Pattern Example
Purchase Order PurchaseOrder-{{ reference }}.pdf PurchaseOrder-PO-0001.pdf
Invoice Invoice-{{ reference }}.pdf Invoice-SO-0003.pdf
Proposal Proposal-{{ reference }}.pdf
Packing slip PackingSlip-{{ reference }}.pdf
Traveler Traveler-{{ reference }}.pdf

Set with a PATCH to /api/report/template/<pk>/ with {"filename_pattern": "..."}.

Two templates must never share a filename pattern. InvenTree's own Purchase Order report and ours both used PurchaseOrder-{{ reference }}.pdf. The first file to claim that name keeps it, so the clean name held the default layout while our prints were pushed to suffixed names — which looked like "the default template comes back when the name matches". Fixed by disabling the shipped report (see below); if one is ever re-enabled, give it a different pattern.

created on a data output is a date, not a timestamp. Anything ageing outputs must read the file's own mtime; parsing created puts every output at midnight and deletes it seconds after it is made. erp-output-clean learned that the hard way on 2026-09-17.

Why a download used to be PO-0001_OligxZw.pdf. Every generated file is kept under media/data_output/, and Django renames a new file when that name is already taken. The suffix is the collision, not the pattern. ~/.local/bin/erp-output-clean deletes generated outputs older than 3 minutes (records and files) and runs every minute from cron, so the next print gets the clean name. The files are disposable — any document prints again from its record.

The footer. The order base template puts the InvenTree version in the centre of the footer; {% block bottom_center %}content: "";{% endblock %} removes it. The left footer is ours.

Which template the ERP offers

InvenTree ships its own report and label templates. Where we have a replacement, the shipped one is disabled so the Print dialog offers exactly one choice and nobody prints the generic version by accident (2026-09-17). Disabled: InvenTree Build Order, Purchase Order, Sales Order, Sales Order Shipment; Part Label, Part Label (Code128), Stock Item Label (QR), both Stock Location labels. Still InvenTree's because we have no replacement yet: Bill of Materials, Return Order, Transfer Order, Test Report, Stock Report Merge, Stock Location Report, Build Line Label. Sales orders deliberately keep two of ours — Invoice and Proposal — so that is a real choice, not an accident. Re-enable one with a PATCH to /api/{report|label}/template/<pk>/ with {"enabled": true}; nothing was deleted.

Notes on a purchase order

Three places take text, and all three print:

Field Where it prints Use it for
Order Description header block, under Supplier Reference the one-line subject
Order Notes under the totals, line breaks kept anything the supplier should read about the whole order
Line notes (per item) grey, under that line's description anything about one item: packaging, date code, substitutions allowed

A line note is set on the line itself in the ERP. Over the API the line serializer requires the whole object, so a bare PATCH {"notes": …} is rejected with "Supplier part must be specified" — send a PUT with order, part, quantity, purchase_price and the note.

Every PO also carries a standing delivery instruction (mark the box with the PO number, send invoice and tracking to hello@embeddedera.com) — that text lives in the template, so change it once there rather than typing it into every order.

Log

  • 2026-09-17: written. Filename patterns set, IPN | name in the PO description column, notes render multi-line, standing delivery instruction added.