[MIG] sale_margin_no_onchange from v14 to v16
This commit is contained in:
@@ -1,25 +1,25 @@
|
||||
# Copyright (C) 2015-2019 Akretion (http://www.akretion.com)
|
||||
# Copyright 2015-2025 Akretion France (https://www.akretion.com)
|
||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
|
||||
{
|
||||
"name": "Sale Margin No Onchange",
|
||||
"version": "14.0.1.0.0",
|
||||
"version": "16.0.1.0.0",
|
||||
"category": "Sales",
|
||||
"license": "AGPL-3",
|
||||
"summary": "Copy standard price on sale order line and compute margins",
|
||||
"description": """
|
||||
This module copies the field *standard_price* of the product on the sale order line when the sale order line is created and then computes the margin of the sale order and the sale order line (in the currency of the quotation, in the currency of the company and the margin rate).
|
||||
This module copies the cost of the product on the sale order line when the sale order line is created and it computes the margin amount (in the currency of the order and in the currency of the company) and the margin rate. It also computes the total margin of the sale order (in the currency of the order and in the currency of the company).
|
||||
|
||||
I decided to develop this module as an alternative to the OCA sale margin modules because I wanted a small and simple module. The module *account_invoice_margin*, available in the same Github repository, do the same thing on customer invoices.
|
||||
I decided to develop this module as an alternative to the OCA sale margin modules because I wanted a small and simple module. The module *account_invoice_margin*, available in the same Github repository, does the same thing on customer invoices.
|
||||
|
||||
This module has been written by Alexis de Lattre from Akretion
|
||||
<alexis.delattre@akretion.com>.
|
||||
""",
|
||||
"author": "Akretion",
|
||||
"website": "http://www.akretion.com",
|
||||
"website": "https://github.com/akretion/odoo-usability",
|
||||
"depends": ["sale"],
|
||||
"data": ["views/sale_view.xml"],
|
||||
"installable": False,
|
||||
"data": ["views/sale_order.xml", "views/sale_report.xml"],
|
||||
"installable": True,
|
||||
}
|
||||
|
||||
102
sale_margin_no_onchange/i18n/fr.po
Normal file
102
sale_margin_no_onchange/i18n/fr.po
Normal file
@@ -0,0 +1,102 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sale_margin_no_onchange
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 16.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-01-16 18:17+0000\n"
|
||||
"PO-Revision-Date: 2025-01-16 18:17+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: sale_margin_no_onchange
|
||||
#: model:ir.model.fields,field_description:sale_margin_no_onchange.field_sale_order__company_currency_id
|
||||
#: model:ir.model.fields,field_description:sale_margin_no_onchange.field_sale_order_line__company_currency_id
|
||||
msgid "Company Currency"
|
||||
msgstr "Devise de la société"
|
||||
|
||||
#. module: sale_margin_no_onchange
|
||||
#: model:ir.model.fields,field_description:sale_margin_no_onchange.field_sale_report__margin
|
||||
#: model_terms:ir.ui.view,arch_db:sale_margin_no_onchange.view_order_form
|
||||
msgid "Margin"
|
||||
msgstr "Marge"
|
||||
|
||||
#. module: sale_margin_no_onchange
|
||||
#: model_terms:ir.ui.view,arch_db:sale_margin_no_onchange.view_order_form
|
||||
#: model_terms:ir.ui.view,arch_db:sale_margin_no_onchange.view_order_line_tree
|
||||
msgid "Margin %"
|
||||
msgstr "Marge %"
|
||||
|
||||
#. module: sale_margin_no_onchange
|
||||
#: model:ir.model.fields,field_description:sale_margin_no_onchange.field_sale_order_line__margin_rate
|
||||
msgid "Margin Rate"
|
||||
msgstr "Taux de marge"
|
||||
|
||||
#. module: sale_margin_no_onchange
|
||||
#: model:ir.model.fields,field_description:sale_margin_no_onchange.field_sale_order__margin_company_currency
|
||||
#: model:ir.model.fields,field_description:sale_margin_no_onchange.field_sale_order_line__margin_company_currency
|
||||
msgid "Margin in Company Currency"
|
||||
msgstr "Marge dans la devise de la société"
|
||||
|
||||
#. module: sale_margin_no_onchange
|
||||
#: model:ir.model.fields,field_description:sale_margin_no_onchange.field_sale_order__margin_sale_currency
|
||||
#: model:ir.model.fields,field_description:sale_margin_no_onchange.field_sale_order_line__margin_sale_currency
|
||||
msgid "Margin in Sale Currency"
|
||||
msgstr "Marge dans la devise de vente"
|
||||
|
||||
#. module: sale_margin_no_onchange
|
||||
#: model:ir.model.fields,help:sale_margin_no_onchange.field_sale_order_line__margin_rate
|
||||
msgid "Margin rate in percentage of the sale price"
|
||||
msgstr "Taux de marge en pourcentage du prix de vente"
|
||||
|
||||
#. module: sale_margin_no_onchange
|
||||
#: model:ir.model,name:sale_margin_no_onchange.model_sale_report
|
||||
msgid "Sales Analysis Report"
|
||||
msgstr "Rapport d'analyse des ventes"
|
||||
|
||||
#. module: sale_margin_no_onchange
|
||||
#: model:ir.model,name:sale_margin_no_onchange.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr "Bon de commande"
|
||||
|
||||
#. module: sale_margin_no_onchange
|
||||
#: model:ir.model,name:sale_margin_no_onchange.model_sale_order_line
|
||||
msgid "Sales Order Line"
|
||||
msgstr "Ligne de commande"
|
||||
|
||||
#. module: sale_margin_no_onchange
|
||||
#: model_terms:ir.ui.view,arch_db:sale_margin_no_onchange.view_order_form
|
||||
msgid "Unit Cost"
|
||||
msgstr "Coût unitaire"
|
||||
|
||||
#. module: sale_margin_no_onchange
|
||||
#: model:ir.model.fields,field_description:sale_margin_no_onchange.field_sale_order_line__standard_price_company_currency
|
||||
msgid "Unit Cost in Company Currency"
|
||||
msgstr "Coût unitaire dans la devise de la société"
|
||||
|
||||
#. module: sale_margin_no_onchange
|
||||
#: model:ir.model.fields,field_description:sale_margin_no_onchange.field_sale_order_line__standard_price_sale_currency
|
||||
msgid "Unit Cost in Sale Currency"
|
||||
msgstr "Coût unitaire dans la devise de vente"
|
||||
|
||||
#. module: sale_margin_no_onchange
|
||||
#: model:ir.model.fields,help:sale_margin_no_onchange.field_sale_order_line__standard_price_company_currency
|
||||
msgid ""
|
||||
"Unit cost in company currency in the unit of measure of the sale order line"
|
||||
msgstr ""
|
||||
"Coût unitaire dans la devise de la société dans l'unité de mesure de la "
|
||||
"ligne de commande client"
|
||||
|
||||
#. module: sale_margin_no_onchange
|
||||
#: model:ir.model.fields,help:sale_margin_no_onchange.field_sale_order_line__standard_price_sale_currency
|
||||
msgid ""
|
||||
"Unit cost in sale currency in the unit of measure of the sale order line"
|
||||
msgstr ""
|
||||
"Coût unitaire dans la devise de vente dans l'unité de mesure de la ligne de "
|
||||
"commande client"
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2015-2019 Akretion (http://www.akretion.com)
|
||||
# Copyright 2015-2025 Akretion France (https://www.akretion.com)
|
||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
@@ -14,33 +14,32 @@ class SaleOrderLine(models.Model):
|
||||
related='order_id.company_id.currency_id',
|
||||
store=True, string='Company Currency')
|
||||
standard_price_company_currency = fields.Float(
|
||||
string='Cost Price in Company Currency', readonly=True,
|
||||
digits="Product Price",
|
||||
help="Cost price in company currency in the unit of measure "
|
||||
compute='_compute_margin', store=True, digits="Product Price",
|
||||
string='Unit Cost in Company Currency',
|
||||
help="Unit cost in company currency in the unit of measure "
|
||||
"of the sale order line")
|
||||
standard_price_sale_currency = fields.Float(
|
||||
string='Cost Price in Sale Currency',
|
||||
compute='_compute_margin', store=True,
|
||||
digits="Product Price",
|
||||
help="Cost price in sale currency in the unit of measure "
|
||||
compute='_compute_margin', store=True, digits="Product Price",
|
||||
string='Unit Cost in Sale Currency',
|
||||
help="Unit cost in sale currency in the unit of measure "
|
||||
"of the sale order line")
|
||||
margin_sale_currency = fields.Monetary(
|
||||
string='Margin in Sale Currency', store=True,
|
||||
compute='_compute_margin', currency_field='currency_id')
|
||||
compute='_compute_margin', store=True, currency_field='currency_id',
|
||||
string='Margin in Sale Currency')
|
||||
margin_company_currency = fields.Monetary(
|
||||
string='Margin in Company Currency', store=True,
|
||||
compute='_compute_margin', currency_field='company_currency_id')
|
||||
compute='_compute_margin', store=True, currency_field='company_currency_id',
|
||||
string='Margin in Company Currency')
|
||||
margin_rate = fields.Float(
|
||||
string="Margin Rate", store=True,
|
||||
compute='_compute_margin',
|
||||
digits=(16, 2), help="Margin rate in percentage of the sale price")
|
||||
compute='_compute_margin', store=True, digits=(16, 2),
|
||||
string="Margin Rate", help="Margin rate in percentage of the sale price")
|
||||
|
||||
@api.depends(
|
||||
'standard_price_company_currency', 'order_id.pricelist_id.currency_id',
|
||||
'order_id.date_order', 'product_uom_qty', 'price_subtotal',
|
||||
'product_id', 'product_uom', 'display_type', 'product_uom_qty', 'price_subtotal',
|
||||
'order_id.pricelist_id.currency_id', 'order_id.date_order',
|
||||
'order_id.company_id')
|
||||
def _compute_margin(self):
|
||||
for line in self:
|
||||
standard_price_comp_cur = 0.0
|
||||
standard_price_sale_cur = 0.0
|
||||
margin_sale_cur = 0.0
|
||||
margin_comp_cur = 0.0
|
||||
@@ -48,84 +47,52 @@ class SaleOrderLine(models.Model):
|
||||
order_cur = line.order_id.pricelist_id.currency_id
|
||||
company = line.order_id.company_id
|
||||
company_cur = company.currency_id
|
||||
if order_cur and company_cur:
|
||||
if (
|
||||
not line.display_type and
|
||||
line.product_id and
|
||||
line.product_uom and
|
||||
order_cur and
|
||||
company_cur):
|
||||
standard_price_comp_cur = line.product_id.with_company(company.id).standard_price
|
||||
if line.product_uom != line.product_id.uom_id:
|
||||
standard_price_comp_cur = line.product_id.uom_id._compute_price(
|
||||
standard_price_comp_cur, line.product_uom)
|
||||
date = line.order_id.date_order
|
||||
standard_price_sale_cur =\
|
||||
company_cur._convert(
|
||||
line.standard_price_company_currency, order_cur,
|
||||
company, date)
|
||||
standard_price_sale_cur = company_cur._convert(
|
||||
standard_price_comp_cur, order_cur, company, date)
|
||||
margin_sale_cur =\
|
||||
line.price_subtotal\
|
||||
- line.product_uom_qty * standard_price_sale_cur
|
||||
- (line.product_uom_qty * standard_price_sale_cur)
|
||||
margin_comp_cur = order_cur._convert(
|
||||
margin_sale_cur, company_cur, company, date)
|
||||
if line.price_subtotal:
|
||||
margin_rate = 100 * margin_sale_cur / line.price_subtotal
|
||||
line.standard_price_company_currency = standard_price_comp_cur
|
||||
line.standard_price_sale_currency = standard_price_sale_cur
|
||||
line.margin_sale_currency = margin_sale_cur
|
||||
line.margin_company_currency = margin_comp_cur
|
||||
line.margin_rate = margin_rate
|
||||
|
||||
# We want to copy standard_price on sale order line
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
if vals.get('product_id'):
|
||||
pp = self.env['product.product'].browse(vals['product_id'])
|
||||
std_price = pp.standard_price
|
||||
sale_uom_id = vals.get('product_uom')
|
||||
if sale_uom_id and sale_uom_id != pp.uom_id.id:
|
||||
sale_uom = self.env['uom.uom'].browse(sale_uom_id)
|
||||
# convert from product UoM to sale UoM
|
||||
std_price = pp.uom_id._compute_price(
|
||||
pp.standard_price, sale_uom)
|
||||
vals['standard_price_company_currency'] = std_price
|
||||
return super().create(vals_list)
|
||||
|
||||
def write(self, vals):
|
||||
if not vals:
|
||||
vals = {}
|
||||
if 'product_id' in vals or 'product_uom' in vals:
|
||||
for sol in self:
|
||||
# product_uom and product_id are required fields
|
||||
if 'product_id' in vals:
|
||||
pp = self.env['product.product'].browse(vals['product_id'])
|
||||
else:
|
||||
pp = sol.product_id
|
||||
if 'product_uom' in vals:
|
||||
sale_uom = self.env['uom.uom'].browse(
|
||||
vals['product_uom'])
|
||||
else:
|
||||
sale_uom = sol.product_uom
|
||||
std_price = pp.standard_price
|
||||
if sale_uom != pp.uom_id:
|
||||
std_price = pp.uom_id._compute_price(std_price, sale_uom)
|
||||
sol.write({'standard_price_company_currency': std_price})
|
||||
return super().write(vals)
|
||||
|
||||
|
||||
class SaleOrder(models.Model):
|
||||
_inherit = 'sale.order'
|
||||
|
||||
# Also defined in bi_sale_company_currency
|
||||
company_currency_id = fields.Many2one(
|
||||
related='company_id.currency_id', store=True,
|
||||
string="Company Currency")
|
||||
related='company_id.currency_id', store=True, string="Company Currency")
|
||||
margin_sale_currency = fields.Monetary(
|
||||
string='Margin in Sale Currency',
|
||||
currency_field='currency_id',
|
||||
compute='_compute_margin', store=True)
|
||||
compute='_compute_margin', store=True, currency_field='currency_id',
|
||||
string='Margin in Sale Currency')
|
||||
margin_company_currency = fields.Monetary(
|
||||
string='Margin in Company Currency',
|
||||
currency_field='company_currency_id',
|
||||
compute='_compute_margin', store=True)
|
||||
compute='_compute_margin', store=True, currency_field='company_currency_id',
|
||||
string='Margin in Company Currency')
|
||||
|
||||
@api.depends(
|
||||
'order_line.margin_sale_currency',
|
||||
'order_line.margin_company_currency')
|
||||
def _compute_margin(self):
|
||||
rg_res = self.env['sale.order.line'].read_group(
|
||||
[('order_id', 'in', self.ids)],
|
||||
[('order_id', 'in', self.ids), ('display_type', '=', False)],
|
||||
['order_id', 'margin_sale_currency:sum', 'margin_company_currency:sum'],
|
||||
['order_id'])
|
||||
mapped_data = dict([
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright 2018-2019 Akretion (http://www.akretion.com)
|
||||
# Copyright 2018-2025 Akretion France (https://www.akretion.com)
|
||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
@@ -10,10 +10,14 @@ class SaleReport(models.Model):
|
||||
|
||||
margin = fields.Float(string='Margin', readonly=True)
|
||||
|
||||
def _query(self, with_clause='', fields={}, groupby='', from_clause=''):
|
||||
fields['margin_company_currency'] =\
|
||||
", SUM(l.margin_company_currency) AS margin"
|
||||
res = super(SaleReport, self)._query(
|
||||
with_clause=with_clause, fields=fields, groupby=groupby,
|
||||
from_clause=from_clause)
|
||||
def _select_additional_fields(self):
|
||||
res = super()._select_additional_fields()
|
||||
margin_sum = f"""
|
||||
CASE WHEN l.product_id IS NOT NULL THEN SUM(l.margin_sale_currency
|
||||
/ {self._case_value_or_one('s.currency_rate')}
|
||||
* {self._case_value_or_one('currency_table.rate')}
|
||||
) ELSE 0
|
||||
END
|
||||
"""
|
||||
res["margin"] = margin_sum
|
||||
return res
|
||||
|
||||
45
sale_margin_no_onchange/views/sale_order.xml
Normal file
45
sale_margin_no_onchange/views/sale_order.xml
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015-2025 Akretion France (https://www.akretion.com/)
|
||||
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
-->
|
||||
|
||||
<odoo>
|
||||
|
||||
|
||||
<record id="view_order_form" model="ir.ui.view">
|
||||
<field name="model">sale.order</field>
|
||||
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<group name="sales_person" position="inside">
|
||||
<field name="margin_sale_currency" string="Margin"/>
|
||||
<field name="margin_company_currency" groups="base.group_multi_currency"/>
|
||||
<field name="company_currency_id" invisible="1"/>
|
||||
</group>
|
||||
<xpath expr="//field[@name='order_line']/tree" position="inside">
|
||||
<field name="standard_price_sale_currency" optional="hide" string="Unit Cost"/>
|
||||
<field name="margin_sale_currency" optional="hide" string="Margin"/>
|
||||
<field name="margin_rate" optional="hide" string="Margin %"/>
|
||||
<field name="company_currency_id" invisible="1"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_order_line_tree" model="ir.ui.view">
|
||||
<field name="model">sale.order.line</field>
|
||||
<field name="inherit_id" ref="sale.view_order_line_tree" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="/tree" position="inside">
|
||||
<field name="standard_price_company_currency" optional="hide" groups="base.group_multi_currency"/>
|
||||
<field name="standard_price_sale_currency" optional="hide"/>
|
||||
<field name="margin_company_currency" optional="hide" groups="base.group_multi_currency"/>
|
||||
<field name="margin_sale_currency" optional="hide"/>
|
||||
<field name="margin_rate" optional="hide" string="Margin %"/>
|
||||
<field name="company_currency_id" invisible="1"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
</odoo>
|
||||
20
sale_margin_no_onchange/views/sale_report.xml
Normal file
20
sale_margin_no_onchange/views/sale_report.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
Copyright 2025 Akretion France (https://www.akretion.com/)
|
||||
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
-->
|
||||
|
||||
<odoo>
|
||||
|
||||
<record id="sale_report_view_tree" model="ir.ui.view">
|
||||
<field name="model">sale.report</field>
|
||||
<field name="inherit_id" ref="sale.sale_report_view_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="price_total" position="after">
|
||||
<field name="margin" sum="1" optional="hide"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -1,55 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2015-2019 Akretion (http://www.akretion.com/)
|
||||
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
-->
|
||||
|
||||
<odoo>
|
||||
|
||||
|
||||
<record id="view_order_form" model="ir.ui.view">
|
||||
<field name="name">margin.sale.order.form</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<group name="technical" position="inside">
|
||||
<field name="margin_sale_currency" string="Margin"
|
||||
groups="account.group_account_user"/>
|
||||
<field name="margin_company_currency"
|
||||
groups="account.group_account_user"/>
|
||||
<field name="company_currency_id" invisible="1"/>
|
||||
</group>
|
||||
<xpath expr="//field[@name='order_line']/form/group/group//field[@name='analytic_tag_ids']/.." position="after">
|
||||
<group>
|
||||
<field name="standard_price_sale_currency" groups="base.group_no_one"/>
|
||||
<field name="standard_price_company_currency" groups="base.group_no_one"/>
|
||||
<field name="margin_sale_currency" groups="base.group_no_one"/>
|
||||
<field name="margin_company_currency" groups="base.group_no_one"/>
|
||||
<label for="margin_rate"/>
|
||||
<div name="margin_rate">
|
||||
<field name="margin_rate" groups="base.group_no_one" class="oe_inline"/> %
|
||||
</div>
|
||||
<field name="company_currency_id" invisible="1"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_order_line_tree" model="ir.ui.view">
|
||||
<field name="name">margin.sale.order.line.tree</field>
|
||||
<field name="model">sale.order.line</field>
|
||||
<field name="inherit_id" ref="sale.view_order_line_tree" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="price_subtotal" position="after">
|
||||
<field name="standard_price_company_currency" groups="account.group_account_user"/>
|
||||
<field name="margin_company_currency" groups="account.group_account_user"/>
|
||||
<field name="margin_rate" groups="account.group_account_user"/>
|
||||
<field name="company_currency_id" invisible="1"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user