How-to guide
How to generate purchase orders from a CSV
Create vendor-ready purchase order PDFs from approved procurement rows.
What you'll need
Supplies
- Approved purchase order CSV
- Vendor and delivery information
- Procurement terms or receiving notes
Tools
- DocForge
- Procurement tracker
- Spreadsheet app
Steps
- 1
Freeze the approved PO export
Freeze the PO export only after approval is complete. Do not generate vendor-facing documents while requester names, delivery dates, or totals are still changing. Export one row per purchase order with stable headers such as `po_number,vendor_name,buyer_name,department,order_date,delivery_date,item_summary,subtotal,tax,total,delivery_address`. Remove approval comments, budget debates, and internal negotiation notes unless procurement has explicitly approved them for the vendor. A common error is exporting live spreadsheet formulas instead of values, which can change `total` after the PDF batch is generated. Paste values into a clean CSV and keep that file as the approved source. Normalize dates before upload. If `delivery_date` arrives as `30/06/26` from a UK procurement export, the `| date` filter may render blank or in the wrong format. Use `2026-06-30`, then display it with `{{ row.delivery_date | date: '%B %-d, %Y' }}`. Include one high-value row such as `4250.00` in the preview set.
- 2
Map procurement fields
Map the procurement fields exactly as the vendor needs to read them. Connect `po_number` to the document title, `vendor_name` to the recipient block, `buyer_name` to the contact area, and `delivery_address` to the receiving location. Check totals carefully: `subtotal,tax,total` must come from the approved export, not a calculation typed into the template. Use clear Liquid placeholders like `{{ row.po_number }}`, `{{ row.vendor_name }}`, and `{{ row.total }}`. For optional receiving instructions, use a conditional block so blank fields do not leave awkward labels: `{% if row.receiving_instructions %}Receiving instructions: {{ row.receiving_instructions }}{% endif %}`. A frequent mapping mistake is connecting `department` to `delivery_address` because both appear near each other in the CSV. Catch that by previewing a row for `Acme Manufacturing Co.` with a known address and comparing the PDF line by line against the CSV. Do not proceed until the PO number, delivery date, and total match exactly.
- 3
Preview vendor-facing edge cases
Preview the rows that vendors are most likely to question. Include a long vendor name, a long `item_summary`, an unusual delivery site, and a large total. Use a row like `PO-2026-1048,Northwest Regional Distribution Holdings LLC,Jamie Park,Operations,2026-06-14,2026-06-30,Rush replacement parts for West Dock compressor station,3900.00,350.00,4250.00`. Check whether the vendor name wraps cleanly, whether the delivery address stays with the delivery label, and whether the item description pushes totals onto a second page. Preview one row with missing optional receiving instructions and one with a strict dock note, such as `Deliver to Gate 3 before 10:00`. If the template uses `{% if row.delivery_date %}`, confirm the label disappears when the date is blank rather than showing an empty promise. Compare each PDF against the frozen CSV. The vendor should understand what was ordered, where it goes, who requested it, and which PO number to cite on invoices.
- 4
Generate and archive the batch
Generate the final PO batch from the frozen CSV, then archive the output with the source file used to create it. Name files with the PO number first, such as `PO-2026-1048-Acme-Manufacturing.pdf`, so receiving, finance, and the vendor can find the same document later. Store the CSV alongside the ZIP; it is the audit trail when someone asks why `total` was `4250.00` or which `delivery_date` was sent. If files go out by email or portal upload, send only the generated PDFs and keep internal notes out of the attachment package. A common error is regenerating after a small spreadsheet fix and mixing old and new PDFs in the same folder. Create a new dated folder for each run, for example `po-batch-2026-06-14`, and keep failed or corrected rows separate. Spot-check three PDFs after generation: first row, last row, and the highest-value PO. Confirm `{{ row.po_number }}` resolved everywhere, including headers and footers.