product_sale_price_from_pricelist: allow to configure rounding of list_price_theoritical after taxes. Add the configured pricelist margin in product form view

This commit is contained in:
Luis 2026-04-02 14:23:36 +02:00
parent 91971ae56b
commit 524e3d643c
5 changed files with 126 additions and 4 deletions

View file

@ -17,6 +17,33 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.6\n"
#. module: product_sale_price_from_pricelist
#: model:ir.model.fields,field_description:product_sale_price_from_pricelist.field_product_pricelist__price_rounding_step
msgid "Sale Price Rounding Step"
msgstr "Redondeo del precio de venta"
#. module: product_sale_price_from_pricelist
#: model:ir.model.fields,help:product_sale_price_from_pricelist.field_product_pricelist__price_rounding_step
msgid "Round the theoretical sale price (after taxes) to the nearest multiple of this value. Set to 0.00 to disable rounding (e.g. 0.05 rounds to the nearest 5 cents)."
msgstr "Redondea el precio de venta teórico (con impuestos) al múltiplo más cercano de este valor. Pon 0,00 para desactivar el redondeo (p. ej. 0,05 redondea al céntimo más cercano de 5)."
#. module: product_sale_price_from_pricelist
#: model:ir.model.fields,field_description:product_sale_price_from_pricelist.field_product_product__configured_sale_margin
#: model:ir.model.fields,field_description:product_sale_price_from_pricelist.field_product_template__configured_sale_margin
msgid "Configured sale margin (%)"
msgstr "Margen de venta configurado (%)"
#. module: product_sale_price_from_pricelist
#: model:ir.model.fields,help:product_sale_price_from_pricelist.field_product_product__configured_sale_margin
#: model:ir.model.fields,help:product_sale_price_from_pricelist.field_product_template__configured_sale_margin
msgid "Effective margin configured by the automatic pricelist chain for this product. It reflects tariff configuration, not the current manual sale price."
msgstr "Margen efectivo configurado por la cadena de tarifas automáticas para este producto. Refleja la configuración de tarifas, no el precio de venta manual actual."
#. module: product_sale_price_from_pricelist
#: model_terms:ir.ui.view,arch_db:product_sale_price_from_pricelist.product_pricelist_view_price_rounding
msgid "Sale Price Rounding"
msgstr "Redondeo del precio de venta"
#. module: product_sale_price_from_pricelist
#: model:ir.model.fields,help:product_sale_price_from_pricelist.field_product_pricelist_item__base
msgid ""

View file

@ -15,6 +15,33 @@ msgstr ""
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: product_sale_price_from_pricelist
#: model:ir.model.fields,field_description:product_sale_price_from_pricelist.field_product_pricelist__price_rounding_step
msgid "Sale Price Rounding Step"
msgstr ""
#. module: product_sale_price_from_pricelist
#: model:ir.model.fields,help:product_sale_price_from_pricelist.field_product_pricelist__price_rounding_step
msgid "Round the theoretical sale price (after taxes) to the nearest multiple of this value. Set to 0.00 to disable rounding (e.g. 0.05 rounds to the nearest 5 cents)."
msgstr ""
#. module: product_sale_price_from_pricelist
#: model:ir.model.fields,field_description:product_sale_price_from_pricelist.field_product_product__configured_sale_margin
#: model:ir.model.fields,field_description:product_sale_price_from_pricelist.field_product_template__configured_sale_margin
msgid "Configured sale margin (%)"
msgstr ""
#. module: product_sale_price_from_pricelist
#: model:ir.model.fields,help:product_sale_price_from_pricelist.field_product_product__configured_sale_margin
#: model:ir.model.fields,help:product_sale_price_from_pricelist.field_product_template__configured_sale_margin
msgid "Effective margin configured by the automatic pricelist chain for this product. It reflects tariff configuration, not the current manual sale price."
msgstr ""
#. module: product_sale_price_from_pricelist
#: model_terms:ir.ui.view,arch_db:product_sale_price_from_pricelist.product_pricelist_view_price_rounding
msgid "Sale Price Rounding"
msgstr ""
#. module: product_sale_price_from_pricelist
#: model:ir.model.fields,help:product_sale_price_from_pricelist.field_product_pricelist_item__base
msgid ""

View file

@ -4,7 +4,7 @@
# import logging
from odoo import api, models
from odoo import api, fields, models
# _logger = logging.getLogger(__name__)
@ -12,6 +12,14 @@ from odoo import api, models
class ProductPricelist(models.Model):
_inherit = "product.pricelist"
price_rounding_step = fields.Float(
string="Sale Price Rounding Step",
digits=(12, 2),
default=0.0,
help="Round the theoretical sale price (after taxes) to the nearest multiple of this value. "
"Set to 0.00 to disable rounding (e.g. 0.05 rounds to the nearest 5 cents).",
)
def _compute_price_rule(self, products, qty, uom=None, date=False, **kwargs):
ProductPricelistItem = self.env["product.pricelist.item"]
ProductProduct = self.env["product.product"]

View file

@ -45,6 +45,51 @@ class ProductTemplate(models.Model):
required=True,
company_dependent=True,
)
configured_sale_margin = fields.Float(
string="Configured sale margin (%)",
digits="Discount",
compute="_compute_configured_sale_margin",
help="Effective margin configured by the automatic pricelist chain for "
"this product. It reflects tariff configuration, not the current "
"manual sale price.",
)
def _compute_configured_sale_margin(self):
pricelist_id = (
self.env["ir.config_parameter"]
.sudo()
.get_param("product_sale_price_from_pricelist.product_pricelist_automatic")
or False
)
if not pricelist_id:
for template in self:
template.configured_sale_margin = 0.0
return
pricelist = self.env["product.pricelist"].browse(int(pricelist_id))
for template in self:
template.configured_sale_margin = 0.0
if (
not template.product_variant_id
or template.last_purchase_price_compute_type == "manual_update"
or not template.last_purchase_price_received
):
continue
# Get the final untaxed price after all chained pricelists are applied.
final_price = template.product_variant_id._get_price(
qty=1,
pricelist=pricelist,
)
untaxed_price = final_price.get(template.product_variant_id.id, {}).get(
"value"
)
if untaxed_price is None:
continue
template.configured_sale_margin = (
(untaxed_price / template.last_purchase_price_received) - 1.0
) * 100.0
def _compute_theoritical_price(self):
pricelist_obj = self.env["product.pricelist"]
@ -82,9 +127,10 @@ class ProductTemplate(models.Model):
tax_price["taxes"][0]["amount"]
+ partial_price[template.product_variant_id.id]["value"]
)
# Round to 0.05
if round(price_with_taxes % 0.05, 2) != 0:
price_with_taxes = round(price_with_taxes * 20) / 20
# Round to configurable step (defined on the pricelist)
step = pricelist.price_rounding_step
if step:
price_with_taxes = math.ceil(price_with_taxes / step) * step
template.write(
{

View file

@ -23,6 +23,7 @@
readonly="1"
/>
<field name="list_price_theoritical" readonly="1" />
<field name="configured_sale_margin" readonly="1" />
<field name="last_purchase_price_updated" readonly="1" />
</field>
</field>
@ -40,6 +41,19 @@
</field>
</record>
<record id="product_pricelist_view_price_rounding" model="ir.ui.view">
<field name="name">product.pricelist.view.price.rounding</field>
<field name="model">product.pricelist</field>
<field name="inherit_id" ref="product.product_pricelist_view" />
<field name="arch" type="xml">
<xpath expr="//page[@name='pricelist_config']//group[@name='pricelist_availability']" position="after">
<group name="pricelist_rounding" string="Sale Price Rounding">
<field name="price_rounding_step" />
</group>
</xpath>
</field>
</record>
<record id="res_config_settings_view_form_pricelists" model="ir.ui.view">
<field name="name">product.print.supermarket.res.config.settings.form</field>
<field name="model">res.config.settings</field>