Odoo Sale Order and Quotation Tutorials

A Sale Order is a document that is created by a salesperson to record a customer’s order for goods or services. It includes details such as the customer’s name and address, the products or services being ordered, and the price and quantity of each item. In Odoo, a Sale Order can be easily created, edited, and tracked through the sales management module.

A Quotation, also known as a Sales Quote or Estimate, is a document that is created by a salesperson to provide a potential customer with an estimate of the cost of goods or services. It includes details such as the customer’s name and address, the products or services being quoted, and the estimated price and quantity of each item. In Odoo, a Quotation can be easily created, edited, and tracked through the sales management module.

Add a menu item to the Sales section

This menu below will appear under Sales > Orders

<record id="act_export_sale" model="ir.actions.act_window">
    <field name="name">Export Sales</field>
    <field name="res_model">wizard.sale.export.sale</field>
    <field name="view_mode">form</field>
    <field name="view_id" ref="export_sale_form_view"/>
    <field name="target">new</field>
</record>

<menuitem id="menu_sale_export" parent="sale.sale_order_menu" name="Export Sales" sequence="0" action="act_export_sale"/>

You need to write export_sale_form_view based on your need.

Inherit and Override Sale Report Document

Create a view file called custom_report_sale.xml then add it to manifest. You can add a custom field or modify existing values in this view.

<?xml version="1.0" encoding="utf-8"?>
<openerp>
    <data>
        <template id="custom_report_sale" inherit_id="sale.report_saleorder_document">
            <xpath expr="//div[@class='page']" position="replace">
                <div class="page">
            <div class="oe_structure"/>

            <h2>
                <t t-if="not (env.context.get('proforma', False) or is_pro_forma)">
                    <span t-if="doc.state not in ['draft','sent']">Order # </span>
                    <span t-if="doc.state in ['draft','sent']">Quotation # </span>
                </t>
                <t t-if="env.context.get('proforma', False) or is_pro_forma">
                    <span>Pro-Forma Invoice # </span>
                </t>
                <span t-field="doc.name"/>
            </h2>

            <div class="row mt32 mb32" id="informations">
                <div t-if="doc.client_order_ref" class="col-auto mw-100 mb-2">
                    <strong>Your Reference:</strong>
                    <p class="m-0" t-field="doc.client_order_ref"/>
                </div>
                <div t-if="doc.confirmation_date and doc.state not in ['draft','sent']" class="col-auto mw-100 mb-2">
                    <strong>Date Ordered:</strong>
                    <p class="m-0" t-field="doc.confirmation_date"/>
                </div>
                <div t-if="doc.date_order and doc.state in ['draft','sent']" class="col-auto mw-100 mb-2">
                    <strong>Quotation Date:</strong>
                    <p class="m-0" t-field="doc.date_order"/>
                </div>
                <div t-if="doc.user_id.name" class="col-auto mw-100 mb-2">
                    <strong>Salesperson:</strong>
                    <p class="m-0" t-field="doc.user_id"/>
                </div>
                <div name="payment_term" t-if="doc.payment_term_id" class="col-auto mw-100 mb-2">
                    <strong>Payment Terms:</strong>
                    <p class="m-0" t-field="doc.payment_term_id"/>
                </div>
                <div t-if="doc.validity_date and doc.state in ['draft', 'sent']" class="col-auto mw-100 mb-2">
                    <strong>Expiration Date:</strong>
                    <p class="m-0" t-field="doc.validity_date"/>
                </div>
            </div>

            <!-- Is there a discount on at least one line? -->
            <t t-set="display_discount" t-value="any([l.discount for l in doc.order_line])"/>

            <table class="table table-sm o_main_table">
                <thead>
                    <tr>
                        <!-- TODO: remove in master -->
                        <t t-set="colspan" t-value="5"/>
                        <th class="text-left">Description</th>
                        <th class="text-right">Quantity</th>
                        <th class="text-right">Unit Price</th>
                        <th class="text-right">Taxes</th>
                        <th t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">
                            <span>Disc.(%)</span>
                            <!-- TODO: remove in master -->
                            <t t-set="colspan" t-value="colspan+1"/>
                        </th>                        
                        <th class="text-right">
                            <t groups="account.group_show_line_subtotals_tax_excluded">Amount</t>
                            <t groups="account.group_show_line_subtotals_tax_included">Total Price</t>
                        </th>
                    </tr>
                </thead>
                <tbody class="sale_tbody">

                    <t t-set="current_subtotal" t-value="0"/>

                    <t t-foreach="doc.order_line" t-as="line">

                        <t t-set="current_subtotal" t-value="current_subtotal + line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
                        <t t-set="current_subtotal" t-value="current_subtotal + line.price_total" groups="account.group_show_line_subtotals_tax_included"/>

                        <tr t-att-class="'bg-200 font-weight-bold o_line_section' if line.display_type == 'line_section' else 'font-italic o_line_note' if line.display_type == 'line_note' else ''">
                            <t t-if="not line.display_type">
                                <td><span t-field="line.name"/></td>
                                <td class="text-right">
                                    <span t-field="line.product_uom_qty"/>
                                    <span t-field="line.product_uom" groups="uom.group_uom"/>
                                </td>
                                <td class="text-right">
                                    <span t-field="line.price_unit"/>
                                </td>
                                <td class="text-right">
                                    <span t-esc="', '.join(map(lambda x: (x.description or x.name), line.tax_id))"/>
                                </td>
                                <td t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">
                                    <span t-field="line.discount"/>
                                </td>                                
                                <td class="text-right o_price_total">
                                    <span t-field="line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
                                    <span t-field="line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
                                </td>
                            </t>
                            <t t-if="line.display_type == 'line_section'">
                                <td colspan="99">
                                    <span t-field="line.name"/>
                                </td>
                                <t t-set="current_section" t-value="line"/>
                                <t t-set="current_subtotal" t-value="0"/>
                            </t>
                            <t t-if="line.display_type == 'line_note'">
                                <td colspan="99">
                                    <span t-field="line.name"/>
                                </td>
                            </t>
                        </tr>

                        <t t-if="current_section and (line_last or doc.order_line[line_index+1].display_type == 'line_section')">
                            <tr class="is-subtotal text-right">
                                <td colspan="99">
                                    <strong class="mr16">Subtotal</strong>
                                    <span
                                        t-esc="current_subtotal"
                                        t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
                                    />
                                </td>
                            </tr>
                        </t>
                    </t>
                </tbody>
            </table>

            <div class="clearfix">
                <div id="total" class="row" name="total">
                    <div t-attf-class="#{'col-4' if report_type != 'html' else 'col-sm-7 col-md-5'} ml-auto">
                        <table class="table table-sm">
                            <tr class="border-black o_subtotal" style="">
                                <td><strong>Subtotal</strong></td>
                                <td class="text-right">
                                    <span t-field="doc.amount_untaxed"/>
                                </td>
                            </tr>
                            <t t-foreach="doc.amount_by_group" t-as="amount_by_group">
                                <tr style="">
                                    <t t-if="amount_by_group[3] == 1 and doc.amount_untaxed == amount_by_group[2]">
                                        <td>
                                            <span t-esc="amount_by_group[0]"/>
                                            <span>&amp;nbsp;<span>on</span>&amp;nbsp;<t t-esc="amount_by_group[2]" t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'/></span>
                                        </td>
                                        <td class="text-right o_price_total">
                                            <span t-esc="amount_by_group[1]"
                                                t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'/>
                                        </td>
                                    </t>
                                    <t t-else ="">
                                        <td>
                                            <span t-esc="amount_by_group[0]"/>
                                        </td>
                                        <td class="text-right o_price_total">
                                            <span t-esc="amount_by_group[1]"
                                                t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'/>
                                        </td>
                                    </t>
                                </tr>
                            </t>
                            <tr class="border-black o_total">
                                <td><strong>Total</strong></td>
                                <td class="text-right">
                                    <span t-field="doc.amount_total"/>
                                </td>
                            </tr>
                        </table>
                    </div>
                </div>
            </div>

            <p t-field="doc.note" />
            <p t-if="doc.payment_term_id.note">
                <span t-field="doc.payment_term_id.note"/>
            </p>
            <p id="fiscal_position_remark" t-if="doc.fiscal_position_id and doc.fiscal_position_id.sudo().note">
                <strong>Fiscal Position Remark:</strong>
                <span t-field="doc.fiscal_position_id.sudo().note"/>
            </p>

            <div t-if="doc.signature" class="mt32 ml16 mr16" name="signature">
                <div class="offset-8">
                    <strong>Signature</strong>
                </div>
                <div class="offset-8">
                    <img t-att-src="image_data_uri(doc.signature)" style="max-height: 4cm; max-width: 8cm;"/>
                </div>
                <div class="offset-8 text-center">
                    <p t-field="doc.signed_by"/>
                </div>
            </div>

            <div class="oe_structure"/>
        </div>
            </xpath>
        </template>

    </data>
</openerp>

Add custom field to Sale Order form view

class CustomSaleOrder(models.Model):
    _inherit = 'sale.order'

    my_custom_field = fields.Char(string='My Field')
<record id="view_sale_order_form_custom_inherit" model="ir.ui.view">
    <field name="name">sale.order.form.inherit</field>
    <field name="model">sale.order</field>
    <field name="inherit_id" ref="sale.view_order_form"/>
    <field name="arch" type="xml">
        <xpath expr="//field[@name='partner_shipping_id']" position="after">
            <field name="my_custom_field"/>            
        </xpath>
    </field>
</record>

Add custom field to Sale Order tree

<record model="ir.ui.view" id="view_sale_order_tree_custom_inherit">
    <field name="name">sale.order.tree.with.onboarding.inherit</field>
    <field name="model">sale.order</field>
    <field name="inherit_id" ref="sale.view_quotation_tree_with_onboarding"/>
    <field name="arch" type="xml">
        <xpath expr="//field[@name='amount_total']" position="after">
            <field name="my_custom_field"/>
        </xpath>
    </field>
</record>

Leave a Comment

Your email address will not be published. Required fields are marked *


Scroll to Top

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close