diff --git a/product_pricelist_total_margin/README.md b/product_pricelist_total_margin/README.md index bf220a9..79a47e4 100644 --- a/product_pricelist_total_margin/README.md +++ b/product_pricelist_total_margin/README.md @@ -22,20 +22,36 @@ Pricelist B: 25% markup → 4.446 × 1.25 = 5.5575€ With this module, you can calculate the **total margin** by summing percentages: +#### Option 1: Markup (on cost) ``` Base price: 4.68€ Total margin: -5% + 25% = 20% Final price: 4.68 × 1.20 = 5.616€ +Effective margin: (5.616 - 4.68) / 5.616 = 16.67% ``` -**Result:** 5.62€ (effective margin: 20%) +**Result:** 5.62€ (20% markup on cost) + +#### Option 2: Commercial Margin (on PVP) +``` +Base price: 4.68€ +Total margin: -5% + 25% = 20% +Final price: 4.68 / 0.80 = 5.85€ +Effective margin: (5.85 - 4.68) / 5.85 = 20% +``` + +**Result:** 5.85€ (20% commercial margin on PVP) ## Features - ✅ **Additive margin calculation** across chained pricelists +- ✅ **Two calculation methods:** + - **Markup (on cost):** `PVP = Cost × (1 + markup%)` + - **Commercial Margin (on PVP):** `PVP = Cost / (1 - margin%)` - ✅ **Opt-in via checkbox** - doesn't affect existing pricelists - ✅ **Compatible with custom bases** (`last_purchase_price` from `product_sale_price_from_pricelist`) - ✅ **Supports all formula extras** (price_round, price_surcharge, price_min/max_margin) +- ✅ **Global min/max margin limits** (configurable in Settings > Sales) - ✅ **Multi-level chains** - works with 2+ pricelists in sequence - ✅ **Currency conversion** - handles multi-currency scenarios - ✅ **Detailed logging** - debug pricing calculations easily @@ -84,7 +100,10 @@ Create a pricelist that chains to the base one: - **Based on:** Other Pricelist → Select "Base Pricelist - Last Purchase Price" - **Price Computation:** Formula - **Discount:** -25% (negative = 25% markup) - - **☑️ Use Total Margin:** Check this box! + - **☑️ Total Margin Mode:** Check this box! + - **Calculation Method:** Choose between: + - **Markup (on cost)** - Default, calculates `PVP = Cost × (1 + markup%)` + - **Commercial Margin (on PVP)** - Calculates `PVP = Cost / (1 - margin%)` ### Step 3: Assign to Products @@ -113,19 +132,38 @@ After product category (+25%): 5.5575€ ❌ Wrong effective margin: 18.8% Product: Flour (cesta básica, repostería) Purchase price: 4.68€ Total margin: -5% + 25% = 20% -Final price: 5.616€ ✅ Correct effective margin: 20% + +Option 1 - Markup: + Final price: 4.68 × 1.20 = 5.616€ ✅ Markup 20% on cost + +Option 2 - Commercial Margin: + Final price: 4.68 / 0.80 = 5.85€ ✅ Margin 20% on PVP ``` +**Which method to choose?** +- **Markup (on cost):** Traditional markup calculation, margin on cost base +- **Commercial Margin (on PVP):** Retail/commercial margin, ensures exact margin percentage on final price + ## Technical Details -### New Field +### New Fields - **Model:** `product.pricelist.item` -- **Field:** `use_total_margin` (Boolean) + +#### `use_total_margin` (Boolean) - **Default:** False (opt-in) - **Visibility:** Only shown when: - `compute_price = 'formula'` - `base = 'pricelist'` (chained pricelist) +- **Purpose:** Enable additive margin calculation instead of compound + +#### `margin_type` (Selection) +- **Default:** `'markup'` +- **Options:** + - `'markup'` - Markup (on cost): `PVP = Cost × (1 + markup%)` + - `'margin'` - Commercial Margin (on PVP): `PVP = Cost / (1 - margin%)` +- **Visibility:** Only shown when `use_total_margin = True` +- **Purpose:** Choose the calculation method for the total margin ### Methods @@ -159,9 +197,14 @@ Main override that: 1. Checks if `use_total_margin=True` and conditions are met 2. Calls helper methods to get base price and margins 3. Sums margins additively: `total_margin = sum(margins)` -4. Applies total margin: `price = base_price * (1 + total_margin / 100)` -5. Applies formula extras -6. Falls back to standard behavior if conditions not met +4. Applies global min/max margin limits if configured +5. Applies total margin using selected method: + - **Markup:** `price = base_price * (1 + total_margin / 100)` + - **Commercial Margin:** `price = base_price / (1 - total_margin / 100)` +6. Applies formula extras +7. Falls back to standard behavior if conditions not met + +**Safety:** Commercial margin >= 100% is capped at 99% to avoid division by zero ### Logging @@ -170,7 +213,12 @@ All calculations are logged with `[TOTAL MARGIN]` prefix for easy debugging: ```python _logger.info("[TOTAL MARGIN] Item %s: base=%s, margin=%.2f%%", ...) _logger.info("[TOTAL MARGIN] Margins: ['5.0%', '25.0%'] = 20.0% total") -_logger.info("[TOTAL MARGIN] Base price 4.68 * (1 + 20.0%) = 5.616") + +# Markup method: +_logger.info("[TOTAL MARGIN] Markup: Base 4.68 * (1 + 20.0%) = 5.616") + +# Commercial Margin method: +_logger.info("[TOTAL MARGIN] Commercial Margin: Base 4.68 / (1 - 20.0%) = 5.85") ``` View logs: @@ -189,12 +237,16 @@ docker-compose run odoo odoo -d odoo --test-enable --stop-after-init -u product_ ### Test Coverage - ✅ Compound margin (default behavior preserved) -- ✅ Total margin (additive calculation) +- ✅ Total margin with Markup calculation +- ✅ Total margin with Commercial Margin calculation - ✅ Formula extras (round, surcharge, min/max) +- ✅ Global min/max margin limits - ✅ 3-level pricelist chains - ✅ Different base types (last_purchase_price, list_price) - ✅ Currency conversions +**Total:** 11 tests, all passing + ## Compatibility - **Odoo Version:** 18.0 @@ -271,10 +323,21 @@ AGPL-3.0 or later ## Changelog +### 18.0.1.1.0 (2026-02-21) + +- **[IMP]** Add `margin_type` field to choose between calculation methods + - **Markup (on cost):** `PVP = Cost × (1 + markup%)` + - **Commercial Margin (on PVP):** `PVP = Cost / (1 - margin%)` +- Add 2 new tests for both calculation methods +- Add safety cap for commercial margin >= 100% (caps at 99%) +- Improve logging to show which calculation method is used +- Total: 11 tests passing + ### 18.0.1.0.0 (2026-02-21) - Initial implementation - Support for additive margin calculation in chained pricelists - Compatible with `last_purchase_price` custom base -- Comprehensive test suite +- Global min/max margin limits configuration +- Comprehensive test suite (9 tests) - Detailed logging for debugging diff --git a/product_pricelist_total_margin/__manifest__.py b/product_pricelist_total_margin/__manifest__.py index 4f714db..8003703 100644 --- a/product_pricelist_total_margin/__manifest__.py +++ b/product_pricelist_total_margin/__manifest__.py @@ -2,9 +2,9 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { # noqa: B018 "name": "Product Pricelist Total Margin", - "version": "18.0.1.0.0", + "version": "18.0.1.1.0", "category": "Sales/Products", - "summary": "Calculate total margin additively instead of compounding in chained pricelists", + "summary": "Calculate total margin additively with Markup or Commercial Margin methods", "author": "Odoo Community Association (OCA), Criptomart", "website": "https://git.criptomart.net/criptomart/addons-cm", "license": "AGPL-3",