Introduction
In Odoo, models define how data is structured and stored in the database. Every piece of business data you work with, from sales orders to invoices to contacts, 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 fields, relationships, and business logic.
This article focuses on one of the most important models in the Sales module: sale.order.line. Whether you are building custom modules, integrating external systems, or configuring pricing workflows, you will work with this model.
What is the sale.order.line Model
The sale.order.line model represents individual line items on a quotation or sales order in Odoo. Each line typically corresponds to one product, with quantity, price, and tax information.
This model in Odoo is used by the Sales module (sale). It inherits from analytic.mixin for project cost tracking and timesheet integration. When you add a product to a quotation, you are creating a sale.order.line record.
The model is defined in the sale module. Other modules extend it through Odoo model inheritance. For example, sale_stock adds delivery-related fields. sale_margin adds margin calculations. 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 sale.order.line model. Understanding these will help you work effectively with quotations and sales orders.
1. order_id
Type: Many2one (sale.order). Required. Reference to the parent sales order. This field links each line to its parent. Lines are deleted when the order is deleted (cascade).
2. sequence
Type: Integer. Default 10. Controls the display order of lines on the order. Used for sorting sections, notes, and product lines.
3. company_id
Type: Many2one (res.company). Related from order_id. Used for multi-company rules and access control.
4. currency_id
Type: Many2one (res.currency). Related from order_id. Used for all monetary fields on the line. Ensures correct currency for pricing.
5. order_partner_id
Type: Many2one (res.partner). Related from order_id. The customer. Used for pricelist and tax rules.
6. salesman_id
Type: Many2one (res.users). Related from order_id. The salesperson. Used for commission and reporting.
7. state
Type: Selection. Related from order_id. Order status (draft, sent, sale, done, cancel). Drives which fields are editable.
8. display_type
Type: Selection. Values: line_section or line_note. When set, the line is a section header or a note, not a product line. Product fields are empty.
9. is_downpayment
Type: Boolean. Indicates if the line is a down payment. Down payments are invoiced separately.
10. is_expense
Type: Boolean. True when the line comes from an expense or vendor bill. Used for project cost tracking.
11. product_id
Type: Many2one (product.product). The product being sold. Domain restricts to saleable products. Required for product lines.
12. product_template_id
Type: Many2one (product.template). Computed from product_id. Used by the product configurator for variant selection.
13. name
Type: Text. The line description. Computed from product and custom attributes. Includes variant details when applicable.
14. product_uom_qty
Type: Float. Required. The quantity ordered. Default 1.0. Can be driven by packaging.
15. product_uom
Type: Many2one (uom.uom). Unit of measure. Defaults from product. Used for quantity and pricing.
16. tax_id
Type: Many2many (account.tax). Taxes applied to the line. Computed from product and fiscal position.
17. price_unit
Type: Float. Required. Unit price per product_uom. Computed from pricelist or product. Can be overridden manually.
18. discount
Type: Float. Discount percentage. Applied to price_unit before tax.
19. price_subtotal
Type: Monetary. Subtotal before tax. Computed from quantity, unit price, and discount.
20. price_tax
Type: Float. Total tax amount. Computed from price_subtotal and tax_id.
21. price_total
Type: Monetary. Total including tax. The main amount for invoicing.
22. product_packaging_id
Type: Many2one (product.packaging). Optional packaging (e.g. box of 12). When set, quantity can be driven by packaging.
23. customer_lead
Type: Float. Lead time in days. Days between order confirmation and shipment. Used for delivery date calculation.
24. qty_delivered
Type: Float. Quantity delivered. Updated by stock moves or manually. Used for partial invoicing.
25. qty_invoiced
Type: Float. Quantity already invoiced. Computed from invoice lines.
26. qty_to_invoice
Type: Float. Remaining quantity to invoice. Computed from qty_delivered and qty_invoiced.
27. invoice_status
Type: Selection. Values: upselling, invoiced, to invoice, no. Indicates invoicing state for the line.
28. invoice_lines
Type: Many2many (account.move.line). Links to invoice lines created from this sale line. Used for traceability.
29. create_date
Type: Datetime. When the record was created. Automatically managed by Odoo.
30. write_date
Type: Datetime. When the record was last modified. Used for auditing.
How This Model Is Used in Business Workflows
1. Quotation and Sales Order
When a salesperson creates a quotation, they add products. Each product becomes a sale.order.line. The lines show quantity, price, discount, and total. The order is confirmed when the customer accepts.
2. Pricelist and Discounts
Pricelists are applied per line. The price_unit and discount fields are computed from pricelist rules. Volume discounts or customer-specific pricing are handled here.
3. Delivery and Invoicing
When stock is delivered, qty_delivered is updated. Invoicing can be done per delivery or at once. The invoice_status field guides the user on what remains to invoice.
4. Project and Services
For service products, the lines link to project tasks and timesheets. The analytic.mixin inheritance enables cost tracking per project.
5. E-commerce and Portal
Website visitors add products to cart. Each cart line becomes a sale.order.line when the order is created. The product configurator uses product_template_id and custom attributes.
How Developers Extend This Model
Developers extend sale.order.line using several patterns. Odoo model inheritance is the main mechanism.
Model Inheritance
Use _inherit = 'sale.order.line' to extend the model. Add new 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 _compute_price_unit, _compute_price_subtotal, or create/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.
Best Practices
- Use display_type for sections and notes instead of fake product lines. This keeps reporting clean.
- When building API integrations, create lines via the order_id. Use the order_line_ids field on sale.order with the correct command format.
- Respect the SQL constraints. A product line must have product_id and product_uom. A section or note must have display_type.
- For custom pricing, use pricelist rules when possible. Override compute methods only when you need logic not supported by pricelists.
- For custom fields, use the
x_prefix or a module prefix to avoid conflicts with future Odoo versions.
Common Mistakes
- Creating lines without order_id. The field is required. Always create lines in the context of an order.
- Mixing up product_id and product_template_id. For product lines, set product_id. For configurator flows, use product_template_id to select a variant.
- Modifying price_unit or discount after invoicing. Once qty_invoiced is greater than zero, price changes can cause inconsistencies.
- Overriding core methods without calling super(). This can break other modules or future upgrades.
- Forgetting to set display_type for section or note lines. Without it, the line is treated as a product line and will fail validation.
Conclusion
The sale.order.line model is central to Odoo Sales. It stores each product line on quotations and orders. Understanding its fields and how modules extend it will help you configure, customize, and integrate Odoo effectively.
Whether you are a functional consultant mapping business processes or a developer building custom modules, a solid grasp of sale.order.line 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 sale.order.line.
If you need help with your Odoo implementation, custom modules, or integrations, we are here to help. Book a demo to discuss your project.