[FIX] docs: correct four statements about variants, packagings and categories - #20071
Open
Natalie-the-technician wants to merge 4 commits into
Open
Natalie-the-technician wants to merge 4 commits into
Natalie-the-technician wants to merge 4 commits into
Conversation
The page stated that dynamic variants are created "only when corresponding attributes and values are added to a sales order". That is not accurate: product_template._create_product_variant() is called from four places outside of Sales as well. In odoo/odoo saas-19.4 (cbed569e): addons/sale/controllers/product_configurator.py:122 addons/sale_product_matrix/models/sale_order.py:62 addons/purchase_product_matrix/models/purchase.py:49 addons/point_of_sale/models/product_template.py:81 addons/website_sale/controllers/main.py:835 Verified on a database with no Sales module installed: enabling the purchase "Variant Grid Entry" setting and filling two cells of the grid created two dynamic variants. Odoo's own test asserts the same behaviour (addons/purchase_product_matrix/tests/test_purchase_matrix.py:20). The sentence about Inventory was added because the opposite was measured as well: on the same database, confirming a purchase order and validating the incoming receipt created no variant at all. Inventory has no field anywhere that selects a combination of attribute values.
The page claimed that packagings "are product specific, not generic" and that each one "must be configured on the individual product form". In saas-19.4 that is no longer the case. The model product.packaging no longer exists. Searched the whole odoo/odoo saas-19.4 tree (643 addons directories plus odoo/addons): grep -rn "_name = ['\"]product.packaging['\"]" addons/ odoo/ -> 0 hits A packaging is now an ordinary uom.uom record. The quantity it holds comes from relative_factor/factor on that single global record (addons/uom/models/uom_uom.py:17-43), and uom.uom has neither product_id nor company_id. Products only reference the packagings they may use, via product.template.uom_ids (labelled "Packagings", addons/product/models/product_template.py:159). Verified: two different products were given the same packaging and both referenced the same uom.uom record, so the same packaging cannot hold a different quantity per product. The only product-specific part is the barcode, held by the helper model product.uom, which has no quantity field at all (fields: allowed_uom_ids, barcode, company_id, product_id, uom_id).
The note told the reader to tick a "Product Packagings" checkbox in the
Inventory settings. No such setting exists in saas-19.4.
Read from a running saas-19.4 database: res.config.settings has no field
whose name contains "packaging" at all. The setting that reveals the
field is group_uom, labelled "Units of Measure & Packagings", implying
group uom.group_uom ("Manage Multiple Units of Measure"). The category
field itself carries groups="uom.group_uom" in view
stock.product_category_form_view_inherit.
The menu path was already correct: group_uom does appear in
stock.res_config_settings_view_form, inside the block titled "Products".
Also aligned the field name in the first sentence with the label the
field actually has: "Reserve Packagings", not "Reserve Packaging"
(product.category.packaging_reserve_method, string "Reserve Packagings",
options "Reserve Only Full Packagings" and "Reserve Partial Packagings").
The page lists the accounting fields on the product category form, but never says what happens with a subcategory. Users reasonably assume the hierarchy carries the values down, and it does not. Measured on a fresh saas-19.4 database. A parent category was created and all writable fields were set to deviating values, then a child category was created with parent_id pointing at it. For all seven fields (expense, income, stock valuation and price difference accounts, stock variation, costing method, inventory valuation) the child differed from the parent and matched a category created with no parent at all. Changing the parent afterwards also had no effect: the parent was moved to "fifo" and "real_time" while the child stayed on "standard" and "periodic". The values come from the company instead. The fields are company_dependent and fall back to ir.default; ir.property no longer exists in this version. Changing the company's expense account propagated immediately to the child category and to categories created before the change, while a category holding its own value kept it. The note deliberately says only what was measured: no inheritance, filled in from the company's defaults, and later parent changes have no effect. The placement differs from the obvious one: the note is inserted directly after the list of product category fields rather than after the sentence about products inheriting from their category, because that sentence ends with a colon introducing the next list and must not be split.
Collaborator
dade-odoo
requested changes
Sep 15, 2026
dade-odoo
left a comment
Contributor
There was a problem hiding this comment.
Hi @Natalie-the-technician , thanks for your contribution. I've suggested one minor wording change. Beyond that, it looks good from the accounting scope.
Comment on lines
+220
to
+222
| Product categories do **not** inherit these fields from their parent category. A new | ||
| subcategory is filled in with the company's default values, and changing a field on a parent | ||
| category afterwards has no effect on its subcategories. |
Contributor
There was a problem hiding this comment.
Suggested change
| Product categories do **not** inherit these fields from their parent category. A new | |
| subcategory is filled in with the company's default values, and changing a field on a parent | |
| category afterwards has no effect on its subcategories. | |
| Product categories do **not** inherit these fields from their parent category. New subcategories | |
| are filled in with the company's default values, and changing a field on a parent category | |
| afterwards has no effect on its subcategories. |
erjer-odoo
approved these changes
Sep 15, 2026
erjer-odoo
left a comment
Contributor
There was a problem hiding this comment.
Approved on behalf of the inventory scope.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

This PR corrects three inaccurate statements and adds one missing one. All four were
verified against odoo/odoo
saas-19.4at commitcbed569e, both by reading the sourceand by measuring on a freshly created database.
1. Sales / Variants: dynamic variants are not created by sales orders only
content/applications/sales/sales/products_prices/products/variants.rst:119What it said: ":guilabel:
Dynamically: Creates variants only when correspondingattributes and values are added to a sales order."
Why that is wrong:
product.template._create_product_variant()is called from sevenplaces outside of tests: two in the Sales app, three in eCommerce, one in Point of Sale,
and one in Purchase.
saleaddons/sale/controllers/product_configurator.py:122sale_product_matrixaddons/sale_product_matrix/models/sale_order.py:62website_saleaddons/website_sale/controllers/main.py:835website_saleaddons/website_sale/models/product_template.py:1114website_saleaddons/website_sale/models/sale_order.py:707point_of_saleaddons/point_of_sale/models/product_template.py:81purchase_product_matrixaddons/purchase_product_matrix/models/purchase.py:49(
_create_first_product_variant()ataddons/product/models/product_template.py:1448alsocalls it, but only to materialise the first variant when a product is copied, so it is not
an entry point for choosing a combination and is not counted here.)
Measured on a database with no Sales module installed (
product,purchase,stock,purchase_stockonly): enabling the purchase "Variant Grid Entry" setting and filling twocells of the grid created two dynamic variants. Odoo's own test asserts the same behaviour:
addons/purchase_product_matrix/tests/test_purchase_matrix.py:20expects 7 variants wherethe non-dynamic attributes alone would produce 4.
The sentence about Inventory was added because the opposite was measured on the same
database: confirming a purchase order and validating the incoming receipt created no
variant. There is no field in
addons/stockthat selects a combination of attributevalues —
stock.move,stock.move.line,stock.quantandstock.lotall point atproduct.product.2. Inventory / Packaging: packagings are not product-specific
content/applications/inventory_and_mrp/inventory/product_management/configure/packaging.rst:10What it said: packagings "must be configured on the individual product form. This is
because packagings are product specific, not generic."
Why that is wrong: the model
product.packagingno longer exists in this version.Searched the whole tree (643 addons directories plus
odoo/addons):grep -rn "_name = ['\"]product.packaging['\"]" addons/ odoo/returns 0 hits.A packaging is now an ordinary
uom.uomrecord. The quantity it holds comes fromrelative_factor/factoron that single global record(
addons/uom/models/uom_uom.py:17-43);uom.uomhas neitherproduct_idnorcompany_id.Products reference the packagings they may use through
product.template.uom_ids, amany2many labelled "Packagings" (
addons/product/models/product_template.py:159).Measured: two different products were given the same packaging and both referenced the same
uom.uomrecord, so one packaging cannot hold a different quantity per product. The onlyproduct-specific part is the barcode, carried by the helper model
product.uom, whosefields are
allowed_uom_ids, barcode, company_id, product_id, uom_id— no quantity field.3. Inventory / Packaging: the setting is called "Units of Measure & Packagings"
Same file, line 132.
What it said: to reveal the field, "tick the :guilabel:
Product Packagingscheckbox".Why that is wrong: read from a running database,
res.config.settingshas no field whosename contains "packaging". The setting is
group_uom, labelled "Units of Measure &Packagings", implying group
uom.group_uom("Manage Multiple Units of Measure"). Thecategory field carries
groups="uom.group_uom"in viewstock.product_category_form_view_inherit.The menu path was already correct and is unchanged:
group_uomdoes appear instock.res_config_settings_view_form, inside the block titled "Products".The same paragraph also referred to the field as "Reserve Packaging"; the field's actual
label is "Reserve Packagings" (
product.category.packaging_reserve_method, options"Reserve Only Full Packagings" and "Reserve Partial Packagings"). Aligned.
4. Accounting: product categories do not inherit from their parent category
content/applications/finance/accounting/get_started.rst, after the list of product categoryfields.
What was missing: the page lists the accounting fields on the product category form but
never says what a subcategory does with them. The hierarchy does not carry them down, and
nothing on the page says so.
Measured on a fresh database. A parent category was created and every writable field set
to a deviating value, then a child category was created with
parent_idpointing at it. Forall seven fields — expense account, income account, stock valuation account, price difference
account, stock variation account, costing method, inventory valuation — the child differed
from the parent and matched a category created with no parent at all. Changing the parent
afterwards had no effect either: the parent was moved to
fifoandreal_timewhile thechild stayed on
standardandperiodic.The values come from the company. These fields are
company_dependentand fall back toir.default;ir.propertyno longer exists in this version. Changing the company's expenseaccount propagated immediately to the child category and to categories created before the
change, while a category holding its own value kept it.
The note deliberately states only what was measured: no inheritance, filled in from the
company defaults, and later parent changes have no effect.
Checks
make fastsucceeds. The warning count is unchanged from the base branch (1 warning,about the optional local
odoo/odoocheckout, unrelated to these files).make testreports the same 990 findings as the base branch, byte for byte. None of themconcern the changed paragraphs.
is gone.