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 | namein the PO description column, notes render multi-line, standing delivery instruction added.