Odoo Sales Order Import from Customer Purchase Order PDFs to Odoo Sales Orders and Quotations

Odoo imports sales orders from a spreadsheet, and it can read a purchase order XML sent by another Odoo database.

PDF, JPG, PNG, BMP, HEIC, TIFF

Submit your purchase orders

PDF, scan, or email attachment
Every order line captured
Rows shaped for Import records
Plans from $49 a month

Overview

Odoo imports sales orders from a spreadsheet, and it can read a purchase order XML sent by another Odoo database. It will not read the PDF your customer emailed. Upload that PO here and get the customer PO number, the ship-to and every line with product, quantity, unit and price, laid out the way Import records expects.

Why customer orders still get retyped into Odoo

Odoo is good at importing a clean file. The time goes into producing that file from purchase orders that each customer formats their own way.

The PDF route only works Odoo to Odoo

The Upload button on the Quotations dashboard reads UBL XML, the format Odoo exports. A PO printed from SAP, NetSuite or a customer portal is a plain PDF, and there is nothing in it for Odoo to parse.

Digitization stops at bills

Odoo document digitization is built for vendor bills and, less commonly, customer invoices and credit notes. Customer purchase orders are not on the list.

Buyer part numbers do not match

Customers order by their own codes. Odoo matches an imported product by name, internal reference or External ID, so every buyer code needs a lookup before the file will load.

Imports cannot be undone

Odoo says so plainly in its import documentation. A file with a shifted column creates wrong quotations that someone then cancels by hand.

From customer PO to Odoo sales order rows

Upload the purchase order your customer sent. The AI reads the header and every line and returns structured data you can paste into an Odoo import template, so creating the quotation becomes a mapping step instead of a typing job.

Reads any PO layout

No per-customer template. Works on digital PDFs, scans and photographed pages from any buyer system.

Every line, not a summary

A 40-line restocking order comes back as 40 rows with part number, description, quantity, unit of measure and unit price.

One order, many lines

Header fields on the first row and line fields below, which is exactly how Odoo reads an order with several Order Lines.

Customer reference ready

The buyer PO number sits in its own column, so it maps to Customer Reference and never to your own SO number.

How sales orders get into Odoo today

Odoo gives you three ways to create sales orders without typing them. The first is Import records: open Sales, go to the Orders list, open the gear or Favorites menu, and choose Import records. Odoo accepts CSV, XLS, XLSX and ODS files, lets you map each column to a field, and has a Test button that checks the file before anything is written. Its own documentation is blunt about the stakes: imports are permanent and cannot be undone.

The second is the purchase-to-sales EDI flow. When your customer also runs Odoo, their purchase order can be exported as a UBL XML file, and you upload it, or drag it, onto the Quotations dashboard. Odoo then builds a sales order with the customer and the lines filled in. The third is the external API, where an integration creates a sale.order record and its order_line entries directly.

Notice what all three assume: that the order already exists as structured data. Most customer POs in the United States do not. They arrive as a PDF from someone else's ERP, a portal download or a scanned fax. That document is the gap this page fills. You upload it and get the rows the import screen or the API needs.

How Odoo reads an order with several lines

An Odoo sales order is one record with many Order Lines, so the import file is not one flat row per order. The first row carries the order fields (Customer, Customer Reference, Order Date, Delivery Address) together with the first line (Order Lines/Product, Order Lines/Quantity, Order Lines/Unit Price). Every following line for the same order goes on its own row with the order columns left blank. A new value in the Customer column starts the next order.

That layout trips people up because a spreadsheet exported from somewhere else repeats the header on every line, and Odoo then creates one order per line. The extraction returns header and lines separately, so building the Odoo layout is a paste, not a rebuild.

Where the customer PO number belongs

The buyer's PO number goes in Customer Reference (the client_order_ref field on the sales order). Your own number, such as S00042, comes from the Odoo sequence, so leave it out of the file. Keeping the customer reference populated is what lets your team find the order when the buyer calls, and it prints on the delivery slip and invoice.

Customer part numbers are not your products

Odoo matches a product in an import by its name, by its internal reference, or by External ID. Standard Odoo keeps vendor product codes on the purchase side but has no built-in table of customer part numbers, so the lookup from a buyer code to your product has to happen before the file loads. The extraction keeps the part number exactly as printed, next to the description, so you can map it once per customer in the spreadsheet and reuse that mapping on every order that account sends. Unknown codes show up as a blank cell, not as a failed import.

When the order volume is high

If a weekend leaves 30 purchase orders in the shared inbox, run them through bulk purchase order upload, up to 50 files in one pass, and get one consistent file back. Long orders are covered by purchase order line item extraction, which returns every row rather than a summary.

Same workflow, other systems

If part of the business runs on another ERP, the pattern is the same with different field names. See Business Central sales order import, NetSuite sales order import and QuickBooks sales order import. For the buying side of Odoo, where you are the one issuing the purchase order, use purchase order to Odoo instead.

Customer PO field to Odoo sales order field

What comes off the customer document and where it lands in an Odoo import file. Labels follow the standard Sales app; technical names are shown for API users.

Odoo import column Technical field Where it comes from Notes
Customer partner_id The buying company, matched to an existing contact. Match by name or External ID
Customer Reference client_order_ref The purchase order number printed on the customer PO. Prints on delivery and invoice
Order Date date_order The PO date on the document. Use a date Odoo can parse
Delivery Address partner_shipping_id The ship-to block on the PO. Contact must exist
Delivery Date commitment_date The need-by date the buyer asked for. Optional
Payment Terms payment_term_id Terms printed on the PO, checked against your agreement. Usually from the customer
Order Lines/Product order_line/product_id The customer part number, mapped to your product. Product must exist
Order Lines/Description order_line/name The line description from the PO. Defaults from the product
Order Lines/Quantity order_line/product_uom_qty Ordered quantity on each line. Per line
Order Lines/Unit Price order_line/price_unit The price the buyer printed. Pricelist can set it instead

How the import runs

Four steps from the customer attachment to a quotation in Odoo.

1

Upload the customer PO

Drop in the PDF, the scan or the email attachment. Up to 50 files per bulk upload, with no per-customer setup.

Tip: Portal downloads and forwarded attachments work the same way.

2

Review and Map

Check the header and lines on screen, then map buyer part numbers to your products and the customer name to the Odoo contact.

Tip: Keep the mapping sheet; the same account sends the same codes.

3

Download the rows

Take Excel or CSV with the order fields on the first row and each line below, or pull JSON through the API for an integration.

Tip: Leave the order columns blank on continuation rows.

4

Import into Odoo

In Sales, open Orders, choose Import records, map the columns, press Test, then Import. Review the new quotations and confirm them.

Tip: Imports cannot be undone, so test a single order first.

Why order desks use this in front of Odoo

1
Row per line item
3
Export formats: Excel, CSV, JSON
50
POs per bulk upload

Security and privacy

  • Bank-grade TLS encryption in transit
  • Your PO data is never sold or shared
  • US-based cloud infrastructure

Odoo sales order import questions, answered

Open the Sales app, go to Orders, and choose Import records from the gear or Favorites menu. Upload a CSV, XLS, XLSX or ODS file, map each column to a field, press Test to check it, then Import. Put the order fields on the first row and each extra line on its own row with the order columns blank.

Only when the PDF comes from another Odoo database as UBL XML, or embeds that XML. Upload it on the Quotations dashboard and Odoo fills in the customer and lines. A plain PDF from another ERP has no data layer to read, so it needs to be extracted into rows first.

No. Odoo documents digitization mainly for vendor bills and refunds, and less commonly for customer invoices and credit notes. Customer purchase orders are not covered, which is why order desks extract them before importing.

In Customer Reference, the client_order_ref field. Your own sales order number comes from the Odoo sequence, so leave it out of the import file. The customer reference prints on the delivery slip and invoice and makes the order easy to find later.

Use one row per line. The first row holds the order fields plus the first line, using columns like Order Lines/Product and Order Lines/Quantity. Following rows fill only the Order Lines columns and leave the order columns empty, so Odoo attaches them to the same order.

Because the order columns are repeated on every row. Odoo treats a filled Customer cell as the start of a new order. Clear the order columns on continuation rows, or group the lines under one header row, and test again before importing.

No. Odoo states that imports are permanent and cannot be undone. Use the Test button, import one order first, and keep the source file so you can find and cancel anything that went wrong.

Match them before importing. Odoo resolves a product by name, internal reference or External ID, and standard Odoo has no customer part number table. Keep a per-customer mapping sheet from buyer code to your internal reference and apply it to the extracted rows.

No. PurchaseOrders reads the customer purchase order and returns structured data as Excel, CSV or JSON. It does not install in Odoo and does not create, confirm or deliver orders. You load the rows through Import records, the API or your own integration.