[DOC] product_pricelist_total_margin: Update docs and version to 18.0.1.2.0

Changelog:
- Document global_margin_type feature for independent global limits calculation
- Update version from 18.0.1.1.0 to 18.0.1.2.0
- Update test coverage count (11 → 13 tests)
- Update manifest summary to include global limits enforcement
This commit is contained in:
snt 2026-02-21 19:19:25 +01:00
parent 449bb75bb6
commit b31df7b9d8
2 changed files with 85 additions and 9 deletions

View file

@ -45,13 +45,16 @@ Effective margin: (5.85 - 4.68) / 5.85 = 20%
## Features ## Features
- ✅ **Additive margin calculation** across chained pricelists - ✅ **Additive margin calculation** across chained pricelists
- ✅ **Two calculation methods:** - ✅ **Two calculation methods per pricelist:**
- **Markup (on cost):** `PVP = Cost × (1 + markup%)` - **Markup (on cost):** `PVP = Cost × (1 + markup%)`
- **Commercial Margin (on PVP):** `PVP = Cost / (1 - margin%)` - **Commercial Margin (on PVP):** `PVP = Cost / (1 - margin%)`
- ✅ **Global min/max margin limits** with independent calculation type:
- Configure minimum and maximum margin percentages
- Choose between Markup or Commercial Margin interpretation for limits
- Limits can use different calculation method than individual pricelists
- ✅ **Opt-in via checkbox** - doesn't affect existing pricelists - ✅ **Opt-in via checkbox** - doesn't affect existing pricelists
- ✅ **Compatible with custom bases** (`last_purchase_price` from `product_sale_price_from_pricelist`) - ✅ **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) - ✅ **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 - ✅ **Multi-level chains** - works with 2+ pricelists in sequence
- ✅ **Currency conversion** - handles multi-currency scenarios - ✅ **Currency conversion** - handles multi-currency scenarios
- ✅ **Detailed logging** - debug pricing calculations easily - ✅ **Detailed logging** - debug pricing calculations easily
@ -105,7 +108,24 @@ Create a pricelist that chains to the base one:
- **Markup (on cost)** - Default, calculates `PVP = Cost × (1 + markup%)` - **Markup (on cost)** - Default, calculates `PVP = Cost × (1 + markup%)`
- **Commercial Margin (on PVP)** - Calculates `PVP = Cost / (1 - margin%)` - **Commercial Margin (on PVP)** - Calculates `PVP = Cost / (1 - margin%)`
### Step 3: Assign to Products ### Step 3: Configure Global Limits (Optional)
Set enterprise-wide minimum and maximum margins:
1. Go to **Settings > Sales > Total Margin Limits**
2. Configure:
- **Minimum Margin (%):** E.g., 10% (no product can have less margin)
- **Maximum Margin (%):** E.g., 50% (no product can have more margin)
- **Calculation Method:** Choose how these percentages are interpreted:
- **Markup (on cost)** - Default, `Min: Cost × 1.10`, `Max: Cost × 1.50`
- **Commercial Margin (on PVP)** - `Min: Cost / 0.90`, `Max: Cost / 0.50`
**Important:** The global limits calculation method is independent from individual pricelist items. You can have:
- Items using Markup, with global limits as Commercial Margin
- Items using Commercial Margin, with global limits as Markup
- Both using the same method
### Step 4: Assign to Products
- For automatic price calculation, configure the pricelist in **Settings > Sales > Automatic Price Configuration** - For automatic price calculation, configure the pricelist in **Settings > Sales > Automatic Price Configuration**
- Or assign the pricelist to specific customers/partners - Or assign the pricelist to specific customers/partners
@ -165,6 +185,30 @@ Option 2 - Commercial Margin:
- **Visibility:** Only shown when `use_total_margin = True` - **Visibility:** Only shown when `use_total_margin = True`
- **Purpose:** Choose the calculation method for the total margin - **Purpose:** Choose the calculation method for the total margin
### Global Configuration Fields
- **Model:** `res.config.settings`
#### `total_margin_min_percent` (Float)
- **Default:** 0.0 (disabled)
- **Purpose:** Minimum margin percentage for all products using Total Margin Mode
- **Interpretation:** Depends on `global_margin_type` setting
#### `total_margin_max_percent` (Float)
- **Default:** 0.0 (disabled)
- **Purpose:** Maximum margin percentage for all products using Total Margin Mode
- **Interpretation:** Depends on `global_margin_type` setting
#### `global_margin_type` (Selection)
- **Default:** `'markup'`
- **Options:**
- `'markup'` - Limits are interpreted as markup on cost
- `'margin'` - Limits are interpreted as commercial margin on PVP
- **Purpose:** Define how min/max percentages are converted to prices
- **Example:** Min 25%:
- Markup: `Min Price = Cost × 1.25`
- Commercial Margin: `Min Price = Cost / 0.75` (ensures 25% margin on final price)
### Methods ### Methods
#### `_get_base_price_and_margins(product, quantity, uom, date, currency)` #### `_get_base_price_and_margins(product, quantity, uom, date, currency)`
@ -191,16 +235,33 @@ Applies additional formula options:
- `price_min_margin`: Enforce minimum margin - `price_min_margin`: Enforce minimum margin
- `price_max_margin`: Enforce maximum margin - `price_max_margin`: Enforce maximum margin
#### `_apply_global_margin_limits(price, base_price)`
Enforces global minimum and maximum margins configured in Settings:
1. Reads `total_margin_min_percent`, `total_margin_max_percent`, and `global_margin_type` from configuration
2. Calculates min/max allowed prices based on `global_margin_type`:
- **Markup:** `Price = Cost × (1 + margin%)`
- **Commercial Margin:** `Price = Cost / (1 - margin%)`
3. Adjusts price if it falls outside configured limits
4. Returns adjusted price
**Note:** Global limits are applied AFTER the item's margin calculation, and can use a different margin type than the item itself.
**Example:**
- Item calculates 15% markup → Price = 100 × 1.15 = 115€
- Global min is 20% commercial margin → Min Price = 100 / 0.80 = 125€
- Result: Price adjusted from 115€ to 125€ to meet global minimum
#### `_compute_price(product, quantity, uom, date, currency)` [OVERRIDE] #### `_compute_price(product, quantity, uom, date, currency)` [OVERRIDE]
Main override that: Main override that:
1. Checks if `use_total_margin=True` and conditions are met 1. Checks if `use_total_margin=True` and conditions are met
2. Calls helper methods to get base price and margins 2. Calls helper methods to get base price and margins
3. Sums margins additively: `total_margin = sum(margins)` 3. Sums margins additively: `total_margin = sum(margins)`
4. Applies global min/max margin limits if configured 4. Applies total margin using item's selected method:
5. Applies total margin using selected method:
- **Markup:** `price = base_price * (1 + total_margin / 100)` - **Markup:** `price = base_price * (1 + total_margin / 100)`
- **Commercial Margin:** `price = base_price / (1 - total_margin / 100)` - **Commercial Margin:** `price = base_price / (1 - total_margin / 100)`
5. Applies global min/max margin limits (may use different margin type)
6. Applies formula extras 6. Applies formula extras
7. Falls back to standard behavior if conditions not met 7. Falls back to standard behavior if conditions not met
@ -240,12 +301,14 @@ docker-compose run odoo odoo -d odoo --test-enable --stop-after-init -u product_
- ✅ Total margin with Markup calculation - ✅ Total margin with Markup calculation
- ✅ Total margin with Commercial Margin calculation - ✅ Total margin with Commercial Margin calculation
- ✅ Formula extras (round, surcharge, min/max) - ✅ Formula extras (round, surcharge, min/max)
- ✅ Global min/max margin limits - ✅ Global min/max margin limits with Markup type
- ✅ Global min/max margin limits with Commercial Margin type
- ✅ Mixed margin types (item vs global with different types)
- ✅ 3-level pricelist chains - ✅ 3-level pricelist chains
- ✅ Different base types (last_purchase_price, list_price) - ✅ Different base types (last_purchase_price, list_price)
- ✅ Currency conversions - ✅ Currency conversions
**Total:** 11 tests, all passing **Total:** 13 tests, all passing
## Compatibility ## Compatibility
@ -323,6 +386,19 @@ AGPL-3.0 or later
## Changelog ## Changelog
### 18.0.1.2.0 (2026-02-21)
- **[IMP]** Add `global_margin_type` field for independent global limits calculation
- Configure how min/max percentages are interpreted (Markup or Commercial Margin)
- Global limits can use different calculation method than individual pricelist items
- Enables flexible business rules (e.g., items use Markup, global limits enforce Commercial Margin)
- Refactor `_apply_global_margin_limits()` to work with prices instead of percentages
- Now calculates min/max prices based on `global_margin_type`
- Applied after item's margin calculation, not before
- Add 2 new tests for global limits with Commercial Margin type
- Update configuration UI with global margin type selector
- Total: 13 tests passing
### 18.0.1.1.0 (2026-02-21) ### 18.0.1.1.0 (2026-02-21)
- **[IMP]** Add `margin_type` field to choose between calculation methods - **[IMP]** Add `margin_type` field to choose between calculation methods

View file

@ -2,9 +2,9 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{ # noqa: B018 { # noqa: B018
"name": "Product Pricelist Total Margin", "name": "Product Pricelist Total Margin",
"version": "18.0.1.1.0", "version": "18.0.1.2.0",
"category": "Sales/Products", "category": "Sales/Products",
"summary": "Calculate total margin additively with Markup or Commercial Margin methods", "summary": "Calculate total margin additively with Markup or Commercial Margin methods, enforce global limits",
"author": "Odoo Community Association (OCA), Criptomart", "author": "Odoo Community Association (OCA), Criptomart",
"website": "https://git.criptomart.net/criptomart/addons-cm", "website": "https://git.criptomart.net/criptomart/addons-cm",
"license": "AGPL-3", "license": "AGPL-3",