[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:
parent
449bb75bb6
commit
b31df7b9d8
2 changed files with 85 additions and 9 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue