Skip to content

Commit 95b2853

Browse files
committed
[ADD] sale_order_net_price_control
1 parent 86f1818 commit 95b2853

18 files changed

Lines changed: 817 additions & 0 deletions
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
============================
2+
Sale Order Net Price Control
3+
============================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:483f44adb6b4c2214568d8f8ec242a00f09f9f58acccc118b3c838778e52cb32
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
13+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
14+
:target: https://odoo-community.org/page/development-status
15+
:alt: Beta
16+
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
17+
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
18+
:alt: License: LGPL-3
19+
.. |badge3| image:: https://img.shields.io/badge/github-solvosci%2Fslv--sale-lightgray.png?logo=github
20+
:target: https://github.com/solvosci/slv-sale/tree/17.0/sale_order_net_price_control
21+
:alt: solvosci/slv-sale
22+
23+
|badge1| |badge2| |badge3|
24+
25+
Controls the net price of the products when they are sold.
26+
It has to be higher than the last price on a purchase invoice and higher than the MAYM pricelist.
27+
Allows users who are in the 'Sell Products at Any Price' group to bypass all controls.
28+
29+
**Table of contents**
30+
31+
.. contents::
32+
:local:
33+
34+
Bug Tracker
35+
===========
36+
37+
Bugs are tracked on `GitHub Issues <https://github.com/solvosci/slv-sale/issues>`_.
38+
In case of trouble, please check there if your issue has already been reported.
39+
If you spotted it first, help us to smash it by providing a detailed and welcomed
40+
`feedback <https://github.com/solvosci/slv-sale/issues/new?body=module:%20sale_order_net_price_control%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
41+
42+
Do not contact contributors directly about support or help with technical issues.
43+
44+
Credits
45+
=======
46+
47+
Authors
48+
~~~~~~~
49+
50+
* Solvos
51+
52+
Contributors
53+
~~~~~~~~~~~~
54+
55+
* Iria Alonso <iria.alonso@solvos.es>
56+
57+
Maintainers
58+
~~~~~~~~~~~
59+
60+
This module is part of the `solvosci/slv-sale <https://github.com/solvosci/slv-sale/tree/17.0/sale_order_net_price_control>`_ project on GitHub.
61+
62+
You are welcome to contribute.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import models
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# © 2025 Solvos Consultoría Informática (<http://www.solvos.es>)
2+
# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html
3+
{
4+
"name": "Sale Order Net Price Control",
5+
"summary": """
6+
Controls the net price of the products when they are sold.
7+
It has to be higher than the last price on a purchase invoice and higher than the MAYM pricelist.
8+
Allows users who are in the 'Sell Products at Any Price' group to bypass all controls.
9+
""",
10+
"author": "Solvos",
11+
"license": "LGPL-3",
12+
"version": "17.0.1.0.0",
13+
'category': "Operations/Sale",
14+
"website": "https://github.com/solvosci/slv-sale",
15+
"depends": ["sale"],
16+
"data": [
17+
"security/sale_order_security.xml",
18+
"views/res_config_settings_views.xml",
19+
"views/sale_order_views.xml",
20+
"views/product_template.xml",
21+
],
22+
'installable': True,
23+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Translation of Odoo Server.
2+
# This file contains the translation of the following modules:
3+
# * sale_order_net_price_control
4+
#
5+
msgid ""
6+
msgstr ""
7+
"Project-Id-Version: Odoo Server 17.0\n"
8+
"Report-Msgid-Bugs-To: \n"
9+
"POT-Creation-Date: 2025-06-09 12:33+0000\n"
10+
"PO-Revision-Date: 2025-06-09 12:33+0000\n"
11+
"Last-Translator: \n"
12+
"Language-Team: \n"
13+
"MIME-Version: 1.0\n"
14+
"Content-Type: text/plain; charset=UTF-8\n"
15+
"Content-Transfer-Encoding: \n"
16+
"Plural-Forms: \n"
17+
18+
#. module: sale_order_net_price_control
19+
#: model:ir.model.fields,field_description:sale_order_net_price_control.field_product_product__admits_offer_prices
20+
#: model:ir.model.fields,field_description:sale_order_net_price_control.field_product_template__admits_offer_prices
21+
#: model:ir.model.fields,field_description:sale_order_net_price_control.field_sale_order_line__admits_offer_prices
22+
msgid "Admits Offer Prices"
23+
msgstr "Admite precios de oferta"
24+
25+
#. module: sale_order_net_price_control
26+
#: model:ir.model,name:sale_order_net_price_control.model_res_company
27+
msgid "Companies"
28+
msgstr "Compañías"
29+
30+
#. module: sale_order_net_price_control
31+
#: model:ir.model,name:sale_order_net_price_control.model_res_config_settings
32+
msgid "Config Settings"
33+
msgstr "Ajustes de configuración"
34+
35+
#. module: sale_order_net_price_control
36+
#: model:ir.model.fields,field_description:sale_order_net_price_control.field_res_company__lowest_pricelist_id
37+
#: model:ir.model.fields,field_description:sale_order_net_price_control.field_res_config_settings__lowest_pricelist_id
38+
#: model_terms:ir.ui.view,arch_db:sale_order_net_price_control.res_config_settings_view_form
39+
msgid "MAYM pricelist"
40+
msgstr "Tarifa MAYM"
41+
42+
#. module: sale_order_net_price_control
43+
#. odoo-python
44+
#: code:addons/sale_order_net_price_control/models/sale_order_line.py:0
45+
#: code:addons/slv-sale6/sale_order_net_price_control/models/sale_order_line.py:0
46+
#, python-format
47+
msgid "Net price of '%s', '%.2f' must be higher that the MAYM pricelist '%s'"
48+
msgstr "El precio neto de '%s', '%.2f' debe ser mayor que el precio de la tarifa MAYM '%s'"
49+
50+
#. module: sale_order_net_price_control
51+
#. odoo-python
52+
#: code:addons/sale_order_net_price_control/models/sale_order_line.py:0
53+
#: code:addons/slv-sale6/sale_order_net_price_control/models/sale_order_line.py:0
54+
#, python-format
55+
msgid ""
56+
"Net price of '%s', '%.2f' must be higher that the last purchase invoice "
57+
"price '%s'"
58+
msgstr ""
59+
"El precio neto de '%s', '%.2f' debe ser mayor que el precio de la última factura de compra "
60+
"'%s'"
61+
62+
#. module: sale_order_net_price_control
63+
#: model:ir.model.fields,field_description:sale_order_net_price_control.field_sale_order_line__offer_price
64+
msgid "Offer Price"
65+
msgstr "Precio de oferta"
66+
67+
#. module: sale_order_net_price_control
68+
#: model:ir.model,name:sale_order_net_price_control.model_product_template
69+
msgid "Product"
70+
msgstr "Producto"
71+
72+
#. module: sale_order_net_price_control
73+
#: model_terms:ir.ui.view,arch_db:sale_order_net_price_control.res_config_settings_view_form
74+
msgid "Reference base pricelist for minimum price control"
75+
msgstr "Tarifa base de referencia para control de precios mínimos"
76+
77+
#. module: sale_order_net_price_control
78+
#: model:ir.model,name:sale_order_net_price_control.model_sale_order_line
79+
msgid "Sales Order Line"
80+
msgstr "Línea de pedido de venta"
81+
82+
#. module: sale_order_net_price_control
83+
#: model:res.groups,name:sale_order_net_price_control.group_sale_at_any_price
84+
msgid "Sell Products at Any Price"
85+
msgstr "Vender productos a cualquier precio"
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Translation of Odoo Server.
2+
# This file contains the translation of the following modules:
3+
# * sale_order_net_price_control
4+
#
5+
msgid ""
6+
msgstr ""
7+
"Project-Id-Version: Odoo Server 17.0\n"
8+
"Report-Msgid-Bugs-To: \n"
9+
"POT-Creation-Date: 2025-06-09 12:33+0000\n"
10+
"PO-Revision-Date: 2025-06-09 12:33+0000\n"
11+
"Last-Translator: \n"
12+
"Language-Team: \n"
13+
"MIME-Version: 1.0\n"
14+
"Content-Type: text/plain; charset=UTF-8\n"
15+
"Content-Transfer-Encoding: \n"
16+
"Plural-Forms: \n"
17+
18+
#. module: sale_order_net_price_control
19+
#: model:ir.model.fields,field_description:sale_order_net_price_control.field_product_product__admits_offer_prices
20+
#: model:ir.model.fields,field_description:sale_order_net_price_control.field_product_template__admits_offer_prices
21+
#: model:ir.model.fields,field_description:sale_order_net_price_control.field_sale_order_line__admits_offer_prices
22+
msgid "Admits Offer Prices"
23+
msgstr ""
24+
25+
#. module: sale_order_net_price_control
26+
#: model:ir.model,name:sale_order_net_price_control.model_res_company
27+
msgid "Companies"
28+
msgstr ""
29+
30+
#. module: sale_order_net_price_control
31+
#: model:ir.model,name:sale_order_net_price_control.model_res_config_settings
32+
msgid "Config Settings"
33+
msgstr ""
34+
35+
#. module: sale_order_net_price_control
36+
#: model:ir.model.fields,field_description:sale_order_net_price_control.field_res_company__lowest_pricelist_id
37+
#: model:ir.model.fields,field_description:sale_order_net_price_control.field_res_config_settings__lowest_pricelist_id
38+
#: model_terms:ir.ui.view,arch_db:sale_order_net_price_control.res_config_settings_view_form
39+
msgid "MAYM pricelist"
40+
msgstr ""
41+
42+
#. module: sale_order_net_price_control
43+
#. odoo-python
44+
#: code:addons/sale_order_net_price_control/models/sale_order_line.py:0
45+
#: code:addons/slv-sale6/sale_order_net_price_control/models/sale_order_line.py:0
46+
#, python-format
47+
msgid "Net price of '%s', '%.2f' must be higher that the MAYM pricelist '%s'"
48+
msgstr ""
49+
50+
#. module: sale_order_net_price_control
51+
#. odoo-python
52+
#: code:addons/sale_order_net_price_control/models/sale_order_line.py:0
53+
#: code:addons/slv-sale6/sale_order_net_price_control/models/sale_order_line.py:0
54+
#, python-format
55+
msgid ""
56+
"Net price of '%s', '%.2f' must be higher that the last purchase invoice "
57+
"price '%s'"
58+
msgstr ""
59+
60+
#. module: sale_order_net_price_control
61+
#: model:ir.model.fields,field_description:sale_order_net_price_control.field_sale_order_line__offer_price
62+
msgid "Offer Price"
63+
msgstr ""
64+
65+
#. module: sale_order_net_price_control
66+
#: model:ir.model,name:sale_order_net_price_control.model_product_template
67+
msgid "Product"
68+
msgstr ""
69+
70+
#. module: sale_order_net_price_control
71+
#: model_terms:ir.ui.view,arch_db:sale_order_net_price_control.res_config_settings_view_form
72+
msgid "Reference base pricelist for minimum price control"
73+
msgstr ""
74+
75+
#. module: sale_order_net_price_control
76+
#: model:ir.model,name:sale_order_net_price_control.model_sale_order_line
77+
msgid "Sales Order Line"
78+
msgstr ""
79+
80+
#. module: sale_order_net_price_control
81+
#: model:res.groups,name:sale_order_net_price_control.group_sale_at_any_price
82+
msgid "Sell Products at Any Price"
83+
msgstr ""
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from . import sale_order_line
2+
from . import res_company
3+
from . import res_config_settings
4+
from . import product_template
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# © 2025 Solvos Consultoría Informática (<http://www.solvos.es>)
2+
# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html
3+
4+
from odoo import models, fields
5+
6+
7+
class ProductTemplate(models.Model):
8+
_inherit = "product.template"
9+
10+
admits_offer_prices = fields.Boolean()
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# © 2025 Solvos Consultoría Informática (<http://www.solvos.es>)
2+
# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html
3+
4+
from odoo import models, fields
5+
6+
7+
class ResCompany(models.Model):
8+
_inherit = "res.company"
9+
10+
lowest_pricelist_id = fields.Many2one(comodel_name='product.pricelist', string='MAYM pricelist')
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# © 2025 Solvos Consultoría Informática (<http://www.solvos.es>)
2+
# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html
3+
4+
from odoo import models, fields
5+
6+
7+
class ResConfigSettings(models.TransientModel):
8+
_inherit = 'res.config.settings'
9+
10+
lowest_pricelist_id = fields.Many2one(related="company_id.lowest_pricelist_id", readonly=False, string='MAYM pricelist')
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# © 2025 Solvos Consultoría Informática (<http://www.solvos.es>)
2+
# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html
3+
4+
from odoo import models, _, fields
5+
from odoo.exceptions import ValidationError
6+
7+
8+
class SaleOrderLine(models.Model):
9+
_inherit = "sale.order.line"
10+
11+
offer_price = fields.Boolean()
12+
admits_offer_prices = fields.Boolean(related='product_template_id.admits_offer_prices', store=True)
13+
14+
# FASE 1 desarrollo
15+
def _compute_amount(self):
16+
super()._compute_amount()
17+
# FASE 3 DEL DESARROLLO
18+
for record in self:
19+
if not self.env.user.has_group('sale_order_net_price_control.group_sale_at_any_price'):
20+
if record.product_id and record.product_uom_qty > 0:
21+
22+
net_price_sale = record.price_subtotal / record.product_uom_qty
23+
24+
purchase_invoice = self.env['account.move'].search([
25+
('invoice_line_ids.product_id', '=', record.product_id.id),
26+
('invoice_line_ids.quantity', '>', 0),
27+
('move_type', '=', 'in_invoice'), # factura de compra
28+
('state', '=', 'posted'), # confirmado
29+
], limit=1, order='name desc')
30+
31+
if purchase_invoice:
32+
invoice_line = purchase_invoice.invoice_line_ids.filtered(
33+
lambda x: x.product_id.id == self.product_id.id
34+
)
35+
net_price_purchase = invoice_line.price_subtotal / invoice_line.quantity
36+
37+
if net_price_sale <= net_price_purchase:
38+
raise ValidationError(_(
39+
"Net price of '%s', '%.2f' must be higher that the last purchase invoice price '%s'"
40+
)%(record.product_template_id.name,net_price_sale,invoice_line.price_unit) )
41+
# FASE 4 DEL DESARROLLO - parte 1
42+
if not self.offer_price:
43+
if net_price_sale <= self._default_lowest_pricelist_id():
44+
raise ValidationError(_(
45+
"Net price of '%s', '%.2f' must be higher that the MAYM pricelist '%s'"
46+
)%(record.product_template_id.name,net_price_sale,self._default_lowest_pricelist_id()) )
47+
48+
# FASE 2 desarrollo
49+
def _default_lowest_pricelist_id(self):
50+
company = self.env["res.company"].browse(
51+
self._context.get("company_id", self.env.user.company_id.id)
52+
)
53+
return company.lowest_pricelist_id.item_ids._compute_price(self.product_template_id, self.product_uom_qty, self.product_uom, self.order_id.date_order, self.currency_id)

0 commit comments

Comments
 (0)