Monday, March 30, 2026
19 changes · saas-19.1
Enhancements to existing features
This update automatically updates the Uruguayan tax authority (DGI) status for electronic stock pickings, ensuring compliance. When a picking is rejected by the DGI, the system now logs the issue and notifies users for manual review, improving accuracy and reducing manual effort.
Original PR description
This pull request introduces an automated process to periodically update the DGI (Dirección General Impositiva) state for Uruguayan electronic stock pickings, along with improvements to error…
This pull request introduces an automated process to periodically update the DGI (Dirección General Impositiva) state for Uruguayan electronic stock pickings, along with improvements to error handling and logging when a picking is rejected. The main changes are the addition of a scheduled cron job, enhanced logging, and user notifications for rejected pickings. **Automated DGI State Updates:** * Added a new scheduled cron job (`ir_cron_update_dgi_state_pickings`) that runs every 10 minutes to update the DGI state of stock pickings with electronic documents in the "received" state. (`l10n_uy_edi_stock/data/ir_cron.xml`, `l10n_uy_edi_stock/__manifest__.py`) [[1]](diffhunk://#diff-36f19bab7c2edeb0f43a1db1639b72e8508deed19652e0b1142ebfce688b9d3eR1-R11) [[2]](diffhunk://#diff-370a6cfd5890d6504958deb4a225d69ec84e84fa59deeeb52c96a849f3fdfc22R14) * Implemented the `_l10n_uy_edi_stock_cron_update_dgi_state` method in `stock_picking.py` to process batches of pickings and trigger itself again if more records remain. (`l10n_uy_edi_stock/models/stock_picking.py`) **Error Handling and Notifications:** * Enhanced the `l10n_uy_edi_action_update_dgi_state` method to log rejected pickings and post a message in the chatter to notify users when a picking has been rejected by DGI, prompting manual review and correction. (`l10n_uy_edi_stock/models/stock_picking.py`) * Introduced logging setup for the module to support the new logging functionality. (`l10n_uy_edi_stock/models/stock_picking.py`) [[1]](diffhunk://#diff-108d31170c95f307accd45410f9a98bd16ce0413932fab4732eb764d75d3e260R2) [[2]](diffhunk://#diff-108d31170c95f307accd45410f9a98bd16ce0413932fab4732eb764d75d3e260R14) Forward-Port-Of: odoo/enterprise#109518
Resolved issues and error corrections
This update fixes an issue where failed quality checks weren't correctly splitting stock moves, leading to inaccurate demand calculations. The fix ensures that when a partial failure occurs, the stock move is divided accurately, maintaining correct inventory levels. This improves the reliability of quality control processes and prevents stock discrepancies.
Original PR description
**Steps to reproduce:** * Install the `quality_control` module. * Create a storable product. * Configure a Quality Control Point: * Set Control per : Quantity. * Set Operation Type to Receipts. * Set…
**Steps to reproduce:** * Install the `quality_control` module. * Create a storable product. * Configure a Quality Control Point: * Set Control per : Quantity. * Set Operation Type to Receipts. * Set Product to the created product. * Create a Receipt for the product with a demand of 5 units. * Confirm the receipt and mark it as To Do. * Click on the Quality Check button. * Click on Fail and set the failed quantity to 3. * Click on Confirm. **Observed behavior:** * A new stock move is created for the failed quantity. * The original move is split incorrectly: * First move: 2 `product_uom_qty` and 2 `quantity`. * Second move: 2 `product_uom_qty` and 3 `quantity`. * The failed move has a demand of **2** instead of **3**. **Cause:** * Clicking on *Quality Check* triggers `check_quality`, which opens the wizard `action_open_quality_check_wizard`: https://github.com/odoo/enterprise/blob/90567af2703a3928c35814a0c4862b4cd98b8432/quality_control/models/stock_picking.py#L79-L82 * Clicking on *Fail* triggers `do_fail`, opening the confirmation wizard: https://github.com/odoo/enterprise/blob/90567af2703a3928c35814a0c4862b4cd98b8432/quality_control/wizard/quality_check_wizard.py#L85-L88 * Clicking on *Confirm* triggers `confirm_fail`, which calls `_move_line_to_failure_location`: https://github.com/odoo/enterprise/blob/90567af2703a3928c35814a0c4862b4cd98b8432/quality_control/wizard/quality_check_wizard.py#L97 * In `_move_line_to_failure_location`, a new stock move is created for the failed quantity: https://github.com/odoo/enterprise/blob/90567af2703a3928c35814a0c4862b4cd98b8432/quality_control/models/quality.py#L480 * The demand quantity is computed using the minimum of the failed quantity and the move line quantity. * This leads to an incorrect demand of *2* instead of *3*. * However, the move line quantity was already reduced by the failed quantity: https://github.com/odoo/enterprise/blob/90567af2703a3928c35814a0c4862b4cd98b8432/quality_control/models/quality.py#L472 **Fix:** * Ensure that partial failures properly split stock moves with correct `product_uom_qty` and `quantity` values. --- opw-5492095 Forward-Port-Of: odoo/enterprise#112298 Forward-Port-Of: odoo/enterprise#107493
This fix addresses a technical issue where AI response errors triggered 500 Internal Server Errors, impacting user experience. Now, errors are presented as messages from the AI agent, providing a smoother and more user-friendly experience for business users.
Original PR description
Currently, when an error occurs during `/ai/generate_response` calls from the frontend, a `UserError` is logged with its exception stack instead of being emitted as a logger warning. Additionally, an…
Currently, when an error occurs during `/ai/generate_response` calls from the
frontend, a `UserError` is logged with its exception stack instead of being
emitted as a logger warning. Additionally, an `UncaughtPromiseError` is
surfaced on the UI.
Error:
```
Error on request:
Traceback (most recent call last):
File "/home/odoo/src/enterprise/saas-19.1/ai/utils/llm_api_service.py", line 302, in _request
response.raise_for_status()
File "/usr/lib/python3/dist-packages/requests/models.py", line 1021, in raise_for_status
```
The issue originates from the `/ai/generate_response` route, which is
defined as a controller `type="http"` to handling POST requests. When
line [1] raises an exception inside this controller, it automatically converts
the unhandled exception into an HTTP response. As a result, the request
returns a `500 Internal Server Error`, which then propagates to the frontend
and appears as an `UncaughtPromiseError`.
This commit fixes the issue by returning internal errors as chat
messages instead of raising exceptions. As a result, the error is
displayed as a message received from the AI agent, rather than
propagating as a server error to internal users.
[1]: https://github.com/odoo/enterprise/blob/423dee8f565a6ca7cd88ec0625a5059920725f50/ai/controllers/thread.py#L83
Sentry-5691330773This update fixes an issue where tax returns were incorrectly including Italian pension fund taxes, leading to discrepancies between reports and the backend view. The change excludes these taxes from the tax return domain, ensuring accurate calculations and consistent reporting for Italian customers. This resolves a reported inconsistency.
Original PR description
The "amount to pay" incorrectly included the Pension Fund taxes, causing inconsistencies between the report and what the customer was able to see in the backend. Now we exclude them from the tax return domain. Steps to reproduce: - Create an Italian company with the Italian CoA - Install `l10n_it_edi_withholding` (not necessary in v19) - Create an invoice - Set the 4% INPS or 4% F.Pens taxes on a line, along with with a normal 22% VAT - Create a tax return Ticket [link](https://www.odoo.com/odoo/project.task/5909407) opw-5909407 Forward-Port-Of: odoo/enterprise#111311
This update corrects an issue where work orders weren't properly tracking consumed components. The previous changes had unintended consequences, so this revert ensures accurate tracking of materials used in work orders. A new test has also been added to prevent similar problems in the future.
Original PR description
Commit [1] has been merged with another commit OC side. However, the latter has impacted some other use cases. This is the reason why we need to revert both. The current commit also improves a test in `test_consume_component.py` to cover the case that has been broken [1] 75febe5a72f091a9940b27c7a59a31f3fe3c407a opw-5939156 Forward-Port-Of: odoo/enterprise#110675
This update fixes a previous error that caused tracebacks when users interacted with the AI using documents opened in the file viewer. The fix ensures the correct file ID is passed to the AI, resolving a 404 error and improving the AI's ability to process documents. Additionally, the AI's behavior when handling images was stabilized.
Original PR description
Before this commit, whenever a user tried to interact with the ai regarding a document opened in the file viewer, they would get a traceback with a 404 error. This was caused by the file id that we passed in the `openAIChat` method of the `AIChatLauncher` service. The id is negative on purpose by the documents team - there is a comment stating that it "prevents a reload from resolving to a real record". Also, the id doesn't reflect the attachment_id, but rather another id dedicated to the file_viewer. On the AI side, when using the id to search for the attachment to send to the AI, we get an error because the id is negative. This bubbles up to the user. We fix this by replacing the `this.file.id` with the `this.file.documentData.attachment_id.id` which is the correct value of the id associated with this document's attachment. Task-6030598
This update fixes an issue where selected failure locations weren't being applied during product repairs. Now, when a quality check fails, the product is automatically moved to the user-specified failure location, ensuring accurate inventory and repair tracking. This improves the reliability of the repair process.
Original PR description
Before this commit: ------------------------- - In the Repair module, when a quality check for a product was marked as failed, Even after selecting a failure location, the failure location is not…
Before this commit: ------------------------- - In the Repair module, when a quality check for a product was marked as failed, Even after selecting a failure location, the failure location is not being set at the final product move. - Instead of showing the selected failure location, the system displayed another location as the move destination after completing the repair process. Steps to reproduce: ------------------------- 1. Install the quality_repair module. 2. In Quality, create a Control Point with: - Type = Pass-Fail - Control Per = Product or Operation - Set at least 1 Failure Location 3. Create a Repair Order for any product and start the repair process. 4. Perform a quality check, set it to Fail, and select a failure location. 5. Open the product moves, the destination location does not match the selected failure location. Cause of the issue: ------------------------- The failure location was not correctly assigned when a quality check failed during the repair process because _move_to_failure_location determines the destination location based on a stock picking (for receipts) or a production_id (for manufacturing). In the Repair module, however, quality checks are linked to a repair order, so the selected failure location was not set correctly. After this commit: ----------------------- - When a quality check fails in a repair order, the product’s destination location is correctly set to the failure location selected by the user. - This ensures that, upon completion of the repair, the product is moved to the selected failure location, maintaining accurate inventory tracking and management. Task ID:5254334 Forward-Port-Of: odoo/enterprise#99235
This update fixes an issue where users were incorrectly grouping POS orders in the l10n_pe_edi_pos module. The system now validates order groupings, preventing errors and providing clear messages to users attempting to combine invoices that don't meet the required criteria. This ensures accurate reporting and compliance for Peruvian businesses.
Original PR description
Joining the values in the selection field with a coma, and then putting everything in another selection field was plain wrong. We now check a bit better what we're generating, and refrain the user with error messages when they try grouping on the same invoices orders that do not share the necessary key values. Forward-Port-Of: odoo/enterprise#111616
This update resolves an issue where the XML generated for Swiss payments (iso20022_ch) was using an outdated payment schema. The fix ensures the XML adheres to current Swiss banking standards, improving payment processing accuracy and compliance. It also includes enhancements for validator schema and QR-IBAN handling.
Original PR description
**PROBLEM** According to documentation (https://www.six-group.com/dam/download/banking-services/standardization/sps/ig-credit-transfer-sps-2025-en.pdf) PstlAdr must be structured. This isn't the case when generating a xml for the payment method iso20022_ch. **STEP TO REPRODUCE** 1. install l10n_ch and account_iso20022. 2. Create a swiss contact with a full address. And activate payment on the bank account of this contact. 3. Select the Company CH, and set a bank account in the bank journal configuration. 4. Create a vendor payment to the swiss contact. 5. Create a batch payment with it, and validate to get the xml. 6. Open the xml, and notice the PstlAdr isn't structured. Ticket [link](https://www.odoo.com/odoo/project.task/5880247) opw-5880247 Forward-Port-Of: odoo/enterprise#111716 Forward-Port-Of: odoo/enterprise#107025
This update fixes an issue where the canteen cost was incorrectly calculated for Belgian employees, even when they had no attendance recorded. The fix ensures that the canteen cost is only applied if the employee has earned money through attendance or work during the payslip period, preventing incorrect charges.
Original PR description
[FIX] l10n_be_payroll: fix canteen cost computation
Bug reproduction: belgium company -> create a new employee -> new contract (payroll wage > 0) -> canteen_cost = 50 -> create payslip -> allocate time off for full month (such that there will be no attendance) -> recompute payslip -> still canteen cost is calculated
Bug cause:
1 - If l10n_be_canteen_cost is > 0 it was computing the canteen cost line for sure
2 - If result_rules['BASIC']['total'] is > 0 then the canteen cost was 50.
Bug solution:
1 - I add worked_days['WORK100'].amount != 0 to the computation condition.
2 - If there is any earned money from attendance or working in that payslip duration, the canteen cost should be deducted completely
3 - If the employee is absent during the payslip, the employee should not pay the canteen cost.
task - 6045406
Forward-Port-Of: odoo/enterprise#111995
Forward-Port-Of: odoo/enterprise#110991This update fixes an error in how project profitability costs were calculated, ensuring accurate tracking of expenses related to purchase orders. The change adjusts the accounting to correctly reflect the cost of goods, preventing double-counting and improving financial reporting. This ensures the 'To Bill' and 'Billed' amounts align with the actual purchase order cost.
Original PR description
Steps to reproduce: ----------------------------------------- 1. Install `sale_purchase_project` and Accounting modules 2. From settings, enable Budget Management 3. Create a new product with type…
Steps to reproduce: ----------------------------------------- 1. Install `sale_purchase_project` and Accounting modules 2. From settings, enable Budget Management 3. Create a new product with type Service and enable Create a project on order. 4. Create and confirm the sale order with that product (note the name of created project) 5. Create and confirm purchase order as follows: > In other information page, select the created project in the Project field > Add the same product in POL, Set price unit price to 100 and remove any tax 6. Create and post a Vendor Bill for the same vendor as the PO as follows: > Add Bill line with label downpayment > Set the Analytic Distribution to the created project > Set amount to 30 7. Go to the created purchase order > Click on bill matching 8. Select the downpayment bill > Add to PO > Select created PO > Add Down Payment 9. Go to the created project and open dashboard Observation: ----------------------------------------- In the Costs section: Expected Cost: 130 To Bill: 100 Billed: 30 Expected values: ----------------------------------------- Expected Cost: 100 To Bill: 70 Billed: 30 Issue: ---------------------------------------- In the following code: https://github.com/odoo/odoo/blob/7e81c528ae350aab4432207f5655dcfadf6ec627/addons/project_purchase/models/project_project.py#L186-L190 When an invoice line was posted (billed), the code correctly subtracted the cost from `amount_invoiced` (making it negative, representing actual cost). But the billed amount was NOT removed from `amount_to_invoice`. This caused double counting the same cost appeared in both 'To Bill' and 'Billed' Solution: ----------------------------------------- Replaced the quantity-based calculation with a proper amount-based approach: - Introduced `total_invoiced_amount` to track the sum of all non-refund invoice line amounts (both posted and draft). - Modified the unbilled calculation to: `PO_amount - total_invoiced_amount`, ensuring that the unbilled portion accurately reflects what remains to be invoiced from the purchase order. - Excluded refunds from `total_invoiced_amount` calculation because credit notes represent reversals of previous invoices, not consumption of the purchase order. Refunds still correctly affect the "billed" and "to_bill" buckets through the normal invoice line processing. This ensures the accounting principle is maintained: Total Expected Cost = Billed + To Bill = Purchase Order Amount opw-5167734 Forward-Port-Of: odoo/odoo#245649
This update resolves a bug where custom snippets using the 'Category' dynamic snippet didn't display correctly in the website preview. The fix ensures that dynamic content from this snippet is now accurately reflected in the preview, improving the user experience when creating custom website content.
Original PR description
Commit 00cf9375b356b2316e24d97234474685e3fb7f94 added the new dynamic snippet for category of product, with a specific interaction. Commit 534a42029d757935788220549aabb8914302a4f3 shows dynamic content of dynamic snippet in snippets preview dialog, but `DynamicSnippetCategory` was missed (in forward port). This commit includes the interaction to load the dynamic content of "Category" dynamic snippet in the snippets preview dialog. Steps to reproduce: - Open website builder - Add a dynamic snippet `s_dynamic_snippet_category_list` - Save the snippet as a custom snippet - Click on "Custom" snippet category - Bug: The preview for the custom snippet does not have the dynamic part task-5427353 Forward-Port-Of: odoo/odoo#255850
This update resolves an issue where prices were still visible in Sale Order subsections even when 'Hide Prices' was enabled on the parent section. The fix ensures that price information is consistently hidden across all sections, including subsections, providing a cleaner and more accurate view of sale order totals. This improves the user experience and data clarity.
Original PR description
Steps to reproduce: --- - Install `Sales` module. - Create a Sale Order. - Add a section with products. - Add a subsection under it with products. - Enable `Hide Prices` on the section. - Enable…
Steps to reproduce: --- - Install `Sales` module. - Create a Sale Order. - Add a section with products. - Add a subsection under it with products. - Enable `Hide Prices` on the section. - Enable `Hide Composition` on the subsection. - Preview the Sale Order. Issue: --- - Prices are still visible in the subsection (grouped view) even though `Hide Prices` is enabled on the parent section. Root cause: --- - The variable `show_section_total` was defined only within the main rendering block and not reused in the grouped (`t-else`) block. - The grouped section summary (used when `collapse_composition=True`) did not respect the parent section's `collapse_prices` setting, causing prices to be displayed. Solution: --- - Moved `show_section_total` definition outside the main conditional block so it can be reused in both rendering paths. - Applied `t-if="show_section_total"` to price fields in the grouped section summary to ensure consistency with the parent section's price visibility. Before: --- <img width="1030" height="232" alt="image" src="https://github.com/user-attachments/assets/1ac05e5e-6841-420f-909f-994690656cc0" /> After: --- <img width="1023" height="232" alt="image" src="https://github.com/user-attachments/assets/921ae337-4d53-433a-a42b-bdc437181889" /> opw-5979807 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#255464
This update fixes an issue where the shipping weight for deliveries wasn't correctly updated when changing the pack type after using the 'put in pack' action. Previously, the system didn't account for the base weight of the new pack type, leading to inaccurate shipping calculations. This ensures accurate shipping costs are reflected in deliveries.
Original PR description
Issue ----- Doing `action_put_in_pack` then changing the pack type to one with a base weight doesn't correctly update the picking's `shipping_weight`. Steps to reproduce ----- - Enable packages - Create package types: - Big box with base weight of 5kg - Huge box with base weight of 15kg - Create a product AAA with weight of 10kg - Create a delivery for 1 unit of AAA - Confirm delivery - Put in pack - Update the pack type to "Big box" > shipping_weight is still 10kg instead of 15kg - Put in pack again - Update the pack type to "Huge box" > shipping_weight is still 10kg instead of 30kg Cause ----- Changing the package type doesn't change its' `shipping_weight`, so we don't trigger the picking's `_compute_shipping_weight`. ----- Ticket: opw-5975689 Forward-Port-Of: odoo/odoo#255764
This update corrects a bug where modifying a recurring event's start time caused duplicate events to be created in Outlook. The fix ensures Microsoft IDs are preserved, preventing notification issues and maintaining accurate event synchronization.
Original PR description
When an attendee syncs a recurring event where the first occurrence (base event) was modified by the organizer, `_write_from_microsoft` falsely triggers the destructive recreation path. This happens because `_has_base_event_time_fields_changed` compares the exception's modified time against the seriesMaster's pattern time, detecting a "change" even though the master hasn't changed. This causes: - All non-base events lose their microsoft_id and ms_universal_event_id - The base event gets recreated without Microsoft IDs - A duplicate event is pushed to Outlook on the next odoo2microsoft sync - Spurious "join the meeting now" notifications are sent to attendees Add a `follow_recurrence` guard so that when the base event is an exception (follow_recurrence=False), the non-destructive else branch is taken instead, preserving all Microsoft IDs. Forward-Port-Of: odoo/odoo#256263 Forward-Port-Of: odoo/odoo#254414
This update fixes an error in the AVCO audit report that was miscalculating added value, leading to inaccurate financial reporting. The change ensures the AVCO value is correctly updated based on the last incoming movement, resolving discrepancies in reported values and improving the accuracy of inventory valuation.
Original PR description
The AVCO audit report currently recalculates the AVCO value on outgoing moves. Based on the logic in `run_average_batch()`, it should instead use the AVCO value computed on the last ingoing move to…
The AVCO audit report currently recalculates the AVCO value on outgoing moves. Based on the logic in `run_average_batch()`, it should instead use the AVCO value computed on the last ingoing move to recalculate the
`added_value`.
On the other hand, the `added_value` should not be recomputed from the AVCO value on ingoing moves.
Source of those changes: [245141](https://github.com/odoo/odoo/pull/245141)
Steps to reproduce the issue:
1. Create a new product. Do not assign it to a category yet.
2. Set the cost (standard_price) to 10$ on the product form.
3. Add 10 units to inventory.
4. Sell and deliver 5 units to a customer.
5. Purchase 10 units at 25$/ea and validate the receipt.
6. Sell and deliver 5 units to a customer.
7. Assign the product to category with AVCO cost method.
8. Go to the AVCO justification report:
- The added value on the purchase is incorrect: 200$ instead of 250$.
- On the last delivery: the added value is -125$ and the AVCO increases from 20$ to 25$.
Ticket: opw-5921104
Forward-Port-Of: odoo/odoo#252239This update ensures the 'Looking for Help' timer is always displayed for live chat channels, regardless of whether an agent is currently involved. This is crucial for accurately tracking service level agreements and ensuring timely escalation of customer inquiries. The fix also corrects inconsistencies in the mock server's timer calculations.
Original PR description
**Description of the issue this PR addresses:** Ensure the `Looking for Help` timer is shown even when the current user is already a member of the livechat channel. **Steps to Reproduce:** 1. Log in…
**Description of the issue this PR addresses:** Ensure the `Looking for Help` timer is shown even when the current user is already a member of the livechat channel. **Steps to Reproduce:** 1. Log in as a visitor and as Mitchell Admin (Live Chat operator). 2. From the visitor side, select I have a pricing question. 3. From the admin side, mark the conversation as Looking for Help. 4. The channel appears in the Looking for Help category. 5. Observe that the timer should be visible there (e.g., 1m). **Current behavior before PR:** - The timer was hidden when an agent was already a member of the conversation, assuming that once an agent joined, the `waiting for help` phase was over. - However, in escalation workflows an analyst may escalate a chat and remain a member while the channel returns to the `Looking for Help` state for another expert. In this case, the escalating analyst could not see the timer needed to track SLA requirements (e.g., creating a ticket after 10 minutes). - Also, the mock server did not recompute `livechat_looking_for_help_since_dt` when `livechat_status` changes, leading to incorrect timer values (e.g., showing 2d instead of < 1m after switching back to Looking for Help). **Desired behavior after PR is merged:** - The timer is displayed whenever the channel is in `need_help` and `livechat_looking_for_help_since_dt` is set, regardless of whether the current user is a member of the channel. - The mock server now mirrors server-side behavior by updating `livechat_looking_for_help_since_dt` when livechat_status changes, prevents missing recomputations and ensuring consistent test behavior. task-[6009851](https://www.odoo.com/odoo/project/1519/tasks/6009851) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update fixes an issue where sale order prices weren't accurately reflecting the original, undiscounted amount, especially when discounts were applied. The change ensures that the base price is calculated correctly, excluding any discount lines, leading to more reliable pricing and reporting. This improves the accuracy of sales data.
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#245246
This update fixes an issue where kit products were incorrectly reporting the full sales price of each component within delivery DDTs. Previously, the report showed individual component values instead of the total kit value. This change ensures accurate reporting of kit component values, improving financial accuracy for IT companies using the system.
Original PR description
Steps to reproduce: - Have an IT company setup - Create a product with a Sales Price and define a kit BOM with 2 components - Create SO with product - Confirm, go to delivery, validate - Print Issue: In the delivery DDT, there is a summary of the delivery where each item has its own entry (product, quantity, value). However, in case of kit BOM, each component is reported with the full value of the sale operation. Analysis: This occurs because in the report code we don't consider the possibility of kit products, where multiple components are associated to the same sale line. Ticket [link](https://www.odoo.com/odoo/project.task/5013606) opw-5013606 Forward-Port-Of: odoo/odoo#255676 Forward-Port-Of: odoo/odoo#224103