Introduction
In Odoo, models define how data is structured and stored in the database. Every piece of business data you work with, from purchase orders to invoices to inventory, lives in a model.
Understanding Odoo models is essential for both developers and functional consultants. Models are the foundation of the Odoo data architecture. They define Odoo fields, relationships, and business logic.
This article focuses on one of the most important models in Odoo: purchase.order. Whether you are building custom modules, integrating external systems, or configuring procurement workflows, you will work with this model.
What is the purchase.order Model
The purchase.order model represents purchase orders and requests for quotation (RFQs) in Odoo. It is the central place where all procurement transactions are captured before they become receipts or vendor bills.
This model in Odoo is used by the Purchase module. When a buyer creates an RFQ, they create a purchase.order record. When the vendor confirms or the buyer approves, the order moves from draft to confirmed. The same model in Odoo holds both draft RFQs and confirmed purchase orders. The state field tracks the lifecycle.
Other modules extend this model through Odoo model inheritance. Inventory adds receipt and picking logic. Accounting adds vendor bill fields. Manufacturing can create purchase orders from bills of materials. Each module adds what it needs without duplicating the core structure.
Key Fields in the Model
Here are the most important Odoo fields in the purchase.order model. Understanding these will help you work effectively with purchase orders.
1. name
Type: Char. This field stores the order reference (e.g. PO00042). It is typically auto-generated and displayed in list views and on documents. It is the primary identifier for the purchase order.
2. state
Type: Selection. Tracks the order lifecycle. Values: draft (RFQ), sent (sent to vendor), to approve (awaiting approval), purchase (confirmed), done (fully received and invoiced), cancel (cancelled). The state drives which actions are available.
3. partner_id
Type: Many2one (res.partner). The vendor or supplier. Required. This is the main contact or company for the order. Used for all vendor-related logic and reporting.
4. partner_ref
Type: Char. The vendor reference or supplier PO number. Used when the vendor provides their own order reference. Displayed on documents and helps match receipts and bills.
5. date_order
Type: Datetime. The order date. For draft orders: creation date. For confirmed orders: confirmation date. Used for reporting, sorting, and as the default expected date for order lines.
6. date_approve
Type: Datetime. The approval or confirmation date. Set when the order moves to purchase state. Read-only. Used for reporting and audit trails.
7. order_line
Type: One2many (purchase.order.line). The order lines. Each line contains product, quantity, price, and tax. This is the core detail of the purchase order.
8. amount_untaxed
Type: Float. The subtotal before tax. Computed from order lines. Used for reporting and display.
9. amount_tax
Type: Float. The total tax amount. Computed from order lines based on tax configuration. Displayed on the order and vendor bill.
10. amount_total
Type: Float. The total amount including tax. The main amount for invoicing and reporting.
11. currency_id
Type: Many2one (res.currency). The currency. Usually inherited from the company or vendor. All monetary fields use this currency.
12. origin
Type: Char. The source document. For example, when an order is created from a sale order (dropship) or manufacturing order, the source name is stored here. Used for traceability.
13. dest_address_id
Type: Many2one (res.partner). The delivery address. If not set, defaults to the company address. Used for dropshipping when goods go directly to the customer.
14. priority
Type: Selection. Order priority: Normal or Urgent. Used for sorting and highlighting. Urgent orders may get special handling in workflows.
15. invoice_status
Type: Selection. Tracks invoicing: no (not invoiced), to invoice (ready to invoice), invoiced (fully invoiced). Drives the Create Bill action visibility.
16. invoice_count
Type: Integer. Number of related vendor bills. Computed. Used for display and to open the list of bills.
17. invoice_ids
Type: One2many (account.move). The related vendor bills. Links purchase orders to accounting. Used for three-way matching and payment tracking.
18. picking_ids
Type: One2many (stock.picking). The related delivery orders or receipts. Used when the Purchase module is installed with Inventory.
19. picking_count
Type: Integer. Number of related pickings. Computed. Used for display and to open the list of receipts.
20. create_date
Type: Datetime. Stores the date and time when the record was created. Automatically managed by Odoo. Useful for reporting and auditing.
21. write_date
Type: Datetime. Stores the date and time of the last modification. Also automatically managed. Helps track when data was last updated.
22. notes
Type: Text. Terms and conditions or internal notes. Can be displayed on the purchase order. Used for special instructions to the vendor.
23. company_id
Type: Many2one (res.company). In multi-company setups, this indicates which Odoo company the order belongs to. Affects record visibility and access.
24. user_id
Type: Many2one (res.users). The buyer or responsible user. Used for approval workflows and activity assignment.
25. fiscal_position_id
Type: Many2one (account.fiscal.position). The fiscal position for tax mapping. Applied when the vendor is in a different country or has special tax regime.
26. payment_term_id
Type: Many2one (account.payment.term). Payment terms (e.g. Net 30, 50% advance). Used when creating vendor bills.
27. display_name
Type: Char. Computed display name. Combines name with vendor info. Used in many2one dropdowns and search results. Read-only.
28. active
Type: Boolean. Soft delete flag. When False, the record is archived and hidden from default views. Purchase orders are not physically deleted to preserve history.
How This Model Is Used in Business Workflows
1. RFQ to Purchase Order
Buyer creates a request for quotation (draft). Adds lines, sends to vendor. Vendor confirms or buyer manually confirms. The order is confirmed (state = purchase). Receipts and vendor bills can be created.
2. Vendor Receipt
When goods arrive, the user creates a receipt from the purchase order. The picking_ids are linked. Quantities received update stock. Product costs are updated from the purchase price.
3. Vendor Bill
From a confirmed order, users create vendor bills. Invoice lines are pulled from order lines. payment_term_id and fiscal_position_id come from the order. invoice_status tracks progress.
4. Dropshipping
When a sale order triggers a purchase, the origin links back to the sale. dest_address_id is set to the customer address so the vendor ships directly. The purchase.order model is the bridge between sales and procurement.
5. Manufacturing and MRP
When a manufacturing order needs components, it can create purchase orders for raw materials. The origin field links to the manufacturing order. This model is central to the procure-to-pay cycle.
How Developers Extend This Model
Developers extend purchase.order using several patterns. Odoo model inheritance is the main mechanism.
Model Inheritance
Use _inherit = 'purchase.order' to extend the model. Add new Odoo fields, override methods, or add constraints. The inherit model in Odoo keeps your changes in a separate module for easy upgrades.
Adding Fields
Define new Odoo fields in your inherited model. Use the right field type: Char, Many2one, Boolean, Integer, Text, Selection. Consider company-dependent fields for multi-company.
Python Extensions
Override button_confirm, create, or write to add logic. Use super() to call the original. Be careful with computed fields and their dependencies.
Odoo Studio
Odoo Studio lets you add fields without code. Good for quick customizations. For complex logic or upgrades, custom modules are more maintainable. The API model in Odoo (purchase.order) is fully exposed via XML-RPC and JSON-RPC for integrations.
Best Practices
- Use the correct state for each stage. Do not skip states or bypass confirmation logic.
- Set partner_ref when the vendor provides a reference. It helps match receipts and bills.
- Use origin to trace the source of purchase orders. Essential for dropshipping and manufacturing.
- When building API integrations, use the XML-RPC or JSON-RPC API. The purchase.order model is fully exposed. Map external IDs carefully.
- For custom fields, use the
x_prefix or a module prefix to avoid conflicts with future Odoo versions.
Common Mistakes
- Modifying confirmed orders without checking state. Confirmed orders have restricted fields. Create a new order or use the proper workflow.
- Mixing up partner_id and dest_address_id. partner_id is the vendor; dest_address_id is where goods go (for dropshipping).
- Overriding button_confirm without calling super(). This can break other modules or future upgrades.
- Adding required custom fields without defaults. Existing orders will fail validation on upgrade.
- Forgetting to set currency_id when dealing with multi-currency vendors. Wrong currency can lead to incorrect costs and invoicing.
Conclusion
The purchase.order model is central to Odoo Purchase. It stores RFQs and confirmed purchase orders. Understanding its Odoo fields and how modules extend it will help you configure, customize, and integrate Odoo effectively.
Whether you are a functional consultant mapping procurement processes or a developer building custom modules, a solid grasp of purchase.order will save time and prevent errors.
Need Help With Your Odoo Implementation ?
Dasolo helps companies implement, customize, and optimize Odoo. We specialize in API integrations and Odoo development. Our team has deep experience with the Odoo data architecture and models like purchase.order.
If you need help with your Odoo implementation, custom modules, or integrations, we are here to help. Book a demo to discuss your project.