Compare commits

..

No commits in common. "55406ca22de1733746df36bff0e9d4e8d5fe7aa8" and "cafa19ffeab6a842187b991f22a21f956a1b88dc" have entirely different histories.

4 changed files with 11 additions and 107 deletions

View file

@ -5,8 +5,8 @@
"version": "18.0.1.0.0",
"category": "Sales/Products",
"summary": "Calculate total margin additively instead of compounding in chained pricelists",
"author": "Odoo Community Association (OCA), Criptomart",
"website": "https://git.criptomart.net/criptomart/addons-cm",
"author": "Odoo Community Association (OCA), Kidekoop",
"website": "https://github.com/kidekoop",
"license": "AGPL-3",
"depends": [
"product",

View file

@ -23,20 +23,6 @@ class ProductPricelistItem(models.Model):
"- Total (this option): 100 * (1 + 0.20) = 120€",
)
margin_type = fields.Selection(
selection=[
("markup", "Markup (on cost)"),
("margin", "Commercial Margin (on PVP)"),
],
string="Calculation Method",
default="markup",
help="Type of margin calculation:\n"
"- Markup: PVP = Cost × (1 + markup%). Margin is calculated on cost.\n"
" Example: Cost 100€, Markup 25% → PVP = 125€\n\n"
"- Commercial Margin: PVP = Cost / (1 - margin%). Margin is calculated on PVP.\n"
" Example: Cost 100€, Margin 20% → PVP = 125€ (margin = 25€/125€ = 20%)",
)
def _get_base_price_and_margins(self, product, quantity, uom, date, currency):
"""
Traverse the pricelist chain to get the original base price and collect
@ -381,28 +367,10 @@ class ProductPricelistItem(models.Model):
# Apply global min/max margin limits
total_margin = self._apply_global_margin_limits(total_margin, base_price)
# Apply total margin to base price using selected margin type
margin_type = self.margin_type or "markup"
if margin_type == "margin":
# Commercial Margin: PVP = Cost / (1 - margin%)
if total_margin >= 100:
_logger.warning(
"[TOTAL MARGIN] Commercial margin %.2f%% >= 100%%, capping at 99%%",
total_margin,
)
total_margin = 99.0
price = base_price / (1 - total_margin / 100)
_logger.info(
"[TOTAL MARGIN] Commercial Margin: Base %.2f / (1 - %.2f%%) = %.2f",
base_price,
total_margin,
price,
)
else:
# Markup: PVP = Cost × (1 + markup%)
# Apply total margin to base price
price = base_price * (1 + total_margin / 100)
_logger.info(
"[TOTAL MARGIN] Markup: Base %.2f * (1 + %.2f%%) = %.2f",
"[TOTAL MARGIN] Base price %.2f * (1 + %.2f%%) = %.2f",
base_price,
total_margin,
price,

View file

@ -351,63 +351,3 @@ class TestTotalMargin(TransactionCase):
self.env["ir.config_parameter"].sudo().set_param(
"product_pricelist_total_margin.max_percent", "0.0"
)
def test_total_margin_markup_type(self):
"""Test total margin with Markup calculation (on cost)."""
# Enable total margin and set to markup
self.item_base.use_total_margin = True
self.item_chained.use_total_margin = True
self.item_chained.margin_type = "markup"
price = self.pricelist_chained._get_product_price(
product=self.product,
quantity=1.0,
)
# Expected calculation (Markup):
# Base: 4.68
# Total margin: -5% + 25% = 20%
# Markup formula: PVP = Cost × (1 + markup%)
# Final: 4.68 * (1 + 0.20) = 5.616
expected_price = 4.68 * 1.20
self.assertAlmostEqual(
price,
expected_price,
places=2,
msg=f"Markup calculation should give {expected_price}, got {price}",
)
def test_total_margin_commercial_margin_type(self):
"""Test total margin with Commercial Margin calculation (on PVP)."""
# Enable total margin and set to commercial margin
self.item_base.use_total_margin = True
self.item_chained.use_total_margin = True
self.item_chained.margin_type = "margin"
price = self.pricelist_chained._get_product_price(
product=self.product,
quantity=1.0,
)
# Expected calculation (Commercial Margin):
# Base: 4.68
# Total margin: -5% + 25% = 20%
# Commercial margin formula: PVP = Cost / (1 - margin%)
# Final: 4.68 / (1 - 0.20) = 5.85
expected_price = 4.68 / 0.80
self.assertAlmostEqual(
price,
expected_price,
places=2,
msg=f"Commercial margin calculation should give {expected_price}, got {price}",
)
# Verify the commercial margin is indeed 20%
# Commercial margin = (PVP - Cost) / PVP
commercial_margin = (price - 4.68) / price
self.assertAlmostEqual(
commercial_margin,
0.20,
places=4,
msg=f"Commercial margin should be 20%, got {commercial_margin * 100}%",
)

View file

@ -5,16 +5,12 @@
<field name="model">product.pricelist.item</field>
<field name="inherit_id" ref="product.product_pricelist_item_form_view" />
<field name="arch" type="xml">
<!-- Add use_total_margin and margin_type fields after compute_price -->
<!-- Add use_total_margin field after compute_price -->
<field name="compute_price" position="after">
<field
name="use_total_margin"
invisible="compute_price != 'formula' or base != 'pricelist'"
/>
<field
name="margin_type"
invisible="not use_total_margin"
/>
</field>
</field>
</record>