Daily updates from Odoo
Thursday, August 6, 2026
316 changes
25 changes
Enhancements to existing features
The feature was never merged on IAP/Internal's side and we now have a new task to move the setting up of the auto-refill from the local db to the IAP server, so the feature is no longer relevant on the client side. Task-6397951 Forward-Port-Of: odoo/odoo#280745 Forward-Port-Of: odoo/odoo#277512
Original PR description
The feature was never merged on IAP/Internal's side and we now have a new task to move the setting up of the auto-refill from the local db to the IAP server, so the feature is no longer relevant on the client side. Task-6397951 Forward-Port-Of: odoo/odoo#280745 Forward-Port-Of: odoo/odoo#277512
This commit adds 3 new `Tax Exemption Reason Code`: - VATEX-FR-F - VATEX-FR-I - VATEX-FR-J task-6333649 Forward-Port-Of: odoo/odoo#280537 Forward-Port-Of: odoo/odoo#278086
Original PR description
This commit adds 3 new `Tax Exemption Reason Code`: - VATEX-FR-F - VATEX-FR-I - VATEX-FR-J task-6333649 Forward-Port-Of: odoo/odoo#280537 Forward-Port-Of: odoo/odoo#278086
Some more improvements to refine the production process: - Allow computation of workorders' expected duration for a full production. - Remove writing into `qty_produced` while setting workorders states, unless its being marked as done. Task: 6421044 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
Some more improvements to refine the production process: - Allow computation of workorders' expected duration for a full production. - Remove writing into `qty_produced` while setting workorders states, unless its being marked as done. Task: 6421044 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Resolved issues and error corrections
Issue: --- `code` is hidden in `payment.method` from without dev mode, which cause an validation error when creating a new payment method. opw-6390285 Forward-Port-Of: odoo/odoo#278671
Original PR description
Issue: --- `code` is hidden in `payment.method` from without dev mode, which cause an validation error when creating a new payment method. opw-6390285 Forward-Port-Of: odoo/odoo#278671
Steps to reproduce 1. Go to Manufacturing > Configuration > Settings and enable By-Products 2. Go to Inventory > Configuration > Locations, find the location with type Production and set the Cost of Production account 3. Go to Inventory > Configuration > Product Categories and set the costing method to Standard Price and Inventory Valuation to Perpetual (at invoicing) 4. Set the finished product and byproduct as Storable with a non-zero Cost 5. Create a BoM with a component and a by
Original PR description
Steps to reproduce 1. Go to Manufacturing > Configuration > Settings and enable By-Products 2. Go to Inventory > Configuration > Locations, find the location with type Production and set the Cost of…
Steps to reproduce 1. Go to Manufacturing > Configuration > Settings and enable By-Products 2. Go to Inventory > Configuration > Locations, find the location with type Production and set the Cost of Production account 3. Go to Inventory > Configuration > Product Categories and set the costing method to Standard Price and Inventory Valuation to Perpetual (at invoicing) 4. Set the finished product and byproduct as Storable with a non-zero Cost 5. Create a BoM with a component and a byproduct with a Cost Share % assigned 6. Create and complete a manufacturing order 7. Check the journal entries of the MO: the byproduct entry shows $0 Issue Standard-cost byproduct moves have no price_unit set in either code path of _cal_price, so their journal entries always show $0. When the finished product is standard cost, _cal_price returns early at https://github.com/odoo/odoo/blob/55221db559cda1c61229eecf8493f5fbaee5cd50/addons/mrp_account/models/mrp_production.py#L66-L68 without iterating byproducts at all, so no price_unit is ever set on them. When the finished product is FIFO/AVCO, the byproduct loop at https://github.com/odoo/odoo/blob/55221db559cda1c61229eecf8493f5fbaee5cd50/addons/mrp_account/models/mrp_production.py#L83-L84 only sets price_unit for FIFO/AVCO byproducts. Standard byproducts are skipped, giving them $0 even though their cost_share was already deducted from the finished product, making value disappear from inventory entirely. For standard-cost products the MO has no influence on their value — they always use the standard_price from the product form, regardless of cost_share. Solution In the early-return branch, iterate byproducts: standard ones get standard_price, FIFO/AVCO ones get total_cost * cost_share. In the FIFO/AVCO branch, add the same standard_price fallback so standard byproducts are no longer left at $0 when their cost_share is set. opw-6020065 Forward-Port-Of: odoo/odoo#280614 Forward-Port-Of: odoo/odoo#257472
**Issue** Confirming a SO that generates a batch of MOs from a BoM with a batch size could lead to creating an invalid number of pickings: all the MOs end up sharing a single picking instead of getting one each. **Steps to reproduce** - Use 2-step manufacturing - Create a storable product with the MTO + Manufacture routes - Add a BOM that has a batch size of 10 that consumes one component - Create and confirm a SO of 100 units of that product -> 10 MOs are created, but each one points
Original PR description
**Issue** Confirming a SO that generates a batch of MOs from a BoM with a batch size could lead to creating an invalid number of pickings: all the MOs end up sharing a single picking instead of…
**Issue** Confirming a SO that generates a batch of MOs from a BoM with a batch size could lead to creating an invalid number of pickings: all the MOs end up sharing a single picking instead of getting one each. **Steps to reproduce** - Use 2-step manufacturing - Create a storable product with the MTO + Manufacture routes - Add a BOM that has a batch size of 10 that consumes one component - Create and confirm a SO of 100 units of that product -> 10 MOs are created, but each one points to the same "Pick Components" transfer instead of getting its own. **Cause** This commit dd6ee071f949752d31497d9f975ba7fc41ebcd98 batches the confirm of productions: https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/mrp/models/stock_rule.py#L120 thus `assign_picking` is called in batches: https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/mrp/models/mrp_production.py#L1653 https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1736-L1737 https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1550-L1556 since, the `reference_ids` are the same for each MO/stock.move (they all come from the same procurement): https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1712-L1713 Consequently, all the moves end up in the same recordset `moves`: https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1527 Creating only one picking: https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1577 opw-6403427 Forward-Port-Of: odoo/odoo#279179
Issue ----- Branch companies can have quants of a product limited to their parent company, but they don't show in the per location report. Steps to reproduce ----- - Create a branch company - Create a WH for the branch - Create a tracked product - Company set to parent only - Switch to the branch company - Add a quant of the product in branch stock - Open Inventory > Reporting > Locations > The product is not shown although there is a quant in the branch Cause ----- The m
Original PR description
Issue ----- Branch companies can have quants of a product limited to their parent company, but they don't show in the per location report. Steps to reproduce ----- - Create a branch company - Create…
Issue ----- Branch companies can have quants of a product limited to their parent company, but they don't show in the per location report. Steps to reproduce ----- - Create a branch company - Create a WH for the branch - Create a tracked product - Company set to parent only - Switch to the branch company - Add a quant of the product in branch stock - Open Inventory > Reporting > Locations > The product is not shown although there is a quant in the branch Cause ----- The menu button triggers `action_view_quants` https://github.com/odoo/odoo/blob/03a3662212f094158f885ae6545009fd0a74d3cb/addons/stock/views/stock_quant_views.xml#L493-L495 https://github.com/odoo/odoo/blob/03a3662212f094158f885ae6545009fd0a74d3cb/addons/stock/models/stock_quant.py#L399-L402 The problem here comes from the fact that in `_get_quants_action`, we limit the products to those of only the active companies, instead of allowing to view those of parent companies aswell. https://github.com/odoo/odoo/blob/03a3662212f094158f885ae6545009fd0a74d3cb/addons/stock/models/stock_quant.py#L1330 Such a change works because the domain is specifically for the product's (`product_id.company_id`) and not the location's. ----- Ticket: opw-6131525 Forward-Port-Of: odoo/odoo#280083 Forward-Port-Of: odoo/odoo#277531
When you refuse an applicant, and there is a survey user_input linked, you are not able to do it because applicant officers don't have access 'write' on the model. So we do it in sudo. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280600
Original PR description
When you refuse an applicant, and there is a survey user_input linked, you are not able to do it because applicant officers don't have access 'write' on the model. So we do it in sudo. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280600
A user with role Inventory/User cannot validate the dropship of a product with an Average Cost (AVCO) Costing Method Steps to reproduce: 1. Install sale_management, stock_dropshipping and stock_landed_costs module 2. Go to Sales > Configuration > Categories and change Furniture / Office's Costing Method to Average Cost (AVCO) 3. Go to Settings > Users and set Marc Demo's role on Purchase to User 3. Log in as Marc Demo 4. Create and confirm a quotation for customer Acme Corporation with p
Original PR description
A user with role Inventory/User cannot validate the dropship of a product with an Average Cost (AVCO) Costing Method Steps to reproduce: 1. Install sale_management, stock_dropshipping and…
A user with role Inventory/User cannot validate the dropship of a product with an Average Cost (AVCO) Costing Method Steps to reproduce: 1. Install sale_management, stock_dropshipping and stock_landed_costs module 2. Go to Sales > Configuration > Categories and change Furniture / Office's Costing Method to Average Cost (AVCO) 3. Go to Settings > Users and set Marc Demo's role on Purchase to User 3. Log in as Marc Demo 4. Create and confirm a quotation for customer Acme Corporation with product Large Cabinet (Dropship route and AVCO costing method) 5. Go to the related purchase order and confirm it 6. Go to the related dropship and validate it 7. An access error is raised Issue: Validating a dropship recomputes the cost of the product and reads `stock.valuation.adjustment.lines` https://github.com/odoo/odoo/blob/cb7b3de6cea07464bcadd1325f52533d34ce09bc/addons/stock_landed_costs/models/stock_move.py#L11 But only Inventory/Administrator have read access to these records https://github.com/odoo/odoo/blob/cb7b3de6cea07464bcadd1325f52533d34ce09bc/addons/stock_landed_costs/security/ir.model.access.csv#L4 Solution: Call `_get_landed_cost` with `.sudo()` in order to update the cost even though the user has no landed costs access opw-6366844 Forward-Port-Of: odoo/odoo#280521 Forward-Port-Of: odoo/odoo#276285
Before this commit: The field selector was not wide enough to fill the available space. Task: 6320505 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
Before this commit: The field selector was not wide enough to fill the available space. Task: 6320505 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Steps to reproduce the error: (Python Version: 3.14.X) - Install ``accounting_firm`` industry with demo data Code for server action to generate the error: ```py for i in [1]: try: pass except Exception: pass if i: pass ``` Traceback: ```py ValueError: forbidden opcode(s) in'...': JUMP_BACKWARD_NO_INTERRUPT ``` https://github.com/odoo/industry/blob/701f7595453772e42ce72aa75df346a504e5bd82/accounting_firm/demo/ir_actions_server.xml#L105-L10
Original PR description
Steps to reproduce the error: (Python Version: 3.14.X) - Install ``accounting_firm`` industry with demo data Code for server action to generate the error: ```py for i in [1]: try: pass except…
Steps to reproduce the error: (Python Version: 3.14.X)
- Install ``accounting_firm`` industry with demo data
Code for server action to generate the error:
```py
for i in [1]:
try:
pass
except Exception:
pass
if i:
pass
```
Traceback:
```py
ValueError: forbidden opcode(s) in'...': JUMP_BACKWARD_NO_INTERRUPT
```
https://github.com/odoo/industry/blob/701f7595453772e42ce72aa75df346a504e5bd82/accounting_firm/demo/ir_actions_server.xml#L105-L108
The server action contains a ``for`` loop with a ``try/except`` block followed by additional statements in the loop body,
this combination generates the ``JUMP_BACKWARD_NO_INTERRUPT`` opcode, which is not included in ``_SAFE_OPCODES`` at [1].
When the server action is evaluated by ``safe_eval``, it calls the ``assert_valid_codeobj`` method, which validates the compiled bytecode against ``_SAFE_OPCODES``. Since ``JUMP_BACKWARD_NO_INTERRUPT`` is not present in the allowed opcodes, ``assert_valid_codeobj()`` raises a ``ValueError`` at [2] before the server action is executed .
Solution:
``JUMP_BACKWARD_NO_INTERRUPT`` opcode is added in the ``_SAFE_OPCODES`` and it is also added in the ``_SAFE_QWEB_OPCODES``.
It was added in Python 3.11: https://docs.python.org/3/whatsnew/3.11.html#new-opcodes
``JUMP_BACKWARD_NO_INTERRUPT`` is a control-flow opcode that only changes
the interpreter's execution flow by jumping back to a previous instruction.
It is the equivalent to ``JUMP_BACKWARD`` opcode. Its only semantic difference is
that the interpreter does not perform an interrupt check at that instruction.
It does not introduce any new capabilities or perform operations such as
attribute access, imports, function calls, or object creation.
Ref: https://docs.python.org/3.12/library/dis.html#opcode-JUMP_BACKWARD_NO_INTERRUPT
Similar commit that adds some necessary opcodes:
https://github.com/odoo/odoo/commit/86498d24946e510025add5d24ef0d4bcce8ad05f
[1]: https://github.com/odoo/odoo/blob/2ccbc4660077bd48529e9de43d4309fbfafc75ca/odoo/tools/safe_eval.py#L135
[2]: https://github.com/odoo/odoo/blob/2ccbc4660077bd48529e9de43d4309fbfafc75ca/odoo/tools/safe_eval.py#L244-L246
sentry-7614026125
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#277192### Issue: A partner with VAT set to '/' incorrectly matches a fiscal position with `VAT required`, instead of one without The '/' value is the placeholder suggested by the UI to indicate that the partner is known to have no VAT, but it was treated as a valid VAT by the fiscal position matching logic ### Cause: `_get_fpos_ranking_functions` uses `_get_vat_valid` to rank fiscal positions based on VAT presence `_get_vat_valid` returned `True` for any non-empty VAT value, including '/' Th
Original PR description
### Issue: A partner with VAT set to '/' incorrectly matches a fiscal position with `VAT required`, instead of one without The '/' value is the placeholder suggested by the UI to indicate that the…
### Issue: A partner with VAT set to '/' incorrectly matches a fiscal position with `VAT required`, instead of one without The '/' value is the placeholder suggested by the UI to indicate that the partner is known to have no VAT, but it was treated as a valid VAT by the fiscal position matching logic ### Cause: `_get_fpos_ranking_functions` uses `_get_vat_valid` to rank fiscal positions based on VAT presence `_get_vat_valid` returned `True` for any non-empty VAT value, including '/' The '/' case was not excluded, causing it to be treated as a valid VAT number ### Steps to reproduce: - Install `account` - Create two fiscal positions with auto-apply: -- Name: FP VAT, VAT required: True, sequence: 1 -- Name: FP no VAT, VAT required: False, sequence: 2 - Create a partner with VAT: '/' - Create an Invoice for that partner and check the Fiscal Position Before the fix, `FP VAT` is selected instead of `FP no VAT` opw-6204531 Forward-Port-Of: odoo/odoo#280494 Forward-Port-Of: odoo/odoo#280065
## Issue When logging timesheet from the *Recorded* smart button on a Sale Order containing multiple service items, the recorded hours will be added to the first sale order line, regardless of the task ID set on the timesheet entries. ## Steps to reproduce 1. Install *Sales Timesheet* (`sale_timesheet`) 2. Create a Product P: - *Product Type*: Service - *Create on Order*: Task - *Project*: Any 3. Create a SO: - *Customer*: Any - Add the product P on two different
Original PR description
## Issue When logging timesheet from the *Recorded* smart button on a Sale Order containing multiple service items, the recorded hours will be added to the first sale order line, regardless of the…
## Issue
When logging timesheet from the *Recorded* smart button on a Sale Order containing multiple service items, the recorded hours will be added to the first sale order line, regardless of the task ID set on the timesheet entries.
## Steps to reproduce
1. Install *Sales Timesheet* (`sale_timesheet`)
2. Create a Product P:
- *Product Type*: Service
- *Create on Order*: Task
- *Project*: Any
3. Create a SO:
- *Customer*: Any
- Add the product P on two different lines and give them two different descriptions D1 and D2
- Confirm the SO, this will create two tasks with the names D1 and D2
4. On the SO, click the *Recorded* smart button and create two entries:
1. Task D1, 2 hours spent
2. Tsk D2, 3 hours spent
5. **Back on the SO, there are 5 hours registered for the first SOL (with the description D1), which does not match the entries we created from the smart button.**
It is worth noting that when we create the Timesheets entries from the project itself (instead of the SO's smart button), the hours are correctly distributed among the different SOLs.
## Cause
The SOL linked to the timesheet entry (`account.analytic.line`) is computed by `_compute_so_line`:
https://github.com/odoo/odoo/blob/8b102f500f5a122e99b07a08cc43814e7c6f0f75/addons/sale_timesheet/models/hr_timesheet.py#L79-L82
This method sets the correct SOL under the condition that `is_so_line_edited` is False and `_is_no_billed()` returns True.
When opening the *Recorded* smart button from a SO, the `is_so_line_edited` is set to True by default, even if no SOL was modified.
https://github.com/odoo/odoo/blob/8b102f500f5a122e99b07a08cc43814e7c6f0f75/addons/sale_timesheet/models/sale_order.py#L113-L117
As that value is never set to False, when trying to compute the SOL for the timesheet entry, the entry is skipped and the default SOL (which is the first one) is used instead.
opw-6133473
Forward-Port-Of: odoo/odoo#274759Before this commit, in some cases, the order created for printing cash move was saved to the IndexedDB and then later loaded from IndexedDB, which caused the order gets synced to the backend but missing some required fields, like preset or pricelist. opw-5969602 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#262248
Original PR description
Before this commit, in some cases, the order created for printing cash move was saved to the IndexedDB and then later loaded from IndexedDB, which caused the order gets synced to the backend but missing some required fields, like preset or pricelist. opw-5969602 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#262248
When an invoice is created with "Invoice Overages" selected but not all delivered amounts are included, and a second invoice is then created without "Invoice Overages" selected, the system generates a credit note Steps to reproduce: 1. Install Sales and Project 2. Go to Sales > Products and create a product "test" with Product Type: Service, Create on Order: Project & Task and Invoicing Policy: Prepaid/Fixed Price 3. Create a new quotation for any customer with one unit of product test and
Original PR description
When an invoice is created with "Invoice Overages" selected but not all delivered amounts are included, and a second invoice is then created without "Invoice Overages" selected, the system generates…
When an invoice is created with "Invoice Overages" selected but not all delivered amounts are included, and a second invoice is then created without "Invoice Overages" selected, the system generates a credit note Steps to reproduce: 1. Install Sales and Project 2. Go to Sales > Products and create a product "test" with Product Type: Service, Create on Order: Project & Task and Invoicing Policy: Prepaid/Fixed Price 3. Create a new quotation for any customer with one unit of product test and confirm it 4. Change the quantity delivered to 5 5. Click on Create Invoice and then Create Draft 6. Change the quantity to 3 and confirm 7. Go back to the sale order 8. Click on Create Invoice and uncheck Invoice Overages then click on Create Draft 9. A credit note is created even though there is nothing to invoice since Invoice Overages was disabled Issue: There is no mechanism to prevent the creation of an invoice if we set Invoice Overages to false Solution: Prevent invoice creation if Invoice Overages is set to False. Also had to adapt `_compute_invoice_overages` to make it consistent with its inverse method while keeping `allow_invoice_overages` as the default value. opw-6356807
When we create a database with `-i web --skip-auto-install`, we run into CSS compilation errors due to undefined variables `$black` and `$gray-200`. This commit adds these two definitions. opw-6398398 Forward-Port-Of: odoo/odoo#279753 Forward-Port-Of: odoo/odoo#279287
Original PR description
When we create a database with `-i web --skip-auto-install`, we run into CSS compilation errors due to undefined variables `$black` and `$gray-200`. This commit adds these two definitions. opw-6398398 Forward-Port-Of: odoo/odoo#279753 Forward-Port-Of: odoo/odoo#279287
Before this commit, this test sometimes failed because it couldn't find a dialog containing "camera" within 200ms. In the test scenario, we click to open the BarcodeDialog, which uses the BarcodeVideoScanner. The latter, in its `onMounted`, checks whether it has the necessary permission, which isn't the case as the `getUserMedia` function is mocked in the test to return a rejected promise. As a consequence, the `onError` callback given in props is called, which changes the state of the parent
Original PR description
Before this commit, this test sometimes failed because it couldn't find a dialog containing "camera" within 200ms. In the test scenario, we click to open the BarcodeDialog, which uses the…
Before this commit, this test sometimes failed because it couldn't find a dialog containing "camera" within 200ms. In the test scenario, we click to open the BarcodeDialog, which uses the BarcodeVideoScanner. The latter, in its `onMounted`, checks whether it has the necessary permission, which isn't the case as the `getUserMedia` function is mocked in the test to return a rejected promise. As a consequence, the `onError` callback given in props is called, which changes the state of the parent component, which re-renders itself so display "Unable to access camera" instead of the BarcodeVideoScanner. To make this test more robust, we do 2 things: 1) load the zxing library before running the test, which avoids the BarcodeVideoScanner component to load it in onWillStart. 2) explicitly wait for the 2 animationFrames, as in the scenario, we must wait for the BarcodeDialog to be rendered twice, and those renderings are now synchronous. runbot error-237933 Forward-Port-Of: odoo/odoo#280613
Steps to reproduce ------------------ 1. Set the company document layout to DIN5008 2. Open a delivery and print the delivery slip The title is missing on the DIN5008 layout, we only have the reference `WH/OUT/00001`. What happens ------------ The DIN5008 layout hides the body title with css and prints its own `h2` instead, from the `din5008_document_title` variable, and uses `o.name` (the picking number) when this variable is not set. The commit 0058d1cf7655 added the title on the
Original PR description
Steps to reproduce ------------------ 1. Set the company document layout to DIN5008 2. Open a delivery and print the delivery slip The title is missing on the DIN5008 layout, we only have the reference `WH/OUT/00001`. What happens ------------ The DIN5008 layout hides the body title with css and prints its own `h2` instead, from the `din5008_document_title` variable, and uses `o.name` (the picking number) when this variable is not set. The commit 0058d1cf7655 added the title on the standard delivery report with `picking_type_id._get_code_report_name()`, but `l10n_din5008_stock` was not updated to set `din5008_document_title`, so on DIN5008 we only get the number. The fix ------- We set it the same way as the other layouts, hence we get back the full title `Delivery Note WH/OUT/00001`. opw-6299248 Forward-Port-Of: odoo/odoo#277444
The tour deletes the five "brol" menu items, then immediately drags `new_nested_menu` onto `new_menu`. Each delete step only waits for the next item's delete button, which is already in the DOM, so the removals may still be un-rendered when the drag starts, shifting the rows under it and leaving `new_nested_menu` unnested. This commit waits for the deleted items to be gone before dragging. runbot-944576
Original PR description
The tour deletes the five "brol" menu items, then immediately drags `new_nested_menu` onto `new_menu`. Each delete step only waits for the next item's delete button, which is already in the DOM, so the removals may still be un-rendered when the drag starts, shifting the rows under it and leaving `new_nested_menu` unnested. This commit waits for the deleted items to be gone before dragging. runbot-944576
[FIX] html_builder: prevent crash on legacy image shapes When the Website Editor encounters an image shape that does not exist in the registry, it fatally crashes upon saving (`TypeError: Cannot read properties of undefined`), blocking the user from saving the page. While an upgrade script exists to remap these shapes ([commit https://github.com/odoo/odoo/commit/f348be018f5740a31754494c905ea2b61bb718be](https://github.com/odoo/upgrade/commit/f348be0dcbc63c1f74f742b562509f81767564c0)), ti
Original PR description
[FIX] html_builder: prevent crash on legacy image shapes When the Website Editor encounters an image shape that does not exist in the registry, it fatally crashes upon saving (`TypeError: Cannot read…
[FIX] html_builder: prevent crash on legacy image shapes When the Website Editor encounters an image shape that does not exist in the registry, it fatally crashes upon saving (`TypeError: Cannot read properties of undefined`), blocking the user from saving the page. While an upgrade script exists to remap these shapes ([commit https://github.com/odoo/odoo/commit/f348be018f5740a31754494c905ea2b61bb718be](https://github.com/odoo/upgrade/commit/f348be0dcbc63c1f74f742b562509f81767564c0)), timeline gaps leave SaaS databases vulnerable. For example, if a client upgraded their database to 17.0 in Feb 2024, they bypassed the migration script merged in Dec 2024. This leaves the legacy shape permanently orphaned inside their modern views. This commit adds a `getImageShape` fallback. Instead of crashing,the editor now defaults to standard values and renders "None" in the UI, allowing the user to select a new shape and save their work. Steps to Reproduce: 1. Install Website. 2. Go to Site -> HTML / CSS Editor. 3. Add `data-shape="web_editor/basic/bsc_organic_2"` to an <img> tag. 4. Click "Edit" to open the Website Builder. 5. Click the image, OR click "Save". 6. JS traceback. [opw-6286044](https://www.odoo.com/odoo/my-support-tasks/6286044?debug=assets) [opw-6291591](https://www.odoo.com/odoo/my-support-tasks/6291591?debug=assets) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280505 Forward-Port-Of: odoo/odoo#270356
The `selection` attribute of `fields.Selection` is not generally translated (unless it is a function instead of a list). For user facing strings, we generally need to translate the value displayed. Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280476 Forward-Port-Of: odoo/odoo#280094
Original PR description
The `selection` attribute of `fields.Selection` is not generally translated (unless it is a function instead of a list). For user facing strings, we generally need to translate the value displayed. Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280476 Forward-Port-Of: odoo/odoo#280094
### Steps to reproduce: - In the settings enable: Multi-Steps Routes - Inventory > Configuration > Warehouse Management > Warehouses - Set your warehouse deliveries in two steps - Inventory > Operations > Tranfers > Internal > New - Set the operation type as Pick, set a partner and add Partner: Bob - In the sales & Purchase tab of the partner form set a customer location to be a child of the Customers location: Customers/Bob'Stock - Confirm and validate the Pick for 1 unit of a product
Original PR description
### Steps to reproduce: - In the settings enable: Multi-Steps Routes - Inventory > Configuration > Warehouse Management > Warehouses - Set your warehouse deliveries in two steps - Inventory >…
### Steps to reproduce: - In the settings enable: Multi-Steps Routes - Inventory > Configuration > Warehouse Management > Warehouses - Set your warehouse deliveries in two steps - Inventory > Operations > Tranfers > Internal > New - Set the operation type as Pick, set a partner and add Partner: Bob - In the sales & Purchase tab of the partner form set a customer location to be a child of the Customers location: Customers/Bob'Stock - Confirm and validate the Pick for 1 unit of a product P #### > A ship picking is created but the destination of the related move is still set to the default customer location. ### Note: If the flow is performed by a sale order, the `property_stock_customer` location will appropriately be used as `location_final_id`: https://github.com/odoo/odoo/blob/7609b5805c3704034b4d7813e2f356381ed18771/addons/sale_stock/models/sale_order_line.py#L297 https://github.com/odoo/odoo/blob/7609b5805c3704034b4d7813e2f356381ed18771/addons/sale_stock/models/sale_order_line.py#L306-L309 https://github.com/odoo/odoo/blob/720598d0315dbb91628441078febfd43ffefb431/addons/stock/models/stock_rule.py#L263-L264 So that the bug does not occur in that case. By contrast if the pick move is created manually, we do not set its `location_final_id` and hence do not propagate the info. Even though it looks expected to be set set as location_dest_id of the ship move sas suggested by the `stock.picking.location_dest_id` compute method : https://github.com/odoo/odoo/blob/fe3aea07a1964cd24f4c8ebf2bc93e483eca6b0b/addons/stock/models/stock_picking.py#L990-L1002 opw-6402483 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279616 Forward-Port-Of: odoo/odoo#278838
# Issue The test `test_absence_management_with_timeoff` fails if demo data is enabled. It was introduced by : https://github.com/odoo/odoo/pull/272089 # Cause `self.env.user` has an 'Europe/Brussels' tz when demo data is enabled. This changes the date used in the `search_count` at then end of the test : 2026-01-14 00:00 => 2026-01-13 23:00 So we check at the wrong date runbot-941523 Forward-Port-Of: odoo/odoo#279764
Original PR description
# Issue The test `test_absence_management_with_timeoff` fails if demo data is enabled. It was introduced by : https://github.com/odoo/odoo/pull/272089 # Cause `self.env.user` has an 'Europe/Brussels' tz when demo data is enabled. This changes the date used in the `search_count` at then end of the test : 2026-01-14 00:00 => 2026-01-13 23:00 So we check at the wrong date runbot-941523 Forward-Port-Of: odoo/odoo#279764
[*]=website 1. Sync background shape color with color preset. Steps to reproduce: 1. Go to the website and enter edit mode. 3. Drop any snippet. 4. Add a background shape. 5. Set the background shape color to "o-color-1". 6. Go to theme tab. 7. Change the value of theme color 1 from theme preset. Issue: The background shape color is not updated when the theme color changes. Reason: The background shape color is embedded in the
Original PR description
[*]=website 1. Sync background shape color with color preset. Steps to reproduce: 1. Go to the website and enter edit mode. 3. Drop any snippet. 4. Add a background shape. 5. Set the background shape…
[*]=website
1. Sync background shape color with color preset.
Steps to reproduce:
1. Go to the website and enter edit mode.
3. Drop any snippet.
4. Add a background shape.
5. Set the background shape color to "o-color-1".
6. Go to theme tab.
7. Change the value of theme color 1 from theme preset.
Issue:
The background shape color is not updated when the theme color changes.
Reason:
The background shape color is embedded in the URL of the "**background-image**" style attribute. When the theme color value changes, this URL is not updated. Additionally, the URL uses color variables rather than resolved hexadecimal color values as parameters. As a result, even when an updation occurs, the URL itself remains unchanged, preventing the background shape color from being updated.
2. Sync image shape color with color preset.
Steps to reproduce:
1. Go to the website and enter edit mode.
2. Drop any snippet.
4. Click on the image and add a shape.
5. Set the image shape color to "o-color-1".
6. Go to theme tab.
7. Change the value of theme color 1 from theme preset.
Issue:
The image shape color is not updated when the theme color changes.
Reason:
When the theme color value changes, the SVGs are not re-fetched. Additionally, the image "**shapeColors**" dataset stores the hexadecimal value of the theme color instead of the corresponding CSS variable. As a result, there is no way to determine which theme color was selected (for example, whether `o-color-1` or `o-color-2`), since only the hex value is available.
task-5438314
Forward-Port-Of: odoo/odoo#276135
Forward-Port-Of: odoo/odoo#241968Miscellaneous changes
load_data() reads product.pricelist.item_ids as a field, which applies _base_domain_item_ids()'s dotted active conditions. Since this runs as the cashier, never sudo, the ORM injects ir.rules into those conditions, forcing a non-hashable subquery that Postgres re-scans once per pricelist item. Cost scales with items times catalog size, freezing session opening on large catalogs. product.pricelist.item is already loaded separately via a plain, indexed pricelist_id domain. Reuse it to build ite
Original PR description
load_data() reads product.pricelist.item_ids as a field, which applies _base_domain_item_ids()'s dotted active conditions. Since this runs as the cashier, never sudo, the ORM injects ir.rules into those conditions, forcing a non-hashable subquery that Postgres re-scans once per pricelist item. Cost scales with items times catalog size, freezing session opening on large catalogs.
product.pricelist.item is already loaded separately via a plain, indexed pricelist_id domain. Reuse it to build item_ids instead of reading the field, avoiding the join/subquery entirely.
before after speedup
item_ids read 475.8s 2.44s ~195x
load_data() (full) unbounded 8.7s hang -> ok
opw-6391847
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#27725420 changes
Enhancements to existing features
The feature was never merged on IAP/Internal's side and we now have a new task to move the setting up of the auto-refill from the local db to the IAP server, so the feature is no longer relevant on the client side. Task-6397951 Forward-Port-Of: odoo/odoo#277512
Original PR description
The feature was never merged on IAP/Internal's side and we now have a new task to move the setting up of the auto-refill from the local db to the IAP server, so the feature is no longer relevant on the client side. Task-6397951 Forward-Port-Of: odoo/odoo#277512
This commit adds 3 new `Tax Exemption Reason Code`: - VATEX-FR-F - VATEX-FR-I - VATEX-FR-J task-6333649 Forward-Port-Of: odoo/odoo#280537 Forward-Port-Of: odoo/odoo#278086
Original PR description
This commit adds 3 new `Tax Exemption Reason Code`: - VATEX-FR-F - VATEX-FR-I - VATEX-FR-J task-6333649 Forward-Port-Of: odoo/odoo#280537 Forward-Port-Of: odoo/odoo#278086
Resolved issues and error corrections
Steps to reproduce 1. Go to Manufacturing > Configuration > Settings and enable By-Products 2. Go to Inventory > Configuration > Locations, find the location with type Production and set the Cost of Production account 3. Go to Inventory > Configuration > Product Categories and set the costing method to Standard Price and Inventory Valuation to Perpetual (at invoicing) 4. Set the finished product and byproduct as Storable with a non-zero Cost 5. Create a BoM with a component and a by
Original PR description
Steps to reproduce 1. Go to Manufacturing > Configuration > Settings and enable By-Products 2. Go to Inventory > Configuration > Locations, find the location with type Production and set the Cost of…
Steps to reproduce 1. Go to Manufacturing > Configuration > Settings and enable By-Products 2. Go to Inventory > Configuration > Locations, find the location with type Production and set the Cost of Production account 3. Go to Inventory > Configuration > Product Categories and set the costing method to Standard Price and Inventory Valuation to Perpetual (at invoicing) 4. Set the finished product and byproduct as Storable with a non-zero Cost 5. Create a BoM with a component and a byproduct with a Cost Share % assigned 6. Create and complete a manufacturing order 7. Check the journal entries of the MO: the byproduct entry shows $0 Issue Standard-cost byproduct moves have no price_unit set in either code path of _cal_price, so their journal entries always show $0. When the finished product is standard cost, _cal_price returns early at https://github.com/odoo/odoo/blob/55221db559cda1c61229eecf8493f5fbaee5cd50/addons/mrp_account/models/mrp_production.py#L66-L68 without iterating byproducts at all, so no price_unit is ever set on them. When the finished product is FIFO/AVCO, the byproduct loop at https://github.com/odoo/odoo/blob/55221db559cda1c61229eecf8493f5fbaee5cd50/addons/mrp_account/models/mrp_production.py#L83-L84 only sets price_unit for FIFO/AVCO byproducts. Standard byproducts are skipped, giving them $0 even though their cost_share was already deducted from the finished product, making value disappear from inventory entirely. For standard-cost products the MO has no influence on their value — they always use the standard_price from the product form, regardless of cost_share. Solution In the early-return branch, iterate byproducts: standard ones get standard_price, FIFO/AVCO ones get total_cost * cost_share. In the FIFO/AVCO branch, add the same standard_price fallback so standard byproducts are no longer left at $0 when their cost_share is set. opw-6020065 Forward-Port-Of: odoo/odoo#280614 Forward-Port-Of: odoo/odoo#257472
**Issue** Confirming a SO that generates a batch of MOs from a BoM with a batch size could lead to creating an invalid number of pickings: all the MOs end up sharing a single picking instead of getting one each. **Steps to reproduce** - Use 2-step manufacturing - Create a storable product with the MTO + Manufacture routes - Add a BOM that has a batch size of 10 that consumes one component - Create and confirm a SO of 100 units of that product -> 10 MOs are created, but each one points
Original PR description
**Issue** Confirming a SO that generates a batch of MOs from a BoM with a batch size could lead to creating an invalid number of pickings: all the MOs end up sharing a single picking instead of…
**Issue** Confirming a SO that generates a batch of MOs from a BoM with a batch size could lead to creating an invalid number of pickings: all the MOs end up sharing a single picking instead of getting one each. **Steps to reproduce** - Use 2-step manufacturing - Create a storable product with the MTO + Manufacture routes - Add a BOM that has a batch size of 10 that consumes one component - Create and confirm a SO of 100 units of that product -> 10 MOs are created, but each one points to the same "Pick Components" transfer instead of getting its own. **Cause** This commit dd6ee071f949752d31497d9f975ba7fc41ebcd98 batches the confirm of productions: https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/mrp/models/stock_rule.py#L120 thus `assign_picking` is called in batches: https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/mrp/models/mrp_production.py#L1653 https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1736-L1737 https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1550-L1556 since, the `reference_ids` are the same for each MO/stock.move (they all come from the same procurement): https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1712-L1713 Consequently, all the moves end up in the same recordset `moves`: https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1527 Creating only one picking: https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1577 opw-6403427 Forward-Port-Of: odoo/odoo#279179
Issue ----- Branch companies can have quants of a product limited to their parent company, but they don't show in the per location report. Steps to reproduce ----- - Create a branch company - Create a WH for the branch - Create a tracked product - Company set to parent only - Switch to the branch company - Add a quant of the product in branch stock - Open Inventory > Reporting > Locations > The product is not shown although there is a quant in the branch Cause ----- The m
Original PR description
Issue ----- Branch companies can have quants of a product limited to their parent company, but they don't show in the per location report. Steps to reproduce ----- - Create a branch company - Create…
Issue ----- Branch companies can have quants of a product limited to their parent company, but they don't show in the per location report. Steps to reproduce ----- - Create a branch company - Create a WH for the branch - Create a tracked product - Company set to parent only - Switch to the branch company - Add a quant of the product in branch stock - Open Inventory > Reporting > Locations > The product is not shown although there is a quant in the branch Cause ----- The menu button triggers `action_view_quants` https://github.com/odoo/odoo/blob/03a3662212f094158f885ae6545009fd0a74d3cb/addons/stock/views/stock_quant_views.xml#L493-L495 https://github.com/odoo/odoo/blob/03a3662212f094158f885ae6545009fd0a74d3cb/addons/stock/models/stock_quant.py#L399-L402 The problem here comes from the fact that in `_get_quants_action`, we limit the products to those of only the active companies, instead of allowing to view those of parent companies aswell. https://github.com/odoo/odoo/blob/03a3662212f094158f885ae6545009fd0a74d3cb/addons/stock/models/stock_quant.py#L1330 Such a change works because the domain is specifically for the product's (`product_id.company_id`) and not the location's. ----- Ticket: opw-6131525 Forward-Port-Of: odoo/odoo#280083 Forward-Port-Of: odoo/odoo#277531
A user with role Inventory/User cannot validate the dropship of a product with an Average Cost (AVCO) Costing Method Steps to reproduce: 1. Install sale_management, stock_dropshipping and stock_landed_costs module 2. Go to Sales > Configuration > Categories and change Furniture / Office's Costing Method to Average Cost (AVCO) 3. Go to Settings > Users and set Marc Demo's role on Purchase to User 3. Log in as Marc Demo 4. Create and confirm a quotation for customer Acme Corporation with p
Original PR description
A user with role Inventory/User cannot validate the dropship of a product with an Average Cost (AVCO) Costing Method Steps to reproduce: 1. Install sale_management, stock_dropshipping and…
A user with role Inventory/User cannot validate the dropship of a product with an Average Cost (AVCO) Costing Method Steps to reproduce: 1. Install sale_management, stock_dropshipping and stock_landed_costs module 2. Go to Sales > Configuration > Categories and change Furniture / Office's Costing Method to Average Cost (AVCO) 3. Go to Settings > Users and set Marc Demo's role on Purchase to User 3. Log in as Marc Demo 4. Create and confirm a quotation for customer Acme Corporation with product Large Cabinet (Dropship route and AVCO costing method) 5. Go to the related purchase order and confirm it 6. Go to the related dropship and validate it 7. An access error is raised Issue: Validating a dropship recomputes the cost of the product and reads `stock.valuation.adjustment.lines` https://github.com/odoo/odoo/blob/cb7b3de6cea07464bcadd1325f52533d34ce09bc/addons/stock_landed_costs/models/stock_move.py#L11 But only Inventory/Administrator have read access to these records https://github.com/odoo/odoo/blob/cb7b3de6cea07464bcadd1325f52533d34ce09bc/addons/stock_landed_costs/security/ir.model.access.csv#L4 Solution: Call `_get_landed_cost` with `.sudo()` in order to update the cost even though the user has no landed costs access opw-6366844 Forward-Port-Of: odoo/odoo#280521 Forward-Port-Of: odoo/odoo#276285
**Steps to reproduce:** - Go to any view where you can send mails (eg sale orders) - Send a first mail to multiple recipients so they are added automatically on the next mail. - Open a new mail, confirm that the recipients are present, add a selectable attachment (eg a PDF), then click on it. - Remove any amount of recipients then send the mail. - You will see that the recipients are added back and the mail is sent to them. **Behavior:** Currently whenever a user clicks on an attachment
Original PR description
**Steps to reproduce:** - Go to any view where you can send mails (eg sale orders) - Send a first mail to multiple recipients so they are added automatically on the next mail. - Open a new mail,…
**Steps to reproduce:**
- Go to any view where you can send mails (eg sale orders)
- Send a first mail to multiple recipients so they are added automatically on the next mail.
- Open a new mail, confirm that the recipients are present, add a selectable attachment (eg a PDF), then click on it.
- Remove any amount of recipients then send the mail.
- You will see that the recipients are added back and the mail is sent to them.
**Behavior:**
Currently whenever a user clicks on an attachment in a mail composer, the systems considers that the user might be trying
to leave the page and will trigger an `urgentSave()`, and further down the line a `web_save()`.
The behavior when a web_save() is triggered is to create a record if there isnt currently one, and otherwise to write the modified values onto the record, using commands.
The recipients for the mail are added by default, which is represented by a list of `[4, id]`add commands, that will be written on the record created in the first `web_save`, however this list is not correctly emptied after the first `_save()`.
If the list is present within `this._changes` then it is correctly cleared, but in the case where no changes were made, the within `this._values['partner_ids']` still contains the commands.
So when we later assign `this.data = { ...this._values };`, `this.data['partner_ids']` now contains our uncleared list of commands.
https://github.com/odoo/odoo/blob/f3e407c6a58abd2ddba42f26fcbd1928da63cb63/addons/web/static/src/model/relational_model/record.js#L1222-L1230
And when we compute changes['partner_ids'] in our next iteration, we find ourselves with our command list again.
https://github.com/odoo/odoo/blob/f3e407c6a58abd2ddba42f26fcbd1928da63cb63/addons/web/static/src/model/relational_model/record.js#L1317-L1322
So when we then try to remove a recipient tag, the new delete command `[3, id]`
just gets canceled out with the already present add command.
And the `write()` in `web_save()` only writes add commands of already present partners, which doesn't do anything.
----
This commit adds a line to ensure commands inside `_values` are cleared
opw-6304713
Forward-Port-Of: odoo/odoo#280533
Forward-Port-Of: odoo/odoo#278876Steps to reproduce: 1. Install Calendar 2. Create a meeting in Calendar in form view 3. Set the video link on it 4. Turn on the debug mode 5. Now, clear the video link Issue: - Traceback ``` Uncaught Promise > Invalid props for component 'CopyButton': 'content' is not a string or object or function ``` Cause: - The 'CopyButton' component expect content to be a string, object or function but receives false. It does not happen in previous versions because in the refector https:/
Original PR description
Steps to reproduce: 1. Install Calendar 2. Create a meeting in Calendar in form view 3. Set the video link on it 4. Turn on the debug mode 5. Now, clear the video link Issue: - Traceback ``` Uncaught…
Steps to reproduce: 1. Install Calendar 2. Create a meeting in Calendar in form view 3. Set the video link on it 4. Turn on the debug mode 5. Now, clear the video link Issue: - Traceback ``` Uncaught Promise > Invalid props for component 'CopyButton': 'content' is not a string or object or function ``` Cause: - The 'CopyButton' component expect content to be a string, object or function but receives false. It does not happen in previous versions because in the refector https://github.com/odoo/odoo/commit/c2f34517b2f9832a498981d0fa17ec39b9739cb6 set the `videocall_location` to false instead of empty string like before https://github.com/odoo/odoo/blob/499420f7062ab467ff6f50b30c547e54c35ae1e9/addons/web/static/src/core/copy_button/copy_button.js#L14 - but any field using the `CopyClipboardChar/CopyClipboardURL` widget passes its raw field value straight through as content. An empty char/text field is represented as false, so whenever such a field becomes empty, CopyClipboardField hands `false` to CopyButton, which fails prop validation (debug mode). Solution: - Fix it at the source: CopyClipboardField's template now falls back to an empty string when the field value is falsy, so CopyButton never receives false but a valid string. opw-6360936 Forward-Port-Of: odoo/odoo#275236
Steps to reproduce the error: (Python Version: 3.14.X) - Install ``accounting_firm`` industry with demo data Code for server action to generate the error: ```py for i in [1]: try: pass except Exception: pass if i: pass ``` Traceback: ```py ValueError: forbidden opcode(s) in'...': JUMP_BACKWARD_NO_INTERRUPT ``` https://github.com/odoo/industry/blob/701f7595453772e42ce72aa75df346a504e5bd82/accounting_firm/demo/ir_actions_server.xml#L105-L10
Original PR description
Steps to reproduce the error: (Python Version: 3.14.X) - Install ``accounting_firm`` industry with demo data Code for server action to generate the error: ```py for i in [1]: try: pass except…
Steps to reproduce the error: (Python Version: 3.14.X)
- Install ``accounting_firm`` industry with demo data
Code for server action to generate the error:
```py
for i in [1]:
try:
pass
except Exception:
pass
if i:
pass
```
Traceback:
```py
ValueError: forbidden opcode(s) in'...': JUMP_BACKWARD_NO_INTERRUPT
```
https://github.com/odoo/industry/blob/701f7595453772e42ce72aa75df346a504e5bd82/accounting_firm/demo/ir_actions_server.xml#L105-L108
The server action contains a ``for`` loop with a ``try/except`` block followed by additional statements in the loop body,
this combination generates the ``JUMP_BACKWARD_NO_INTERRUPT`` opcode, which is not included in ``_SAFE_OPCODES`` at [1].
When the server action is evaluated by ``safe_eval``, it calls the ``assert_valid_codeobj`` method, which validates the compiled bytecode against ``_SAFE_OPCODES``. Since ``JUMP_BACKWARD_NO_INTERRUPT`` is not present in the allowed opcodes, ``assert_valid_codeobj()`` raises a ``ValueError`` at [2] before the server action is executed .
Solution:
``JUMP_BACKWARD_NO_INTERRUPT`` opcode is added in the ``_SAFE_OPCODES`` and it is also added in the ``_SAFE_QWEB_OPCODES``.
It was added in Python 3.11: https://docs.python.org/3/whatsnew/3.11.html#new-opcodes
``JUMP_BACKWARD_NO_INTERRUPT`` is a control-flow opcode that only changes
the interpreter's execution flow by jumping back to a previous instruction.
It is the equivalent to ``JUMP_BACKWARD`` opcode. Its only semantic difference is
that the interpreter does not perform an interrupt check at that instruction.
It does not introduce any new capabilities or perform operations such as
attribute access, imports, function calls, or object creation.
Ref: https://docs.python.org/3.12/library/dis.html#opcode-JUMP_BACKWARD_NO_INTERRUPT
Similar commit that adds some necessary opcodes:
https://github.com/odoo/odoo/commit/86498d24946e510025add5d24ef0d4bcce8ad05f
[1]: https://github.com/odoo/odoo/blob/2ccbc4660077bd48529e9de43d4309fbfafc75ca/odoo/tools/safe_eval.py#L135
[2]: https://github.com/odoo/odoo/blob/2ccbc4660077bd48529e9de43d4309fbfafc75ca/odoo/tools/safe_eval.py#L244-L246
sentry-7614026125
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#277192### Issue: A partner with VAT set to '/' incorrectly matches a fiscal position with `VAT required`, instead of one without The '/' value is the placeholder suggested by the UI to indicate that the partner is known to have no VAT, but it was treated as a valid VAT by the fiscal position matching logic ### Cause: `_get_fpos_ranking_functions` uses `_get_vat_valid` to rank fiscal positions based on VAT presence `_get_vat_valid` returned `True` for any non-empty VAT value, including '/' Th
Original PR description
### Issue: A partner with VAT set to '/' incorrectly matches a fiscal position with `VAT required`, instead of one without The '/' value is the placeholder suggested by the UI to indicate that the…
### Issue: A partner with VAT set to '/' incorrectly matches a fiscal position with `VAT required`, instead of one without The '/' value is the placeholder suggested by the UI to indicate that the partner is known to have no VAT, but it was treated as a valid VAT by the fiscal position matching logic ### Cause: `_get_fpos_ranking_functions` uses `_get_vat_valid` to rank fiscal positions based on VAT presence `_get_vat_valid` returned `True` for any non-empty VAT value, including '/' The '/' case was not excluded, causing it to be treated as a valid VAT number ### Steps to reproduce: - Install `account` - Create two fiscal positions with auto-apply: -- Name: FP VAT, VAT required: True, sequence: 1 -- Name: FP no VAT, VAT required: False, sequence: 2 - Create a partner with VAT: '/' - Create an Invoice for that partner and check the Fiscal Position Before the fix, `FP VAT` is selected instead of `FP no VAT` opw-6204531 Forward-Port-Of: odoo/odoo#280494 Forward-Port-Of: odoo/odoo#280065
## Issue When logging timesheet from the *Recorded* smart button on a Sale Order containing multiple service items, the recorded hours will be added to the first sale order line, regardless of the task ID set on the timesheet entries. ## Steps to reproduce 1. Install *Sales Timesheet* (`sale_timesheet`) 2. Create a Product P: - *Product Type*: Service - *Create on Order*: Task - *Project*: Any 3. Create a SO: - *Customer*: Any - Add the product P on two different
Original PR description
## Issue When logging timesheet from the *Recorded* smart button on a Sale Order containing multiple service items, the recorded hours will be added to the first sale order line, regardless of the…
## Issue
When logging timesheet from the *Recorded* smart button on a Sale Order containing multiple service items, the recorded hours will be added to the first sale order line, regardless of the task ID set on the timesheet entries.
## Steps to reproduce
1. Install *Sales Timesheet* (`sale_timesheet`)
2. Create a Product P:
- *Product Type*: Service
- *Create on Order*: Task
- *Project*: Any
3. Create a SO:
- *Customer*: Any
- Add the product P on two different lines and give them two different descriptions D1 and D2
- Confirm the SO, this will create two tasks with the names D1 and D2
4. On the SO, click the *Recorded* smart button and create two entries:
1. Task D1, 2 hours spent
2. Tsk D2, 3 hours spent
5. **Back on the SO, there are 5 hours registered for the first SOL (with the description D1), which does not match the entries we created from the smart button.**
It is worth noting that when we create the Timesheets entries from the project itself (instead of the SO's smart button), the hours are correctly distributed among the different SOLs.
## Cause
The SOL linked to the timesheet entry (`account.analytic.line`) is computed by `_compute_so_line`:
https://github.com/odoo/odoo/blob/8b102f500f5a122e99b07a08cc43814e7c6f0f75/addons/sale_timesheet/models/hr_timesheet.py#L79-L82
This method sets the correct SOL under the condition that `is_so_line_edited` is False and `_is_no_billed()` returns True.
When opening the *Recorded* smart button from a SO, the `is_so_line_edited` is set to True by default, even if no SOL was modified.
https://github.com/odoo/odoo/blob/8b102f500f5a122e99b07a08cc43814e7c6f0f75/addons/sale_timesheet/models/sale_order.py#L113-L117
As that value is never set to False, when trying to compute the SOL for the timesheet entry, the entry is skipped and the default SOL (which is the first one) is used instead.
opw-6133473
Forward-Port-Of: odoo/odoo#274759Before this commit, in some cases, the order created for printing cash move was saved to the IndexedDB and then later loaded from IndexedDB, which caused the order gets synced to the backend but missing some required fields, like preset or pricelist. opw-5969602 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#262248
Original PR description
Before this commit, in some cases, the order created for printing cash move was saved to the IndexedDB and then later loaded from IndexedDB, which caused the order gets synced to the backend but missing some required fields, like preset or pricelist. opw-5969602 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#262248
When we create a database with `-i web --skip-auto-install`, we run into CSS compilation errors due to undefined variables `$black` and `$gray-200`. This commit adds these two definitions. opw-6398398 Forward-Port-Of: odoo/odoo#279753 Forward-Port-Of: odoo/odoo#279287
Original PR description
When we create a database with `-i web --skip-auto-install`, we run into CSS compilation errors due to undefined variables `$black` and `$gray-200`. This commit adds these two definitions. opw-6398398 Forward-Port-Of: odoo/odoo#279753 Forward-Port-Of: odoo/odoo#279287
Before this commit, this test sometimes failed because it couldn't find a dialog containing "camera" within 200ms. In the test scenario, we click to open the BarcodeDialog, which uses the BarcodeVideoScanner. The latter, in its `onMounted`, checks whether it has the necessary permission, which isn't the case as the `getUserMedia` function is mocked in the test to return a rejected promise. As a consequence, the `onError` callback given in props is called, which changes the state of the parent
Original PR description
Before this commit, this test sometimes failed because it couldn't find a dialog containing "camera" within 200ms. In the test scenario, we click to open the BarcodeDialog, which uses the…
Before this commit, this test sometimes failed because it couldn't find a dialog containing "camera" within 200ms. In the test scenario, we click to open the BarcodeDialog, which uses the BarcodeVideoScanner. The latter, in its `onMounted`, checks whether it has the necessary permission, which isn't the case as the `getUserMedia` function is mocked in the test to return a rejected promise. As a consequence, the `onError` callback given in props is called, which changes the state of the parent component, which re-renders itself so display "Unable to access camera" instead of the BarcodeVideoScanner. To make this test more robust, we do 2 things: 1) load the zxing library before running the test, which avoids the BarcodeVideoScanner component to load it in onWillStart. 2) explicitly wait for the 2 animationFrames, as in the scenario, we must wait for the BarcodeDialog to be rendered twice, and those renderings are now synchronous. runbot error-237933 Forward-Port-Of: odoo/odoo#280613
Steps to reproduce ------------------ 1. Set the company document layout to DIN5008 2. Open a delivery and print the delivery slip The title is missing on the DIN5008 layout, we only have the reference `WH/OUT/00001`. What happens ------------ The DIN5008 layout hides the body title with css and prints its own `h2` instead, from the `din5008_document_title` variable, and uses `o.name` (the picking number) when this variable is not set. The commit 0058d1cf7655 added the title on the
Original PR description
Steps to reproduce ------------------ 1. Set the company document layout to DIN5008 2. Open a delivery and print the delivery slip The title is missing on the DIN5008 layout, we only have the reference `WH/OUT/00001`. What happens ------------ The DIN5008 layout hides the body title with css and prints its own `h2` instead, from the `din5008_document_title` variable, and uses `o.name` (the picking number) when this variable is not set. The commit 0058d1cf7655 added the title on the standard delivery report with `picking_type_id._get_code_report_name()`, but `l10n_din5008_stock` was not updated to set `din5008_document_title`, so on DIN5008 we only get the number. The fix ------- We set it the same way as the other layouts, hence we get back the full title `Delivery Note WH/OUT/00001`. opw-6299248 Forward-Port-Of: odoo/odoo#277444
The compute method should not depend whether or not the employee is active or not. Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
The compute method should not depend whether or not the employee is active or not. Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Bug === Since a4efb745e9f4ba24dfa01f1e17e65f1a7cac90a5 , we cannot insert properties. The reason is that we try to get the field definition from the inserted QWeb expression (eg `object.properties.get('property_product', env['product']).name`) which crash. Task-6311655 Forward-Port-Of: odoo/odoo#279373
Original PR description
Bug
===
Since a4efb745e9f4ba24dfa01f1e17e65f1a7cac90a5 , we cannot insert properties. The reason is that we try to get the field definition from the inserted QWeb expression (eg `object.properties.get('property_product', env['product']).name`) which crash.
Task-6311655
Forward-Port-Of: odoo/odoo#279373# Issue The test `test_absence_management_with_timeoff` fails if demo data is enabled. It was introduced by : https://github.com/odoo/odoo/pull/272089 # Cause `self.env.user` has an 'Europe/Brussels' tz when demo data is enabled. This changes the date used in the `search_count` at then end of the test : 2026-01-14 00:00 => 2026-01-13 23:00 So we check at the wrong date runbot-941523 Forward-Port-Of: odoo/odoo#279764
Original PR description
# Issue The test `test_absence_management_with_timeoff` fails if demo data is enabled. It was introduced by : https://github.com/odoo/odoo/pull/272089 # Cause `self.env.user` has an 'Europe/Brussels' tz when demo data is enabled. This changes the date used in the `search_count` at then end of the test : 2026-01-14 00:00 => 2026-01-13 23:00 So we check at the wrong date runbot-941523 Forward-Port-Of: odoo/odoo#279764
See commit messages for details. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
See commit messages for details. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Miscellaneous changes
load_data() reads product.pricelist.item_ids as a field, which applies _base_domain_item_ids()'s dotted active conditions. Since this runs as the cashier, never sudo, the ORM injects ir.rules into those conditions, forcing a non-hashable subquery that Postgres re-scans once per pricelist item. Cost scales with items times catalog size, freezing session opening on large catalogs. product.pricelist.item is already loaded separately via a plain, indexed pricelist_id domain. Reuse it to build ite
Original PR description
load_data() reads product.pricelist.item_ids as a field, which applies _base_domain_item_ids()'s dotted active conditions. Since this runs as the cashier, never sudo, the ORM injects ir.rules into those conditions, forcing a non-hashable subquery that Postgres re-scans once per pricelist item. Cost scales with items times catalog size, freezing session opening on large catalogs.
product.pricelist.item is already loaded separately via a plain, indexed pricelist_id domain. Reuse it to build item_ids instead of reading the field, avoiding the join/subquery entirely.
before after speedup
item_ids read 475.8s 2.44s ~195x
load_data() (full) unbounded 8.7s hang -> ok
opw-6391847
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#27725416 changes
Resolved issues and error corrections
### Issue: A user who can modify `tax_exigibility` but does not have `account.group_account_readonly` is blocked from switching to `on_payment` There is no way to complete the operation as the account field is not visible and cannot be filled before saving ### Cause: When `tax_exigibility` is set to `on_payment`, the view expects `cash_basis_transition_account_id` to be filled before saving`_constrains_cash_basis_transition_account` then validates that the account is reconcilable `cas
Original PR description
### Issue: A user who can modify `tax_exigibility` but does not have `account.group_account_readonly` is blocked from switching to `on_payment` There is no way to complete the operation as the…
### Issue: A user who can modify `tax_exigibility` but does not have `account.group_account_readonly` is blocked from switching to `on_payment` There is no way to complete the operation as the account field is not visible and cannot be filled before saving ### Cause: When `tax_exigibility` is set to `on_payment`, the view expects `cash_basis_transition_account_id` to be filled before saving`_constrains_cash_basis_transition_account` then validates that the account is reconcilable `cash_basis_transition_account_id` was restricted to `account.group_account_readonly`, hiding it from other users The field is never rendered, so it cannot be filled The `required` constraint is never evaluated client-side and `_constrains_cash_basis_transition_account` raises a `ValidationError` on save because the account is empty There is no reason to restrict `cash_basis_transition_account_id` independently — if a user can modify `tax_exigibility`, they must also be able to set the linked account ### Steps to reproduce: - Install `account` - Enable Cash Basis in Settings (On RunBot ensure no default account is set) - Enable Developer Mode in Settings - Go to Settings > Users & Companies > Users - Open the current user and disable both: `Show Accounting Features - Readonly` and `Show Full Accounting Features` (if set) - Go to Invoicing > Configuration > Taxes - Open any tax and go to Advanced Options - Change Tax Exigibility to Based on Payment Before the fix, the account selector is not displayed and saving raises an error opw-6359173 Forward-Port-Of: odoo/odoo#274728
Before this commit, the `empty a many2one field in list view` test sometimes failed, because the many2one value wasn't correctly unset (`first record` was selected). This happened because we cleared the input and automatically validated (typically with tab). However, it could happen that the validation occurred after the dropdown was opened, so the first value of the dropdown was selected. As a matter of fact, adding `await runAllTimers()` after clearing the input is a way to make the test fail
Original PR description
Before this commit, the `empty a many2one field in list view` test sometimes failed, because the many2one value wasn't correctly unset (`first record` was selected). This happened because we cleared…
Before this commit, the `empty a many2one field in list view` test sometimes failed, because the many2one value wasn't correctly unset (`first record` was selected). This happened because we cleared the input and automatically validated (typically with tab). However, it could happen that the validation occurred after the dropdown was opened, so the first value of the dropdown was selected. As a matter of fact, adding `await runAllTimers()` after clearing the input is a way to make the test fail deterministically. This commit avoids the issue by emptying the many2one without validation, so it basically only set the input value to the empty string, but doesn't tab/enter or anything else, hence it never selects an unwanted value. runbot error-941430 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280351
**Steps to reproduce:** - Go to any view where you can send mails (eg sale orders) - Send a first mail to multiple recipients so they are added automatically on the next mail. - Open a new mail, confirm that the recipients are present, add a selectable attachment (eg a PDF), then click on it. - Remove any amount of recipients then send the mail. - You will see that the recipients are added back and the mail is sent to them. **Behavior:** Currently whenever a user clicks on an attachment
Original PR description
**Steps to reproduce:** - Go to any view where you can send mails (eg sale orders) - Send a first mail to multiple recipients so they are added automatically on the next mail. - Open a new mail,…
**Steps to reproduce:**
- Go to any view where you can send mails (eg sale orders)
- Send a first mail to multiple recipients so they are added automatically on the next mail.
- Open a new mail, confirm that the recipients are present, add a selectable attachment (eg a PDF), then click on it.
- Remove any amount of recipients then send the mail.
- You will see that the recipients are added back and the mail is sent to them.
**Behavior:**
Currently whenever a user clicks on an attachment in a mail composer, the systems considers that the user might be trying
to leave the page and will trigger an `urgentSave()`, and further down the line a `web_save()`.
The behavior when a web_save() is triggered is to create a record if there isnt currently one, and otherwise to write the modified values onto the record, using commands.
The recipients for the mail are added by default, which is represented by a list of `[4, id]`add commands, that will be written on the record created in the first `web_save`, however this list is not correctly emptied after the first `_save()`.
If the list is present within `this._changes` then it is correctly cleared, but in the case where no changes were made, the within `this._values['partner_ids']` still contains the commands.
So when we later assign `this.data = { ...this._values };`, `this.data['partner_ids']` now contains our uncleared list of commands.
https://github.com/odoo/odoo/blob/f3e407c6a58abd2ddba42f26fcbd1928da63cb63/addons/web/static/src/model/relational_model/record.js#L1222-L1230
And when we compute changes['partner_ids'] in our next iteration, we find ourselves with our command list again.
https://github.com/odoo/odoo/blob/f3e407c6a58abd2ddba42f26fcbd1928da63cb63/addons/web/static/src/model/relational_model/record.js#L1317-L1322
So when we then try to remove a recipient tag, the new delete command `[3, id]`
just gets canceled out with the already present add command.
And the `write()` in `web_save()` only writes add commands of already present partners, which doesn't do anything.
----
This commit adds a line to ensure commands inside `_values` are cleared
opw-6304713
Forward-Port-Of: odoo/odoo#280533
Forward-Port-Of: odoo/odoo#278876This is rather an attempt of fix, as we couldn't reproduce the error locally or in a multi build., so we have no guarantee that this strenghtens the test. The test sometimes fails as `.o_crop_icon` can't be found within 200ms. Before that, we wait for the video element to be ready (we rely on a patch of the `isVideoReady` method of the component to know that the video is ready). Once it is, the isReady flag in the state is set to true and the CropOverlay component renders its `o_crop_icon` el
Original PR description
This is rather an attempt of fix, as we couldn't reproduce the error locally or in a multi build., so we have no guarantee that this strenghtens the test. The test sometimes fails as `.o_crop_icon`…
This is rather an attempt of fix, as we couldn't reproduce the error locally or in a multi build., so we have no guarantee that this strenghtens the test. The test sometimes fails as `.o_crop_icon` can't be found within 200ms. Before that, we wait for the video element to be ready (we rely on a patch of the `isVideoReady` method of the component to know that the video is ready). Once it is, the isReady flag in the state is set to true and the CropOverlay component renders its `o_crop_icon` element. Our guess is that we may sometimes early return in `isVideoReady`, because the component has been destroyed (a new rendering might be on the way). To ensure that we don't take that as the ready signal in the test, we now only consider that we're ready if isVideoReady returned true (i.e. no early return). runbot error-241798 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280329
When a customer invoice is digitized through OCR, the salesperson may be set to the "Public User" instead of the internal user who uploaded the document. Steps to reproduce: - Upload a PDF invoice of an existing customer - Send the PDF to OCR - Reload the page Issue: Observe the Salesperson field: it is set to the Public User. Analysis: This occurs because when the partner is filled in, the compute of the salesperson will trigger. On SaaS this happens through the extraction c
Original PR description
When a customer invoice is digitized through OCR, the salesperson may be set to the "Public User" instead of the internal user who uploaded the document. Steps to reproduce: - Upload a PDF invoice of an existing customer - Send the PDF to OCR - Reload the page Issue: Observe the Salesperson field: it is set to the Public User. Analysis: This occurs because when the partner is filled in, the compute of the salesperson will trigger. On SaaS this happens through the extraction completion webhook a public route processed in sudo. that does not change the current user (public user). As self.env.user is the fallback of the compute, it may be set as salesperson. opw-6296330 Forward-Port-Of: odoo/odoo#279724
**Issue** Confirming a SO that generates a batch of MOs from a BoM with a batch size could lead to creating an invalid number of pickings: all the MOs end up sharing a single picking instead of getting one each. **Steps to reproduce** - Use 2-step manufacturing - Create a storable product with the MTO + Manufacture routes - Add a BOM that has a batch size of 10 that consumes one component - Create and confirm a SO of 100 units of that product -> 10 MOs are created, but each one points
Original PR description
**Issue** Confirming a SO that generates a batch of MOs from a BoM with a batch size could lead to creating an invalid number of pickings: all the MOs end up sharing a single picking instead of…
**Issue** Confirming a SO that generates a batch of MOs from a BoM with a batch size could lead to creating an invalid number of pickings: all the MOs end up sharing a single picking instead of getting one each. **Steps to reproduce** - Use 2-step manufacturing - Create a storable product with the MTO + Manufacture routes - Add a BOM that has a batch size of 10 that consumes one component - Create and confirm a SO of 100 units of that product -> 10 MOs are created, but each one points to the same "Pick Components" transfer instead of getting its own. **Cause** This commit dd6ee071f949752d31497d9f975ba7fc41ebcd98 batches the confirm of productions: https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/mrp/models/stock_rule.py#L120 thus `assign_picking` is called in batches: https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/mrp/models/mrp_production.py#L1653 https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1736-L1737 https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1550-L1556 since, the `reference_ids` are the same for each MO/stock.move (they all come from the same procurement): https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1712-L1713 Consequently, all the moves end up in the same recordset `moves`: https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1527 Creating only one picking: https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1577 opw-6403427 Forward-Port-Of: odoo/odoo#279179
Issue ----- Branch companies can have quants of a product limited to their parent company, but they don't show in the per location report. Steps to reproduce ----- - Create a branch company - Create a WH for the branch - Create a tracked product - Company set to parent only - Switch to the branch company - Add a quant of the product in branch stock - Open Inventory > Reporting > Locations > The product is not shown although there is a quant in the branch Cause ----- The m
Original PR description
Issue ----- Branch companies can have quants of a product limited to their parent company, but they don't show in the per location report. Steps to reproduce ----- - Create a branch company - Create…
Issue ----- Branch companies can have quants of a product limited to their parent company, but they don't show in the per location report. Steps to reproduce ----- - Create a branch company - Create a WH for the branch - Create a tracked product - Company set to parent only - Switch to the branch company - Add a quant of the product in branch stock - Open Inventory > Reporting > Locations > The product is not shown although there is a quant in the branch Cause ----- The menu button triggers `action_view_quants` https://github.com/odoo/odoo/blob/03a3662212f094158f885ae6545009fd0a74d3cb/addons/stock/views/stock_quant_views.xml#L493-L495 https://github.com/odoo/odoo/blob/03a3662212f094158f885ae6545009fd0a74d3cb/addons/stock/models/stock_quant.py#L399-L402 The problem here comes from the fact that in `_get_quants_action`, we limit the products to those of only the active companies, instead of allowing to view those of parent companies aswell. https://github.com/odoo/odoo/blob/03a3662212f094158f885ae6545009fd0a74d3cb/addons/stock/models/stock_quant.py#L1330 Such a change works because the domain is specifically for the product's (`product_id.company_id`) and not the location's. ----- Ticket: opw-6131525 Forward-Port-Of: odoo/odoo#280083 Forward-Port-Of: odoo/odoo#277531
Version: -------- - 19.0+ Steps to reproduce: ------------------- - Install `purchase` and `stock` - Create a Purchase Order for a storable or consumable product - Set the ordered quantity to a negative value (e.g. `-10`) - Confirm the Purchase Order. - Open the Deliveries and search using the Purchase Order name Issue: ------ An outgoing return picking is correctly created for 10 units, but an additional empty incoming receipt is also generated and linked to the Purchase Order
Original PR description
Version: -------- - 19.0+ Steps to reproduce: ------------------- - Install `purchase` and `stock` - Create a Purchase Order for a storable or consumable product - Set the ordered quantity to a…
Version: -------- - 19.0+ Steps to reproduce: ------------------- - Install `purchase` and `stock` - Create a Purchase Order for a storable or consumable product - Set the ordered quantity to a negative value (e.g. `-10`) - Confirm the Purchase Order. - Open the Deliveries and search using the Purchase Order name Issue: ------ An outgoing return picking is correctly created for 10 units, but an additional empty incoming receipt is also generated and linked to the Purchase Order. Cause: ------ When a Purchase Order is confirmed, `purchase.order.button_approve()` calls `purchase.order._create_picking()`: https://github.com/odoo/odoo/blob/ed0d1bd21131920cb9c7b2ae69d7baedc2818e85/addons/purchase_stock/models/purchase_order.py#L179 Inside `_create_picking()`, if no open picking exists yet, Then first creates an incoming receipt using `_prepare_picking()`: https://github.com/odoo/odoo/blob/ed0d1bd21131920cb9c7b2ae69d7baedc2818e85/addons/purchase_stock/models/purchase_order.py#L382 The generated receipt is configured with: - source location: Vendor - destination location: Stock - picking type: Incoming - origin: Purchase Order reference Stock moves are then created on that receipt through: https://github.com/odoo/odoo/blob/ed0d1bd21131920cb9c7b2ae69d7baedc2818e85/addons/purchase_stock/models/purchase_order.py#L387-L388 For negative PO quantities, the move is initially created with a negative demand: ``` Vendor -> Stock, quantity -10 ```` During `stock.move._action_confirm()`, stock identifies such moves in `neg_r_moves`: [https://github.com/odoo/odoo/blob/ed0d1bd21131920cb9c7b2ae69d7baedc2818e85/addons/stock/models/stock_move.py#L1604](https://github.com/odoo/odoo/blob/ed0d1bd21131920cb9c7b2ae69d7baedc2818e85/addons/stock/models/stock_move.py#L1604) Those moves are automatically converted into return moves by: * swapping source and destination locations, * converting the quantity to positive, * assigning the return picking type when available: [https://github.com/odoo/odoo/blob/ed0d1bd21131920cb9c7b2ae69d7baedc2818e85/addons/stock/models/stock_move.py#L1612-L1614](https://github.com/odoo/odoo/blob/ed0d1bd21131920cb9c7b2ae69d7baedc2818e85/addons/stock/models/stock_move.py#L1612-L1614) The resulting move becomes: ``` Stock -> Vendor, quantity 10 ``` The converted move is then reassigned through: [https://github.com/odoo/odoo/blob/ed0d1bd21131920cb9c7b2ae69d7baedc2818e85/addons/stock/models/stock_move.py#L1630](https://github.com/odoo/odoo/blob/ed0d1bd21131920cb9c7b2ae69d7baedc2818e85/addons/stock/models/stock_move.py#L1630) This correctly creates the outgoing return picking. However, the original incoming receipt created earlier by `purchase_stock` is left without any moves, resulting in an empty ghost receipt linked to the Purchase Order. Solution: --------- - Track whether `_create_picking()` created a new incoming receipt during the current confirmation flow. - After stock move confirmation, if that newly-created receipt no longer contains any moves, unlink it and remove it from the pickings to confirm. - This preserves the valid outgoing return picking while preventing empty incoming receipts from being generated. NOTE: ------ This Issue not reproduce from saas-19.3. Fix in this [commit](https://github.com/odoo-dev/odoo/commit/81caaaa3f7212ad5d9d72cd446b65c5fb0a9bef9) --- opw-6131329 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#265704
### Steps to reproduce: - Create 3 AVCO products: Super Kit, Kit, Comp - Super Kit BoM: 2 x Kit - Kit BoM: 1 x Comp - Create and confirm a purchase order for 1 X Super Kit at 100 - Validate the receipt of 2 Comp - Go to the valuation > Both units of Comp are valued at 100 for a total of 200 ### Cause of the issue: The issue has been introduced by: https://github.com/odoo/odoo/pull/158849/changes/713701a5035d342263e3fef2a2819b5696b6d063 To be more precise, the price unit of each u
Original PR description
### Steps to reproduce: - Create 3 AVCO products: Super Kit, Kit, Comp - Super Kit BoM: 2 x Kit - Kit BoM: 1 x Comp - Create and confirm a purchase order for 1 X Super Kit at 100 - Validate the…
### Steps to reproduce: - Create 3 AVCO products: Super Kit, Kit, Comp - Super Kit BoM: 2 x Kit - Kit BoM: 1 x Comp - Create and confirm a purchase order for 1 X Super Kit at 100 - Validate the receipt of 2 Comp - Go to the valuation > Both units of Comp are valued at 100 for a total of 200 ### Cause of the issue: The issue has been introduced by: https://github.com/odoo/odoo/pull/158849/changes/713701a5035d342263e3fef2a2819b5696b6d063 To be more precise, the price unit of each unit of Comp is expected to be computed by the `_get_price_unit`. This method used to rely on the `product_qty` appropriately: https://github.com/odoo/odoo/pull/158849/changes/713701a5035d342263e3fef2a2819b5696b6d063#diff-687527af1723e60816358020c4d83687479df62ca0cfd71079f0a82afb4b3efeL27 However, backorder adapt the move demand and hence did not provide the appropriate demand in this flow that computation logic was changed to rely on the `bom` and `bom_line` quantities: https://github.com/odoo/odoo/blob/29977a6a80442af49ecefa7fef54f085483d8f77/addons/purchase_mrp/models/stock_move.py#L20-L40 This new computation is not correct in case of nested boms since the `bom_line` only carries the unit demand on the last explosion stage. ### Additional issue: If nested kit boms lead to the creation of 2 moves with the same `cost_share` and `bom_line_id`, these moves will be merged without summing their `cost_share` leading to an under pricing of the kit since its related `stock_move`'s `cost_share` will not sum up to 100 percents anymore. This issue is tested in `test_avco_purchase_nested_kit_explode_cost_share_backorder_2` and fixed similarly to demand merging: https://github.com/odoo/odoo/blob/613f3cb7b2f4813ce6c8f53718a6cca841b081ad/addons/stock/models/stock_move.py#L1122-L1134 ### Note: We also modify the test `test_valuation_with_backorder` to be understandable and to make appropriate asserts. opw-6253776 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279890 Forward-Port-Of: odoo/odoo#276242
Steps to reproduce the bug: - Create three storable products C1 ($10), C2 ($20), C3 ($5) - Create a product P1 with a BoM: 1x C1 + 1x C2 - Create a Manufacturing Order for P1 and validate it - Unlock the MO (Settings > Unlock) - Add C3 as an extra component on the unlocked MO - Open the MO overview Problem: The extra move had value=0 after creation, causing the unit_cost in the MO overview to appear as 0. When a move is added to a done picking or MO it is created with state='done' an
Original PR description
Steps to reproduce the bug: - Create three storable products C1 ($10), C2 ($20), C3 ($5) - Create a product P1 with a BoM: 1x C1 + 1x C2 - Create a Manufacturing Order for P1 and validate it - Unlock…
Steps to reproduce the bug: - Create three storable products C1 ($10), C2 ($20), C3 ($5) - Create a product P1 with a BoM: 1x C1 + 1x C2 - Create a Manufacturing Order for P1 and validate it - Unlock the MO (Settings > Unlock) - Add C3 as an extra component on the unlocked MO - Open the MO overview Problem: The extra move had value=0 after creation, causing the unit_cost in the MO overview to appear as 0. When a move is added to a done picking or MO it is created with state='done' and quantity set immediately. This triggers _set_quantity_done, which creates the move line and calls _set_value(correction_quantity=delta). Inside _set_value, for outgoing moves with a correction_quantity, the code computes: previous_qty = move.quantity - correction_quantity Since the move had no prior quantity, previous_qty=0. The original code then computed ratio=0 and applied move.value += 0, leaving value=0 instead of computing it from scratch. Solution: When previous_qty=0, skip the ratio branch and fall through to the existing from-scratch computation (standard_price * _get_valued_qty() for AVCO/standard costing, _run_fifo() for FIFO). opw-6377393 Forward-Port-Of: odoo/odoo#276303
Steps to reproduce the error: (Python Version: 3.14.X) - Install ``accounting_firm`` industry with demo data Code for server action to generate the error: ```py for i in [1]: try: pass except Exception: pass if i: pass ``` Traceback: ```py ValueError: forbidden opcode(s) in'...': JUMP_BACKWARD_NO_INTERRUPT ``` https://github.com/odoo/industry/blob/701f7595453772e42ce72aa75df346a504e5bd82/accounting_firm/demo/ir_actions_server.xml#L105-L10
Original PR description
Steps to reproduce the error: (Python Version: 3.14.X) - Install ``accounting_firm`` industry with demo data Code for server action to generate the error: ```py for i in [1]: try: pass except…
Steps to reproduce the error: (Python Version: 3.14.X)
- Install ``accounting_firm`` industry with demo data
Code for server action to generate the error:
```py
for i in [1]:
try:
pass
except Exception:
pass
if i:
pass
```
Traceback:
```py
ValueError: forbidden opcode(s) in'...': JUMP_BACKWARD_NO_INTERRUPT
```
https://github.com/odoo/industry/blob/701f7595453772e42ce72aa75df346a504e5bd82/accounting_firm/demo/ir_actions_server.xml#L105-L108
The server action contains a ``for`` loop with a ``try/except`` block followed by additional statements in the loop body,
this combination generates the ``JUMP_BACKWARD_NO_INTERRUPT`` opcode, which is not included in ``_SAFE_OPCODES`` at [1].
When the server action is evaluated by ``safe_eval``, it calls the ``assert_valid_codeobj`` method, which validates the compiled bytecode against ``_SAFE_OPCODES``. Since ``JUMP_BACKWARD_NO_INTERRUPT`` is not present in the allowed opcodes, ``assert_valid_codeobj()`` raises a ``ValueError`` at [2] before the server action is executed .
Solution:
``JUMP_BACKWARD_NO_INTERRUPT`` opcode is added in the ``_SAFE_OPCODES`` and it is also added in the ``_SAFE_QWEB_OPCODES``.
It was added in Python 3.11: https://docs.python.org/3/whatsnew/3.11.html#new-opcodes
``JUMP_BACKWARD_NO_INTERRUPT`` is a control-flow opcode that only changes
the interpreter's execution flow by jumping back to a previous instruction.
It is the equivalent to ``JUMP_BACKWARD`` opcode. Its only semantic difference is
that the interpreter does not perform an interrupt check at that instruction.
It does not introduce any new capabilities or perform operations such as
attribute access, imports, function calls, or object creation.
Ref: https://docs.python.org/3.12/library/dis.html#opcode-JUMP_BACKWARD_NO_INTERRUPT
Similar commit that adds some necessary opcodes:
https://github.com/odoo/odoo/commit/86498d24946e510025add5d24ef0d4bcce8ad05f
[1]: https://github.com/odoo/odoo/blob/2ccbc4660077bd48529e9de43d4309fbfafc75ca/odoo/tools/safe_eval.py#L135
[2]: https://github.com/odoo/odoo/blob/2ccbc4660077bd48529e9de43d4309fbfafc75ca/odoo/tools/safe_eval.py#L244-L246
sentry-7614026125
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#277192### Issue: A partner with VAT set to '/' incorrectly matches a fiscal position with `VAT required`, instead of one without The '/' value is the placeholder suggested by the UI to indicate that the partner is known to have no VAT, but it was treated as a valid VAT by the fiscal position matching logic ### Cause: `_get_fpos_ranking_functions` uses `_get_vat_valid` to rank fiscal positions based on VAT presence `_get_vat_valid` returned `True` for any non-empty VAT value, including '/' Th
Original PR description
### Issue: A partner with VAT set to '/' incorrectly matches a fiscal position with `VAT required`, instead of one without The '/' value is the placeholder suggested by the UI to indicate that the…
### Issue: A partner with VAT set to '/' incorrectly matches a fiscal position with `VAT required`, instead of one without The '/' value is the placeholder suggested by the UI to indicate that the partner is known to have no VAT, but it was treated as a valid VAT by the fiscal position matching logic ### Cause: `_get_fpos_ranking_functions` uses `_get_vat_valid` to rank fiscal positions based on VAT presence `_get_vat_valid` returned `True` for any non-empty VAT value, including '/' The '/' case was not excluded, causing it to be treated as a valid VAT number ### Steps to reproduce: - Install `account` - Create two fiscal positions with auto-apply: -- Name: FP VAT, VAT required: True, sequence: 1 -- Name: FP no VAT, VAT required: False, sequence: 2 - Create a partner with VAT: '/' - Create an Invoice for that partner and check the Fiscal Position Before the fix, `FP VAT` is selected instead of `FP no VAT` opw-6204531 Forward-Port-Of: odoo/odoo#280494 Forward-Port-Of: odoo/odoo#280065
## Issue When logging timesheet from the *Recorded* smart button on a Sale Order containing multiple service items, the recorded hours will be added to the first sale order line, regardless of the task ID set on the timesheet entries. ## Steps to reproduce 1. Install *Sales Timesheet* (`sale_timesheet`) 2. Create a Product P: - *Product Type*: Service - *Create on Order*: Task - *Project*: Any 3. Create a SO: - *Customer*: Any - Add the product P on two different
Original PR description
## Issue When logging timesheet from the *Recorded* smart button on a Sale Order containing multiple service items, the recorded hours will be added to the first sale order line, regardless of the…
## Issue
When logging timesheet from the *Recorded* smart button on a Sale Order containing multiple service items, the recorded hours will be added to the first sale order line, regardless of the task ID set on the timesheet entries.
## Steps to reproduce
1. Install *Sales Timesheet* (`sale_timesheet`)
2. Create a Product P:
- *Product Type*: Service
- *Create on Order*: Task
- *Project*: Any
3. Create a SO:
- *Customer*: Any
- Add the product P on two different lines and give them two different descriptions D1 and D2
- Confirm the SO, this will create two tasks with the names D1 and D2
4. On the SO, click the *Recorded* smart button and create two entries:
1. Task D1, 2 hours spent
2. Tsk D2, 3 hours spent
5. **Back on the SO, there are 5 hours registered for the first SOL (with the description D1), which does not match the entries we created from the smart button.**
It is worth noting that when we create the Timesheets entries from the project itself (instead of the SO's smart button), the hours are correctly distributed among the different SOLs.
## Cause
The SOL linked to the timesheet entry (`account.analytic.line`) is computed by `_compute_so_line`:
https://github.com/odoo/odoo/blob/8b102f500f5a122e99b07a08cc43814e7c6f0f75/addons/sale_timesheet/models/hr_timesheet.py#L79-L82
This method sets the correct SOL under the condition that `is_so_line_edited` is False and `_is_no_billed()` returns True.
When opening the *Recorded* smart button from a SO, the `is_so_line_edited` is set to True by default, even if no SOL was modified.
https://github.com/odoo/odoo/blob/8b102f500f5a122e99b07a08cc43814e7c6f0f75/addons/sale_timesheet/models/sale_order.py#L113-L117
As that value is never set to False, when trying to compute the SOL for the timesheet entry, the entry is skipped and the default SOL (which is the first one) is used instead.
opw-6133473
Forward-Port-Of: odoo/odoo#274759Before this commit, in some cases, the order created for printing cash move was saved to the IndexedDB and then later loaded from IndexedDB, which caused the order gets synced to the backend but missing some required fields, like preset or pricelist. opw-5969602 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#262248
Original PR description
Before this commit, in some cases, the order created for printing cash move was saved to the IndexedDB and then later loaded from IndexedDB, which caused the order gets synced to the backend but missing some required fields, like preset or pricelist. opw-5969602 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#262248
Miscellaneous changes
**Steps to reproduce:** - Install Survey app - Create a survey - Share as e-mail - Add some text in the body/subject - Modify recipients - The added text is automatically reset **Issue:** Behavior was previously introduced to match the content of the composer body/subject to the recipient language. If there was only one language among the recipients it automatically adapted the template and changed the rendered language (which also refreshed the content). This logic was trigge
Original PR description
**Steps to reproduce:** - Install Survey app - Create a survey - Share as e-mail - Add some text in the body/subject - Modify recipients - The added text is automatically reset **Issue:** Behavior…
**Steps to reproduce:** - Install Survey app - Create a survey - Share as e-mail - Add some text in the body/subject - Modify recipients - The added text is automatically reset **Issue:** Behavior was previously introduced to match the content of the composer body/subject to the recipient language. If there was only one language among the recipients it automatically adapted the template and changed the rendered language (which also refreshed the content). This logic was triggered by a depends on `partner_ids` and triggered the compute on every recipient changes which led to the subject/body reset. **Fix:** Revert commit: https://github.com/odoo/odoo/commit/b7bbb7b21f4848323666230b518cad9459726f67 in 18.0+ Also adapt commit: https://github.com/odoo/odoo/commit/c6f19e89cb6019e7dbaadbc7427fbb6ddd5661ed to avoid mixed language in resulting mail when the composer was modified We could also try to prevent the compute when the subject or body is already modified instead of removing its logic. opw-6020245 Forward-Port-Of: odoo/odoo#280162 Forward-Port-Of: odoo/odoo#254090
load_data() reads product.pricelist.item_ids as a field, which applies _base_domain_item_ids()'s dotted active conditions. Since this runs as the cashier, never sudo, the ORM injects ir.rules into those conditions, forcing a non-hashable subquery that Postgres re-scans once per pricelist item. Cost scales with items times catalog size, freezing session opening on large catalogs. product.pricelist.item is already loaded separately via a plain, indexed pricelist_id domain. Reuse it to build ite
Original PR description
load_data() reads product.pricelist.item_ids as a field, which applies _base_domain_item_ids()'s dotted active conditions. Since this runs as the cashier, never sudo, the ORM injects ir.rules into those conditions, forcing a non-hashable subquery that Postgres re-scans once per pricelist item. Cost scales with items times catalog size, freezing session opening on large catalogs.
product.pricelist.item is already loaded separately via a plain, indexed pricelist_id domain. Reuse it to build item_ids instead of reading the field, avoiding the join/subquery entirely.
before after speedup
item_ids read 475.8s 2.44s ~195x
load_data() (full) unbounded 8.7s hang -> ok
opw-6391847
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#27725415 changes
Enhancements to existing features
This commit adds 3 new `Tax Exemption Reason Code`: - VATEX-FR-F - VATEX-FR-I - VATEX-FR-J task-6333649 Forward-Port-Of: odoo/odoo#280537 Forward-Port-Of: odoo/odoo#278086
Original PR description
This commit adds 3 new `Tax Exemption Reason Code`: - VATEX-FR-F - VATEX-FR-I - VATEX-FR-J task-6333649 Forward-Port-Of: odoo/odoo#280537 Forward-Port-Of: odoo/odoo#278086
Doing an euclidean division on floats with the native operators is unreliable: because of IEEE-754 representation errors, `value1 % value2` can return a spurious remainder (e.g. `50.4 % 16.8 == 16.799999999999997` instead of 0.0) and `int(value1 / value2)` can truncate the quotient one step too low (e.g. `int(0.3 / 0.1) == 2` instead of 3). `float_div` returns the `(quotient, remainder)` pair free of those errors. The key is to never run a lossy `%` or `//` on the raw floats. Instead both ope
Original PR description
Doing an euclidean division on floats with the native operators is unreliable: because of IEEE-754 representation errors, `value1 % value2` can return a spurious remainder (e.g. `50.4 % 16.8 ==…
Doing an euclidean division on floats with the native operators is unreliable: because of IEEE-754 representation errors, `value1 % value2` can return a spurious remainder (e.g. `50.4 % 16.8 == 16.799999999999997` instead of 0.0) and `int(value1 / value2)` can truncate the quotient one step too low (e.g. `int(0.3 / 0.1) == 2` instead of 3). `float_div` returns the `(quotient, remainder)` pair free of those errors. The key is to never run a lossy `%` or `//` on the raw floats. Instead both operands are first snapped onto the precision grid with `float_round` and then scaled to integers: since a grid-snapped value is a multiple of `rounding`, dividing it by `rounding` counts how many grid steps it spans. That division is still noisy (`4.35 / 0.05 == 86.99999999999999`), so the result is passed through `builtins.round` to coerce it to the exact integer step count. The euclidean division itself is then a plain integer `divmod`, which is exact, and the remainder is scaled back to real units. This is why the correction is applied to the inputs and not to the output: rounding the result of a native `%` would only round an already-corrupt value, and would still misreport the quotient in the corner cases the util exists to handle. Dividing by `rounding` is meaningful for any precision, not only powers of ten: the grid step can be `0.05`, `0.25`, `0.5`, `0.03`, ... and `value / step` counts the steps in every case. This mirrors the normalize/denormalize scheme `float_round` already uses internally. The util shares `float_round`'s inherent limitation: the scaled step count must stay representable as an exact `float` integer, so exactness is lost past ~2**53 grid steps (extreme magnitudes at a fine precision). This is the IEEE-754 double-precision ceiling and is well outside any realistic quantity or price range. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280456 Forward-Port-Of: odoo/odoo#277160
Resolved issues and error corrections
**Issue** Confirming a SO that generates a batch of MOs from a BoM with a batch size could lead to creating an invalid number of pickings: all the MOs end up sharing a single picking instead of getting one each. **Steps to reproduce** - Use 2-step manufacturing - Create a storable product with the MTO + Manufacture routes - Add a BOM that has a batch size of 10 that consumes one component - Create and confirm a SO of 100 units of that product -> 10 MOs are created, but each one points
Original PR description
**Issue** Confirming a SO that generates a batch of MOs from a BoM with a batch size could lead to creating an invalid number of pickings: all the MOs end up sharing a single picking instead of…
**Issue** Confirming a SO that generates a batch of MOs from a BoM with a batch size could lead to creating an invalid number of pickings: all the MOs end up sharing a single picking instead of getting one each. **Steps to reproduce** - Use 2-step manufacturing - Create a storable product with the MTO + Manufacture routes - Add a BOM that has a batch size of 10 that consumes one component - Create and confirm a SO of 100 units of that product -> 10 MOs are created, but each one points to the same "Pick Components" transfer instead of getting its own. **Cause** This commit dd6ee071f949752d31497d9f975ba7fc41ebcd98 batches the confirm of productions: https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/mrp/models/stock_rule.py#L120 thus `assign_picking` is called in batches: https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/mrp/models/mrp_production.py#L1653 https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1736-L1737 https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1550-L1556 since, the `reference_ids` are the same for each MO/stock.move (they all come from the same procurement): https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1712-L1713 Consequently, all the moves end up in the same recordset `moves`: https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1527 Creating only one picking: https://github.com/odoo/odoo/blob/b51dc298fd63d9586d0b8d7cff59764b0dee5cae/addons/stock/models/stock_move.py#L1577 opw-6403427 Forward-Port-Of: odoo/odoo#279179
Issue ----- Branch companies can have quants of a product limited to their parent company, but they don't show in the per location report. Steps to reproduce ----- - Create a branch company - Create a WH for the branch - Create a tracked product - Company set to parent only - Switch to the branch company - Add a quant of the product in branch stock - Open Inventory > Reporting > Locations > The product is not shown although there is a quant in the branch Cause ----- The m
Original PR description
Issue ----- Branch companies can have quants of a product limited to their parent company, but they don't show in the per location report. Steps to reproduce ----- - Create a branch company - Create…
Issue ----- Branch companies can have quants of a product limited to their parent company, but they don't show in the per location report. Steps to reproduce ----- - Create a branch company - Create a WH for the branch - Create a tracked product - Company set to parent only - Switch to the branch company - Add a quant of the product in branch stock - Open Inventory > Reporting > Locations > The product is not shown although there is a quant in the branch Cause ----- The menu button triggers `action_view_quants` https://github.com/odoo/odoo/blob/03a3662212f094158f885ae6545009fd0a74d3cb/addons/stock/views/stock_quant_views.xml#L493-L495 https://github.com/odoo/odoo/blob/03a3662212f094158f885ae6545009fd0a74d3cb/addons/stock/models/stock_quant.py#L399-L402 The problem here comes from the fact that in `_get_quants_action`, we limit the products to those of only the active companies, instead of allowing to view those of parent companies aswell. https://github.com/odoo/odoo/blob/03a3662212f094158f885ae6545009fd0a74d3cb/addons/stock/models/stock_quant.py#L1330 Such a change works because the domain is specifically for the product's (`product_id.company_id`) and not the location's. ----- Ticket: opw-6131525 Forward-Port-Of: odoo/odoo#280083 Forward-Port-Of: odoo/odoo#277531
Task-6429727 Forward-Port-Of: odoo/odoo#279204
Original PR description
Task-6429727 Forward-Port-Of: odoo/odoo#279204
Steps to reproduce the error: (Python Version: 3.14.X) - Install ``accounting_firm`` industry with demo data Code for server action to generate the error: ```py for i in [1]: try: pass except Exception: pass if i: pass ``` Traceback: ```py ValueError: forbidden opcode(s) in'...': JUMP_BACKWARD_NO_INTERRUPT ``` https://github.com/odoo/industry/blob/701f7595453772e42ce72aa75df346a504e5bd82/accounting_firm/demo/ir_actions_server.xml#L105-L10
Original PR description
Steps to reproduce the error: (Python Version: 3.14.X) - Install ``accounting_firm`` industry with demo data Code for server action to generate the error: ```py for i in [1]: try: pass except…
Steps to reproduce the error: (Python Version: 3.14.X)
- Install ``accounting_firm`` industry with demo data
Code for server action to generate the error:
```py
for i in [1]:
try:
pass
except Exception:
pass
if i:
pass
```
Traceback:
```py
ValueError: forbidden opcode(s) in'...': JUMP_BACKWARD_NO_INTERRUPT
```
https://github.com/odoo/industry/blob/701f7595453772e42ce72aa75df346a504e5bd82/accounting_firm/demo/ir_actions_server.xml#L105-L108
The server action contains a ``for`` loop with a ``try/except`` block followed by additional statements in the loop body,
this combination generates the ``JUMP_BACKWARD_NO_INTERRUPT`` opcode, which is not included in ``_SAFE_OPCODES`` at [1].
When the server action is evaluated by ``safe_eval``, it calls the ``assert_valid_codeobj`` method, which validates the compiled bytecode against ``_SAFE_OPCODES``. Since ``JUMP_BACKWARD_NO_INTERRUPT`` is not present in the allowed opcodes, ``assert_valid_codeobj()`` raises a ``ValueError`` at [2] before the server action is executed .
Solution:
``JUMP_BACKWARD_NO_INTERRUPT`` opcode is added in the ``_SAFE_OPCODES`` and it is also added in the ``_SAFE_QWEB_OPCODES``.
It was added in Python 3.11: https://docs.python.org/3/whatsnew/3.11.html#new-opcodes
``JUMP_BACKWARD_NO_INTERRUPT`` is a control-flow opcode that only changes
the interpreter's execution flow by jumping back to a previous instruction.
It is the equivalent to ``JUMP_BACKWARD`` opcode. Its only semantic difference is
that the interpreter does not perform an interrupt check at that instruction.
It does not introduce any new capabilities or perform operations such as
attribute access, imports, function calls, or object creation.
Ref: https://docs.python.org/3.12/library/dis.html#opcode-JUMP_BACKWARD_NO_INTERRUPT
Similar commit that adds some necessary opcodes:
https://github.com/odoo/odoo/commit/86498d24946e510025add5d24ef0d4bcce8ad05f
[1]: https://github.com/odoo/odoo/blob/2ccbc4660077bd48529e9de43d4309fbfafc75ca/odoo/tools/safe_eval.py#L135
[2]: https://github.com/odoo/odoo/blob/2ccbc4660077bd48529e9de43d4309fbfafc75ca/odoo/tools/safe_eval.py#L244-L246
sentry-7614026125
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#277192### Issue: A partner with VAT set to '/' incorrectly matches a fiscal position with `VAT required`, instead of one without The '/' value is the placeholder suggested by the UI to indicate that the partner is known to have no VAT, but it was treated as a valid VAT by the fiscal position matching logic ### Cause: `_get_fpos_ranking_functions` uses `_get_vat_valid` to rank fiscal positions based on VAT presence `_get_vat_valid` returned `True` for any non-empty VAT value, including '/' Th
Original PR description
### Issue: A partner with VAT set to '/' incorrectly matches a fiscal position with `VAT required`, instead of one without The '/' value is the placeholder suggested by the UI to indicate that the…
### Issue: A partner with VAT set to '/' incorrectly matches a fiscal position with `VAT required`, instead of one without The '/' value is the placeholder suggested by the UI to indicate that the partner is known to have no VAT, but it was treated as a valid VAT by the fiscal position matching logic ### Cause: `_get_fpos_ranking_functions` uses `_get_vat_valid` to rank fiscal positions based on VAT presence `_get_vat_valid` returned `True` for any non-empty VAT value, including '/' The '/' case was not excluded, causing it to be treated as a valid VAT number ### Steps to reproduce: - Install `account` - Create two fiscal positions with auto-apply: -- Name: FP VAT, VAT required: True, sequence: 1 -- Name: FP no VAT, VAT required: False, sequence: 2 - Create a partner with VAT: '/' - Create an Invoice for that partner and check the Fiscal Position Before the fix, `FP VAT` is selected instead of `FP no VAT` opw-6204531 Forward-Port-Of: odoo/odoo#280494 Forward-Port-Of: odoo/odoo#280065
## Issue When logging timesheet from the *Recorded* smart button on a Sale Order containing multiple service items, the recorded hours will be added to the first sale order line, regardless of the task ID set on the timesheet entries. ## Steps to reproduce 1. Install *Sales Timesheet* (`sale_timesheet`) 2. Create a Product P: - *Product Type*: Service - *Create on Order*: Task - *Project*: Any 3. Create a SO: - *Customer*: Any - Add the product P on two different
Original PR description
## Issue When logging timesheet from the *Recorded* smart button on a Sale Order containing multiple service items, the recorded hours will be added to the first sale order line, regardless of the…
## Issue
When logging timesheet from the *Recorded* smart button on a Sale Order containing multiple service items, the recorded hours will be added to the first sale order line, regardless of the task ID set on the timesheet entries.
## Steps to reproduce
1. Install *Sales Timesheet* (`sale_timesheet`)
2. Create a Product P:
- *Product Type*: Service
- *Create on Order*: Task
- *Project*: Any
3. Create a SO:
- *Customer*: Any
- Add the product P on two different lines and give them two different descriptions D1 and D2
- Confirm the SO, this will create two tasks with the names D1 and D2
4. On the SO, click the *Recorded* smart button and create two entries:
1. Task D1, 2 hours spent
2. Tsk D2, 3 hours spent
5. **Back on the SO, there are 5 hours registered for the first SOL (with the description D1), which does not match the entries we created from the smart button.**
It is worth noting that when we create the Timesheets entries from the project itself (instead of the SO's smart button), the hours are correctly distributed among the different SOLs.
## Cause
The SOL linked to the timesheet entry (`account.analytic.line`) is computed by `_compute_so_line`:
https://github.com/odoo/odoo/blob/8b102f500f5a122e99b07a08cc43814e7c6f0f75/addons/sale_timesheet/models/hr_timesheet.py#L79-L82
This method sets the correct SOL under the condition that `is_so_line_edited` is False and `_is_no_billed()` returns True.
When opening the *Recorded* smart button from a SO, the `is_so_line_edited` is set to True by default, even if no SOL was modified.
https://github.com/odoo/odoo/blob/8b102f500f5a122e99b07a08cc43814e7c6f0f75/addons/sale_timesheet/models/sale_order.py#L113-L117
As that value is never set to False, when trying to compute the SOL for the timesheet entry, the entry is skipped and the default SOL (which is the first one) is used instead.
opw-6133473
Forward-Port-Of: odoo/odoo#274759Before this commit, in some cases, the order created for printing cash move was saved to the IndexedDB and then later loaded from IndexedDB, which caused the order gets synced to the backend but missing some required fields, like preset or pricelist. opw-5969602 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#262248
Original PR description
Before this commit, in some cases, the order created for printing cash move was saved to the IndexedDB and then later loaded from IndexedDB, which caused the order gets synced to the backend but missing some required fields, like preset or pricelist. opw-5969602 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#262248
How to reproduce: - In a Fiscal Position, map the Downpayment account set in the settings to anything else - Put that Fiscal Position on a SO. - On that SO, create a Downpayment invoice -> The regular Downpayment account is used on the Downpayment invoice, but it should have been mapped because of the Fiscal Position account mapping Solution: Pre-map the company's default down payment account using the Sales Order's Fiscal Position before passing it to the invoice line creation
Original PR description
How to reproduce: - In a Fiscal Position, map the Downpayment account set in the settings to anything else - Put that Fiscal Position on a SO. - On that SO, create a Downpayment invoice -> The regular Downpayment account is used on the Downpayment invoice, but it should have been mapped because of the Fiscal Position account mapping Solution: Pre-map the company's default down payment account using the Sales Order's Fiscal Position before passing it to the invoice line creation. This ensures the correct account mapping is always respected for advance payment invoices. Task-6212218 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279464
When we create a database with `-i web --skip-auto-install`, we run into CSS compilation errors due to undefined variables `$black` and `$gray-200`. This commit adds these two definitions. opw-6398398 Forward-Port-Of: odoo/odoo#279753 Forward-Port-Of: odoo/odoo#279287
Original PR description
When we create a database with `-i web --skip-auto-install`, we run into CSS compilation errors due to undefined variables `$black` and `$gray-200`. This commit adds these two definitions. opw-6398398 Forward-Port-Of: odoo/odoo#279753 Forward-Port-Of: odoo/odoo#279287
Before this commit, this test sometimes failed because it couldn't find a dialog containing "camera" within 200ms. In the test scenario, we click to open the BarcodeDialog, which uses the BarcodeVideoScanner. The latter, in its `onMounted`, checks whether it has the necessary permission, which isn't the case as the `getUserMedia` function is mocked in the test to return a rejected promise. As a consequence, the `onError` callback given in props is called, which changes the state of the parent
Original PR description
Before this commit, this test sometimes failed because it couldn't find a dialog containing "camera" within 200ms. In the test scenario, we click to open the BarcodeDialog, which uses the…
Before this commit, this test sometimes failed because it couldn't find a dialog containing "camera" within 200ms. In the test scenario, we click to open the BarcodeDialog, which uses the BarcodeVideoScanner. The latter, in its `onMounted`, checks whether it has the necessary permission, which isn't the case as the `getUserMedia` function is mocked in the test to return a rejected promise. As a consequence, the `onError` callback given in props is called, which changes the state of the parent component, which re-renders itself so display "Unable to access camera" instead of the BarcodeVideoScanner. To make this test more robust, we do 2 things: 1) load the zxing library before running the test, which avoids the BarcodeVideoScanner component to load it in onWillStart. 2) explicitly wait for the 2 animationFrames, as in the scenario, we must wait for the BarcodeDialog to be rendered twice, and those renderings are now synchronous. runbot error-237933 Forward-Port-Of: odoo/odoo#280613
Steps to reproduce ------------------ 1. Set the company document layout to DIN5008 2. Open a delivery and print the delivery slip The title is missing on the DIN5008 layout, we only have the reference `WH/OUT/00001`. What happens ------------ The DIN5008 layout hides the body title with css and prints its own `h2` instead, from the `din5008_document_title` variable, and uses `o.name` (the picking number) when this variable is not set. The commit 0058d1cf7655 added the title on the
Original PR description
Steps to reproduce ------------------ 1. Set the company document layout to DIN5008 2. Open a delivery and print the delivery slip The title is missing on the DIN5008 layout, we only have the reference `WH/OUT/00001`. What happens ------------ The DIN5008 layout hides the body title with css and prints its own `h2` instead, from the `din5008_document_title` variable, and uses `o.name` (the picking number) when this variable is not set. The commit 0058d1cf7655 added the title on the standard delivery report with `picking_type_id._get_code_report_name()`, but `l10n_din5008_stock` was not updated to set `din5008_document_title`, so on DIN5008 we only get the number. The fix ------- We set it the same way as the other layouts, hence we get back the full title `Delivery Note WH/OUT/00001`. opw-6299248 Forward-Port-Of: odoo/odoo#277444
Documentation and clarification updates
Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278458
Original PR description
Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278458
Miscellaneous changes
load_data() reads product.pricelist.item_ids as a field, which applies _base_domain_item_ids()'s dotted active conditions. Since this runs as the cashier, never sudo, the ORM injects ir.rules into those conditions, forcing a non-hashable subquery that Postgres re-scans once per pricelist item. Cost scales with items times catalog size, freezing session opening on large catalogs. product.pricelist.item is already loaded separately via a plain, indexed pricelist_id domain. Reuse it to build ite
Original PR description
load_data() reads product.pricelist.item_ids as a field, which applies _base_domain_item_ids()'s dotted active conditions. Since this runs as the cashier, never sudo, the ORM injects ir.rules into those conditions, forcing a non-hashable subquery that Postgres re-scans once per pricelist item. Cost scales with items times catalog size, freezing session opening on large catalogs.
product.pricelist.item is already loaded separately via a plain, indexed pricelist_id domain. Reuse it to build item_ids instead of reading the field, avoiding the join/subquery entirely.
before after speedup
item_ids read 475.8s 2.44s ~195x
load_data() (full) unbounded 8.7s hang -> ok
opw-6391847
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#2772548 changes
Resolved issues and error corrections
When a product carrying a multi-select (no_variant) attribute is added to an order by scanning its barcode, the extra price of the selected attribute value was dropped and the product was added at its base price. The order line total is computed from `price_unit` alone. The `price_extra` of a no_variant attribute is never included in the variant price (a "multi" attribute requires create_variant="no_variant"), so it only reaches the total once folded into `price_unit`. `addLineToOrder` did th
Original PR description
When a product carrying a multi-select (no_variant) attribute is added to an order by scanning its barcode, the extra price of the selected attribute value was dropped and the product was added at…
When a product carrying a multi-select (no_variant) attribute is added to an order by scanning its barcode, the extra price of the selected attribute value was dropped and the product was added at its base price. The order line total is computed from `price_unit` alone. The `price_extra` of a no_variant attribute is never included in the variant price (a "multi" attribute requires create_variant="no_variant"), so it only reaches the total once folded into `price_unit`. `addLineToOrder` did that fold-in only when the product was not scanned (`!isScannedProduct`). That guard was added to avoid counting the extra price twice when scanning an "always" variant barcode, whose extra is already part of its price. Since then, the extra price reaching this block is filtered to no_variant values only, both in the configurator and in the direct-variant branch, so an "always" extra can no longer reach it and the guard now only drops legitimate no_variant surcharges. Remove the guard so the no_variant extra price is always applied. Steps to reproduce: - Create a product with a multi-checkbox attribute whose value has an extra price, and give the product a barcode. - Open the PoS and scan the barcode. - Pick the attribute value in the configurator and validate. => The extra price is not added to the order line. opw-6413924 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278389
During the forward port of #278378, the dynamic NSI file path variable was overwritten by an old hardcoded one, breaking the IoT package build.
Original PR description
During the forward port of #278378, the dynamic NSI file path variable was overwritten by an old hardcoded one, breaking the IoT package build.
Steps to reproduce the error: (Python Version: 3.14.X) - Install ``accounting_firm`` industry with demo data Code for server action to generate the error: ```py for i in [1]: try: pass except Exception: pass if i: pass ``` Traceback: ```py ValueError: forbidden opcode(s) in'...': JUMP_BACKWARD_NO_INTERRUPT ``` https://github.com/odoo/industry/blob/701f7595453772e42ce72aa75df346a504e5bd82/accounting_firm/demo/ir_actions_server.xml#L105-L10
Original PR description
Steps to reproduce the error: (Python Version: 3.14.X) - Install ``accounting_firm`` industry with demo data Code for server action to generate the error: ```py for i in [1]: try: pass except…
Steps to reproduce the error: (Python Version: 3.14.X)
- Install ``accounting_firm`` industry with demo data
Code for server action to generate the error:
```py
for i in [1]:
try:
pass
except Exception:
pass
if i:
pass
```
Traceback:
```py
ValueError: forbidden opcode(s) in'...': JUMP_BACKWARD_NO_INTERRUPT
```
https://github.com/odoo/industry/blob/701f7595453772e42ce72aa75df346a504e5bd82/accounting_firm/demo/ir_actions_server.xml#L105-L108
The server action contains a ``for`` loop with a ``try/except`` block followed by additional statements in the loop body,
this combination generates the ``JUMP_BACKWARD_NO_INTERRUPT`` opcode, which is not included in ``_SAFE_OPCODES`` at [1].
When the server action is evaluated by ``safe_eval``, it calls the ``assert_valid_codeobj`` method, which validates the compiled bytecode against ``_SAFE_OPCODES``. Since ``JUMP_BACKWARD_NO_INTERRUPT`` is not present in the allowed opcodes, ``assert_valid_codeobj()`` raises a ``ValueError`` at [2] before the server action is executed .
Solution:
``JUMP_BACKWARD_NO_INTERRUPT`` opcode is added in the ``_SAFE_OPCODES`` and it is also added in the ``_SAFE_QWEB_OPCODES``.
It was added in Python 3.11: https://docs.python.org/3/whatsnew/3.11.html#new-opcodes
``JUMP_BACKWARD_NO_INTERRUPT`` is a control-flow opcode that only changes
the interpreter's execution flow by jumping back to a previous instruction.
It is the equivalent to ``JUMP_BACKWARD`` opcode. Its only semantic difference is
that the interpreter does not perform an interrupt check at that instruction.
It does not introduce any new capabilities or perform operations such as
attribute access, imports, function calls, or object creation.
Ref: https://docs.python.org/3.12/library/dis.html#opcode-JUMP_BACKWARD_NO_INTERRUPT
Similar commit that adds some necessary opcodes:
https://github.com/odoo/odoo/commit/86498d24946e510025add5d24ef0d4bcce8ad05f
[1]: https://github.com/odoo/odoo/blob/2ccbc4660077bd48529e9de43d4309fbfafc75ca/odoo/tools/safe_eval.py#L135
[2]: https://github.com/odoo/odoo/blob/2ccbc4660077bd48529e9de43d4309fbfafc75ca/odoo/tools/safe_eval.py#L244-L246
sentry-7614026125
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#277192The getter `getLoadedDataSources` was filtering out datasources that are not 'ready' but they should actually filter out datasources that were already loaded (so ready) but invalid. Task: 6387729 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276255
Original PR description
The getter `getLoadedDataSources` was filtering out datasources that are not 'ready' but they should actually filter out datasources that were already loaded (so ready) but invalid. Task: 6387729 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276255
## Issue When logging timesheet from the *Recorded* smart button on a Sale Order containing multiple service items, the recorded hours will be added to the first sale order line, regardless of the task ID set on the timesheet entries. ## Steps to reproduce 1. Install *Sales Timesheet* (`sale_timesheet`) 2. Create a Product P: - *Product Type*: Service - *Create on Order*: Task - *Project*: Any 3. Create a SO: - *Customer*: Any - Add the product P on two different
Original PR description
## Issue When logging timesheet from the *Recorded* smart button on a Sale Order containing multiple service items, the recorded hours will be added to the first sale order line, regardless of the…
## Issue
When logging timesheet from the *Recorded* smart button on a Sale Order containing multiple service items, the recorded hours will be added to the first sale order line, regardless of the task ID set on the timesheet entries.
## Steps to reproduce
1. Install *Sales Timesheet* (`sale_timesheet`)
2. Create a Product P:
- *Product Type*: Service
- *Create on Order*: Task
- *Project*: Any
3. Create a SO:
- *Customer*: Any
- Add the product P on two different lines and give them two different descriptions D1 and D2
- Confirm the SO, this will create two tasks with the names D1 and D2
4. On the SO, click the *Recorded* smart button and create two entries:
1. Task D1, 2 hours spent
2. Tsk D2, 3 hours spent
5. **Back on the SO, there are 5 hours registered for the first SOL (with the description D1), which does not match the entries we created from the smart button.**
It is worth noting that when we create the Timesheets entries from the project itself (instead of the SO's smart button), the hours are correctly distributed among the different SOLs.
## Cause
The SOL linked to the timesheet entry (`account.analytic.line`) is computed by `_compute_so_line`:
https://github.com/odoo/odoo/blob/8b102f500f5a122e99b07a08cc43814e7c6f0f75/addons/sale_timesheet/models/hr_timesheet.py#L79-L82
This method sets the correct SOL under the condition that `is_so_line_edited` is False and `_is_no_billed()` returns True.
When opening the *Recorded* smart button from a SO, the `is_so_line_edited` is set to True by default, even if no SOL was modified.
https://github.com/odoo/odoo/blob/8b102f500f5a122e99b07a08cc43814e7c6f0f75/addons/sale_timesheet/models/sale_order.py#L113-L117
As that value is never set to False, when trying to compute the SOL for the timesheet entry, the entry is skipped and the default SOL (which is the first one) is used instead.
opw-6133473
Forward-Port-Of: odoo/odoo#274759Issue: --- Fiscal position is wrongly set to `self.env.user.partner_id.country_id` instead of `partner_shipping` country, if `partner_shipping_id` is not changed in the checkout process. Steps: 1- Create two auto detect fiscal positions: France, Germany 2- Set portal user's partner address country to France. 3- Using portal user, shop from website, and create a delivery address. 4- Pay and confirm the order. 5- Using the admin user, you check the SO's FP which is correctly set to
Original PR description
Issue: --- Fiscal position is wrongly set to `self.env.user.partner_id.country_id` instead of `partner_shipping` country, if `partner_shipping_id` is not changed in the checkout process. Steps: 1-…
Issue: --- Fiscal position is wrongly set to `self.env.user.partner_id.country_id` instead of `partner_shipping` country, if `partner_shipping_id` is not changed in the checkout process. Steps: 1- Create two auto detect fiscal positions: France, Germany 2- Set portal user's partner address country to France. 3- Using portal user, shop from website, and create a delivery address. 4- Pay and confirm the order. 5- Using the admin user, you check the SO's FP which is correctly set to Germany. 6- Using portal user, again shop from website, and don't change address. Keep previous shipping address which is Germany. 7- Confirm and pay the order. 8- Using admin user, check the new SO's FP. It's set to France. Cause: --- `_compute_fiscal_position_id` in SO depends on `partner_shipping_id`. When the `partner_shipping_id` is not changed, the fiscal position value set in create will remain. This value is set in `Website._prepare_sale_order_values()`. The `fiscal_position_id` is set to self.fiscal_position_id, which is `_get_fiscal_position(self.env.user.partner_id)`. Fix: --- If the user has already a SO, we can use last SO's shipping address and invoice address to calculate FP in `_prepare_sale_order_values`. opw-6357638 Forward-Port-Of: odoo/odoo#278974 Forward-Port-Of: odoo/odoo#276485
When we create a database with `-i web --skip-auto-install`, we run into CSS compilation errors due to undefined variables `$black` and `$gray-200`. This commit adds these two definitions. opw-6398398 Forward-Port-Of: odoo/odoo#279287
Original PR description
When we create a database with `-i web --skip-auto-install`, we run into CSS compilation errors due to undefined variables `$black` and `$gray-200`. This commit adds these two definitions. opw-6398398 Forward-Port-Of: odoo/odoo#279287
Documentation and clarification updates
Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278458
Original PR description
Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278458
10 changes
Resolved issues and error corrections
#### Description of the issue this PR addresses: - The predicate loop kept climbing ancestors above the editable root when no match was found inside it. The later containment check would discard such a match anyway, so the predicate should never run outside the editable in the first place. - This ran the predicate on at least 14 unnecessary ancestors up to `<html>`, costly if the predicate is expensive. Stop the search at the editable boundary instead. - `movenode_plugin` calls closestElement
Original PR description
#### Description of the issue this PR addresses: - The predicate loop kept climbing ancestors above the editable root when no match was found inside it. The later containment check would discard such a match anyway, so the predicate should never run outside the editable in the first place. - This ran the predicate on at least 14 unnecessary ancestors up to `<html>`, costly if the predicate is expensive. Stop the search at the editable boundary instead. - `movenode_plugin` calls closestElement on every mousemove, so its predicate was needlessly re-evaluated on those 14+ ancestors on every single mouse move. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Description of the issue this commit addresses: The invoice product prediction test requires account_accountant but runs and fails when that optional module is not installed. --- Desired behavior after this commit is merged: This commit skips the test when account_accountant is not installed and enables product prediction when it is available. --- runbot-[941525](https://runbot.odoo.com/odoo/error/941525) --- I confirm I have signed the CLA and read the PR guidelines at w
Original PR description
Description of the issue this commit addresses: The invoice product prediction test requires account_accountant but runs and fails when that optional module is not installed. --- Desired behavior after this commit is merged: This commit skips the test when account_accountant is not installed and enables product prediction when it is available. --- runbot-[941525](https://runbot.odoo.com/odoo/error/941525) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Steps to reproduce: - Set a Saudi company with a long legal name (e.g. "Golden Oasis Trading and Contracting Company Limited") - Make a POS order and look at the receipt QR code Issue: The QR code is drawn visibly smaller and denser than for a company with a short name, even though the image it sits in is the same 150px box: 90px of code at a 2px module pitch, against 111px at 3px. Cause: The ZATCA payload embeds the seller name, so a longer name needs a higher QR version, i.e. more mo
Original PR description
Steps to reproduce: - Set a Saudi company with a long legal name (e.g. "Golden Oasis Trading and Contracting Company Limited") - Make a POS order and look at the receipt QR code Issue: The QR code is drawn visibly smaller and denser than for a company with a short name, even though the image it sits in is the same 150px box: 90px of code at a 2px module pitch, against 111px at 3px. Cause: The ZATCA payload embeds the seller name, so a longer name needs a higher QR version, i.e. more modules. ZXing's BrowserQRCodeSvgWriter draws each module at a whole number of pixels of the canvas it is given (multiple = floor(canvas / (modules + 8))), so asking it for a fixed 150x150 or 200x200 canvas leaves a leftover margin that varies with the module count. The code shrinks as soon as the module count crosses a multiple of the canvas size. opw-6399878 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
When an order lands in the database with missing lines or payments, the logs only give its name and uuid. That is not enough to tell whether the front end sent an incomplete payload or the write was only partially applied, and the payload dump that would answer it sits behind _logger.debug, which is unusable in production since lowering the whole log level there is not an option. Log the ids of the lines and payments effectively added to an existing order, so a mismatch can be traced back to
Original PR description
When an order lands in the database with missing lines or payments, the logs only give its name and uuid. That is not enough to tell whether the front end sent an incomplete payload or the write was only partially applied, and the payload dump that would answer it sits behind _logger.debug, which is unusable in production since lowering the whole log level there is not an option. Log the ids of the lines and payments effectively added to an existing order, so a mismatch can be traced back to the write that produced it. Add the `point_of_sale.log_order_data` config parameter to log the full order payload at INFO. It stays disabled by default, as the payload contains customer data. Backport of the logging part of eebe9b131a68 and 2446e19bda17. opw-6401146 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Steps to reproduce the error: (Python Version: 3.14.X) - Install ``accounting_firm`` industry with demo data Code for server action to generate the error: ```py for i in [1]: try: pass except Exception: pass if i: pass ``` Traceback: ```py ValueError: forbidden opcode(s) in'...': JUMP_BACKWARD_NO_INTERRUPT ``` https://github.com/odoo/industry/blob/701f7595453772e42ce72aa75df346a504e5bd82/accounting_firm/demo/ir_actions_server.xml#L105-L10
Original PR description
Steps to reproduce the error: (Python Version: 3.14.X) - Install ``accounting_firm`` industry with demo data Code for server action to generate the error: ```py for i in [1]: try: pass except…
Steps to reproduce the error: (Python Version: 3.14.X)
- Install ``accounting_firm`` industry with demo data
Code for server action to generate the error:
```py
for i in [1]:
try:
pass
except Exception:
pass
if i:
pass
```
Traceback:
```py
ValueError: forbidden opcode(s) in'...': JUMP_BACKWARD_NO_INTERRUPT
```
https://github.com/odoo/industry/blob/701f7595453772e42ce72aa75df346a504e5bd82/accounting_firm/demo/ir_actions_server.xml#L105-L108
The server action contains a ``for`` loop with a ``try/except`` block followed by additional statements in the loop body,
this combination generates the ``JUMP_BACKWARD_NO_INTERRUPT`` opcode, which is not included in ``_SAFE_OPCODES`` at [1].
When the server action is evaluated by ``safe_eval``, it calls the ``assert_valid_codeobj`` method, which validates the compiled bytecode against ``_SAFE_OPCODES``. Since ``JUMP_BACKWARD_NO_INTERRUPT`` is not present in the allowed opcodes, ``assert_valid_codeobj()`` raises a ``ValueError`` at [2] before the server action is executed .
Solution:
``JUMP_BACKWARD_NO_INTERRUPT`` opcode is added in the ``_SAFE_OPCODES`` and it is also added in the ``_SAFE_QWEB_OPCODES``.
It was added in Python 3.11: https://docs.python.org/3/whatsnew/3.11.html#new-opcodes
``JUMP_BACKWARD_NO_INTERRUPT`` is a control-flow opcode that only changes
the interpreter's execution flow by jumping back to a previous instruction.
It is the equivalent to ``JUMP_BACKWARD`` opcode. Its only semantic difference is
that the interpreter does not perform an interrupt check at that instruction.
It does not introduce any new capabilities or perform operations such as
attribute access, imports, function calls, or object creation.
Ref: https://docs.python.org/3.12/library/dis.html#opcode-JUMP_BACKWARD_NO_INTERRUPT
Similar commit that adds some necessary opcodes:
https://github.com/odoo/odoo/commit/86498d24946e510025add5d24ef0d4bcce8ad05f
[1]: https://github.com/odoo/odoo/blob/2ccbc4660077bd48529e9de43d4309fbfafc75ca/odoo/tools/safe_eval.py#L135
[2]: https://github.com/odoo/odoo/blob/2ccbc4660077bd48529e9de43d4309fbfafc75ca/odoo/tools/safe_eval.py#L244-L246
sentry-7614026125
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#277192When we create a database with `-i web --skip-auto-install`, we run into CSS compilation errors due to undefined variables `$black` and `$gray-200`. This commit adds these two definitions. opw-6398398 Forward-Port-Of: odoo/odoo#279287
Original PR description
When we create a database with `-i web --skip-auto-install`, we run into CSS compilation errors due to undefined variables `$black` and `$gray-200`. This commit adds these two definitions. opw-6398398 Forward-Port-Of: odoo/odoo#279287
Miscellaneous changes
Support for multiple employees per user Use Case Example: - Create Company A and Company B - Create a user named Test with access to both companies - Create an employee named Test A linked to Company A and the Test user - Create an employee named Test B linked to Company B and the Test user - Log in as the Test user with only Company A selected. Everything should be linked to employee Test A - Log in as the Test user with only Company B selected. Everything should be linked to employee
Original PR description
Support for multiple employees per user Use Case Example: - Create Company A and Company B - Create a user named Test with access to both companies - Create an employee named Test A linked to Company A and the Test user - Create an employee named Test B linked to Company B and the Test user - Log in as the Test user with only Company A selected. Everything should be linked to employee Test A - Log in as the Test user with only Company B selected. Everything should be linked to employee Test B Please @pedrobaeza can you review it? @Tecnativa TT63466 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The product specifications table is displayed whenever the product has tags, even if none of them are visible on the ecommerce website. The product tags template filters out non-visible tags, but the surrounding table remains rendered and appears empty. Only display the tags table when at least one tag is visible on ecommerce. @Tecnativa TT63855 **Description of the issue/feature this PR addresses:** The condition used to display the product tags table considers all tags associate
Original PR description
The product specifications table is displayed whenever the product has tags, even if none of them are visible on the ecommerce website. The product tags template filters out non-visible tags, but the…
The product specifications table is displayed whenever the product has tags, even if none of them are visible on the ecommerce website. The product tags template filters out non-visible tags, but the surrounding table remains rendered and appears empty. Only display the tags table when at least one tag is visible on ecommerce. @Tecnativa TT63855 **Description of the issue/feature this PR addresses:** The condition used to display the product tags table considers all tags associated with the product, including those that are not visible on ecommerce. **Current behavior before PR:** When a product only has non-visible tags, the tags table is displayed without any content. <img width="669" height="350" alt="image" src="https://github.com/user-attachments/assets/4758ea76-5186-4035-a065-aa4c71ce7053" /> **Desired behavior after PR is merged:** The tags table is only displayed when the product has at least one tag visible on ecommerce. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278604
In `settleSO`, two per-line bottlenecks were addressed: 1. `has_valued_move_ids` was called once per order line via a separate RPC, causing N sequential HTTP round-trips. It is now computed server-side inside `read_converted`, which is already called once for all lines. 2. `addLineToCurrentOrder` was awaited per line, yielding to the event loop each iteration and triggering a full Owl re-render for every line. Lines are now created directly, batching all mutations into a single render. `re
Original PR description
In `settleSO`, two per-line bottlenecks were addressed: 1. `has_valued_move_ids` was called once per order line via a separate RPC, causing N sequential HTTP round-trips. It is now computed server-side inside `read_converted`, which is already called once for all lines. 2. `addLineToCurrentOrder` was awaited per line, yielding to the event loop each iteration and triggering a full Owl re-render for every line. Lines are now created directly, batching all mutations into a single render. `recomputeOrderData()` is called once after the loop. `updatePrograms` is moved to a `pos_sale_loyalty` patch on `settleSO` so the loyalty concern belongs to the bridge module. opw-6319922 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
6 changes
Resolved issues and error corrections
Sometimes the default_state remains in the context from a past action and when the subcontractor fills its components consumptions it will directly modify the quant's quantity. This trigger the automatic rules and generate an unwanted picking opw-6109661 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
Sometimes the default_state remains in the context from a past action and when the subcontractor fills its components consumptions it will directly modify the quant's quantity. This trigger the automatic rules and generate an unwanted picking opw-6109661 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Description of the issue/feature this PR addresses: A user without accounting rights cannot open an invoice form when l10n_es_edi_verifactu is installed. The module adds a VeriFactu page and three warning banners to the standard invoice form without any groups restriction, so every user able to open an invoice reads three fields (l10n_es_edi_verifactu_document_ids, l10n_es_edi_verifactu_warning, l10n_es_edi_verifactu_warning_level) pointing at l10n_es_edi_verifactu.document. That model
Original PR description
Description of the issue/feature this PR addresses: A user without accounting rights cannot open an invoice form when l10n_es_edi_verifactu is installed. The module adds a VeriFactu page and three…
Description of the issue/feature this PR addresses: A user without accounting rights cannot open an invoice form when l10n_es_edi_verifactu is installed. The module adds a VeriFactu page and three warning banners to the standard invoice form without any groups restriction, so every user able to open an invoice reads three fields (l10n_es_edi_verifactu_document_ids, l10n_es_edi_verifactu_warning, l10n_es_edi_verifactu_warning_level) pointing at l10n_es_edi_verifactu.document. That model only grants read access to account.group_account_invoice and account.group_account_readonly. Steps to reproduce: - install `l10n_es_edi_verifactu` - create a salesman user with sales rights but no accounting right (*Own Documents Only* is enough) - create an ES company and an ES customer - give the salesman access to the ES company - activate Peppol in the general settings - log in as the salesman - create a sale order in the ES company for the ES customer - confirm it - click **Create Invoice** - click **Create Draft** Current behavior before PR: An error access is raised: Failed to read field account.move.l10n_es_edi_verifactu_document_ids You are not allowed to access 'Veri*Factu Document' (l10n_es_edi_verifactu.document) records. This operation is allowed for the following groups: - Invoicing/Billing - Technical/Show Accounting Features - Readonly Contact your administrator to request access if necessary. In Odoo sh (for databases 19.0), the standard test sale_management / TestSaleFlowTourPostInstall.test_basic_sale_flow_with_minimal_access_rights fails for the same reason as soon as l10n_es_edi_verifactu is installed alongside sale_management. Desired behavior after PR is merged: On a database with l10n_es_edi_verifactu installed, a non-accountant user having the possibility to create invoices should not have the error message displayed. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
When edit_translations is set, convert_to_record wraps translated terms in branding spans. Related (non-stored) fields re-read that already-wrapped value and ran the same wrapping again, producing nested spans. Only wrap terms for stored fields so related Html inherits the source branding unchanged. Also keep data-oe-translation-state in HTML safe_attrs so sanitization does not strip it. Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior af
Original PR description
When edit_translations is set, convert_to_record wraps translated terms in branding spans. Related (non-stored) fields re-read that already-wrapped value and ran the same wrapping again, producing nested spans. Only wrap terms for stored fields so related Html inherits the source branding unchanged. Also keep data-oe-translation-state in HTML safe_attrs so sanitization does not strip it. Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Description of the issue/feature this PR addresses: Portal/public users get AccessError (HTTP 403) when kit product stock fields are read on the website shop or cart. Kits (phantom BoMs) have no on-hand stock of their own: `qty_available` / `free_qty` are derived by finding the BoM, exploding components, and computing how many complete kits can be built. Shop and cart code read those fields for availability (in stock / out of stock, prevent selling more than available, etc.). Portal users are
Original PR description
Description of the issue/feature this PR addresses: Portal/public users get AccessError (HTTP 403) when kit product stock fields are read on the website shop or cart. Kits (phantom BoMs) have no…
Description of the issue/feature this PR addresses: Portal/public users get AccessError (HTTP 403) when kit product stock fields are read on the website shop or cart. Kits (phantom BoMs) have no on-hand stock of their own: `qty_available` / `free_qty` are derived by finding the BoM, exploding components, and computing how many complete kits can be built. Shop and cart code read those fields for availability (in stock / out of stock, prevent selling more than available, etc.). Portal users are not meant to manage BOMs (no ACL on `mrp.bom`), and kit components are often not website-published, so the base `_compute_quantities_dict` path blows up on a normal shop browse. `website_sale_mrp` already avoids this for cart checks by reading `product.sudo().free_qty`; the product quantity compute did not. This affects 17.0+ (verified on 17.0 and 19.0). Targeting 17.0 as the oldest supported branch so it can be forward-ported. CLA: covered by https://github.com/odoo/odoo/pull/275749 Current behavior before PR: Reading kit stock fields (e.g. on the shop) as a portal/public user raises AccessError when the BoM or unpublished components must be read. Desired behavior after PR is merged: For portal/public users, kit quantity computation runs under `sudo()` (same idea as `website_sale_mrp`). BOMs stay unreadable to portal users directly. Regression test covers portal and public access. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
In ubuntu 26.04 the time zone database contents changed. A couple of timezone names are not available anymore, causing runtime exceptions. This mapping links old with new time zone naming conventions to prevent future (test) breakdown. The mapping WET to Europe/Lisbon is imperfect, and the localizations for the fixed date inside the test did not match. There exists no better nor correct mapping for WET. The mapping to Europe/Lisbon comes from the IANA tzdb-2026c and is official, so it
Original PR description
In ubuntu 26.04 the time zone database contents changed. A couple of timezone names are not available anymore, causing runtime exceptions. This mapping links old with new time zone naming conventions to prevent future (test) breakdown. The mapping WET to Europe/Lisbon is imperfect, and the localizations for the fixed date inside the test did not match. There exists no better nor correct mapping for WET. The mapping to Europe/Lisbon comes from the IANA tzdb-2026c and is official, so it is kept unchanged. The fixed date inside the test is changed to a recent one that aligns the test outcome with expectations: - Offsets match for recent history and future time - Match daylight savings time (DST) observation --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
### Issue before this commit: When generating ZUGFeRD or Factur-X electronic invoices, including an HTML hyperlink (such as an email or website) in the document layout caused the final PDF to fail PDF/A validation. Portals would reject the file with the error: "An annotation dictionary does not contain the key F". ### Steps to reproduce the issue: 1. Download Accounting and l10n_de 2. Go to Contacts > set Tax ID for company Bloem GmbH and also for DE company 3. Activate ZUGFeRD from setti
Original PR description
### Issue before this commit: When generating ZUGFeRD or Factur-X electronic invoices, including an HTML hyperlink (such as an email or website) in the document layout caused the final PDF to fail…
### Issue before this commit: When generating ZUGFeRD or Factur-X electronic invoices, including an HTML hyperlink (such as an email or website) in the document layout caused the final PDF to fail PDF/A validation. Portals would reject the file with the error: "An annotation dictionary does not contain the key F". ### Steps to reproduce the issue: 1. Download Accounting and l10n_de 2. Go to Contacts > set Tax ID for company Bloem GmbH and also for DE company 3. Activate ZUGFeRD from settings 4. Go to settings and click on Configure Document Layout and insert something like <a href="mailto:info@company.de_skr03example.com"> info@company.de_skr03example.com</a> into the footer 5. Create an invoice to Bloem GmbH and send it through ZUGFeRD 6. Download PDF and upload it on https://www.portinvoice.com/ 7. See error: An annotation dictionary does not contain the key F. With the exception of annotation dictionaries whose subtype value is Popup, all annotation dictionaries must contain the key F. ### Cause of the issue: The underlying PDF generation engine creates hyperlinks as PDF annotations but omits the /F (Flags) key. During Odoo's PDF/A conversion process, these annotations were left unmodified. This violates the strict PDF/A specification, which mandates that all non-Popup annotations must explicitly define the /F key. ### Reason to introduce the fix: This fix ensures full PDF/A compliance by iterating through all page annotations and injecting the missing /F key for any annotation that is not a Popup. This allows users to safely include clickable links in their invoice templates without breaking electronic document validation. opw-6388212 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr