[IMP] product_pricelist_total_margin: Add margin_type selector (Markup vs Commercial Margin)

- Add margin_type field to choose between calculation methods:
  * Markup (on cost): PVP = Cost × (1 + markup%)
  * Commercial Margin (on PVP): PVP = Cost / (1 - margin%)
- Update _compute_price() to apply correct formula based on margin_type
- Add safety cap for commercial margin >= 100% (caps at 99%)
- Add detailed logging for both calculation types
- Update views to show margin_type field when use_total_margin is enabled
- Add 2 new tests to validate both calculation methods:
  * test_total_margin_markup_type: validates markup formula
  * test_total_margin_commercial_margin_type: validates commercial margin formula
- All 11 tests passing (9 existing + 2 new)

Example with Commercial Margin:
Base: 4.68€, Total Margin: 20%
- Markup: 4.68 × 1.20 = 5.616€ (margin = 16.67% of PVP)
- Commercial Margin: 4.68 / 0.80 = 5.85€ (margin = 20% of PVP) ✓
This commit is contained in:
snt 2026-02-21 18:54:38 +01:00
parent cafa19ffea
commit 0f239601ce
4 changed files with 107 additions and 11 deletions

View file

@ -351,3 +351,63 @@ 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}%",
)