Thursday, October 9, 2025
16 changes · saas-18.4
Enhancements to existing features
Website editor selections are now kept within the currently focused content area, preventing accidental selection of an entire snippet or page section. This makes editing multi-column snippets more predictable and reduces unwanted changes while using mouse selection or Ctrl+A.
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
The website builder now keeps text selection limited to the currently edited section, preventing accidental selection of an entire snippet when users press Ctrl+A or drag with the mouse. This makes editing multi-column layouts more predictable while preserving existing link handling behavior in edge cases.
Original PR description
Reproduction: 1. drag and drop a column snippet in website 2. click on one of the column and press ctrl+A, everything is selected 3. similarly the selection with mouse also can cross the whole…
Reproduction: 1. drag and drop a column snippet in website 2. click on one of the column and press ctrl+A, everything is selected 3. similarly the selection with mouse also can cross the whole snippet After this commit: Selection is now restricted to the element bound by the current container. This is achieved by setting the target element’s `contenteditable` attribute to `true` and its parent’s to `false`. These changes are reverted whenever `change_current_options_containers_listeners` is dispatched, and also during destory, ensuring that the `contenteditable` manipulation only applies dynamically while editing in the website builder. Special cases: 1. If the bound element is already `contenteditable="false"`, we do not modify the attribute to avoid unwanted changes. 2. If there is only one option container (e.g., the outermost container of a snippet), we do not alter the `contenteditable` attribute. This is because the outer section is naturally set as `contenteditable="false"`. Restriction is only needed when there are multiple option containers and an inner one is selected. Predicate in addition to isContentEditable: We introduce `is_element_editable_predicates` to determine if an element is editable, taking into account possible restrictions. Since the parent of the currently bound option container is set to `contenteditable="false"`, elements outside the bound element cannot be normalized. Using this predicate alongside `isContentEditable` ensures these elements are still considered editable when appropriate. This is a rare edge case, as typically only elements within the bound container are edited. See test change 1 for details. Test changes: 1. Link normalization checks if the link element is not unremovable and stops normalization if it is. In the `website_media_iframe_video` tour, there is a case where the link to be normalized is outside the bound option container. The new predicate is used for proper normalization in this scenario. 2. The PR is designed to minimize test breakage. If tests involve only one option container, the new contenteditable manipulation logic does not affect them. 3. A new test for this contenteditable manipulation is added. task-4901968 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The website builder now keeps text selections within the currently edited content block instead of allowing users to accidentally select the whole editing area. This makes editing pages more predictable when using mouse selection, touch selection, or Select All shortcuts.
Original PR description
Before this commit: we can select the whole editing area in website builder by mouse selection or ctrl+a After this commit: when we do selection, the selection is checked that if it's inside the…
Before this commit: we can select the whole editing area in website builder by mouse selection or ctrl+a After this commit: when we do selection, the selection is checked that if it's inside the current element bond to the blue container. if the selection is out of the container, we reset the selection to keep it inside. In this commit, we introduced a new plugin in the html_builder to restrict the selections to the closest div element or p element. We also moved the click listener in builder_options_plugin.js to properly update the blue container to properly align with the restricted selection. We have the restriction function called on event keydown, mouseup and touchend. The listeners are set this way to avoid unnecessary selection restriction during the selection making. The keydown event is to handle ctrl+a to select the whole block and then restrict it. The mouseup and touchend handle the cases where we make the seleciton by mouse or touch. We introduced two concepts SPECIAL_BLOCK_WITH_TEXT_IN_NON_DIV and UNCROSSABLR_ELEMENTS. In general cases, we restrict the selection to the closes div element. However, for special blocks in SPECIAL_BLOCK_WITH_TEXT_IN_NON_DIV, we restrict selection in closest p element. UNCROSSABLR_ELEMENTS can be defined by tag name or classes. They are the elements that the selection should not cross, these elements can appear in a div element and the restricted selection should stop before crossing them. When we do select all, we first check if we should select the whole div element or the p element, then we extend the selection to one direction + correct the selection on UNCROSSABLR_ELEMENTS, then we do the same for the other direction. When we make selection by mouse, we first check if the current selection is beyond the div or p element it should be and we set the selection inside if it is. Then we check inside the div or p element, if there are UNCROSSABLR_ELEMENTS, we correct the selection by stopping the selection ahead of them. task-4901968 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The Peppol configuration screen now remains available even if the service status check fails. This allows users to continue important actions such as unregistering instead of being blocked by a temporary service lookup problem.
Original PR description
Handle `api/peppol/2/get_services` errors gracefully, without blocking critical section of peppol functionnal flow (deletion). If the API endpoint for services returns an error (which should not be affecting any users), the whole peppol config wizard is no longer accessible. The users will therefore not be able to unregister. Note that with this change, if we get an API error, all services will be marked as disabled (which is fair, and better than displaying an API request error) no-task Forward-Port-Of: odoo/odoo#228800
Resolved issues and error corrections
Stripe payments now handle currencies like the Ugandan shilling that use special decimal rules in Stripe. This prevents valid orders from being sent with the wrong amount and rejected at checkout.
Original PR description
Steps: - Activate the 'UGX' currency. - Make a sale order with amount 100 with 'UGX' currency - Try to pay that order (100 USh) using card Issue: - stripe throws the following error - > 'The Checkout Session's total amount must convert to at least 50 cents. 1.00 USh converts to approximately €0.00.' - Hence 100 USh sent was identified as 1 USh by stripe. This confirms issue with decimals and currency mapping. Cause: - 'UGX' is zero-decimal currency but stripe identify it as two-decimal. Fix: - Update mapping for such special currency cases for stripe that don't follow general rules opw-5075707 Forward-Port-Of: odoo/odoo#227688
This fixes an issue where some nested website product description text could not be translated when using multiple languages. Businesses can now maintain complete localized shop content, improving consistency for international customers.
Original PR description
Scenario: - enable second language on website - go to /shop/1 and try to translate description_ecommerce Result: this is not translatable Cause: Since at least…
Scenario:
- enable second language on website
- go to /shop/1 and try to translate description_ecommerce
Result: this is not translatable
Cause:
Since at least b455ea85853dfc19ed01e33986ad270cf80ee5d6 the
contenteditable attribute in ContentEditablePlugin is not set on an
element if it has a contenteditable ancestor.
TranslationPlugin disable all editable nodes containing editable nodes.
So with this combination, if we had a node for example:
```
<div class="oe_editable" data-oe-model="product.template" data-oe-id="1"
data-oe-field="description_ecommerce" data-oe-type="html">
<div>
<span class="oe_editable" data-oe-model="product.template"
data-oe-id="1" data-oe-field="description_ecommerce">
test
</span>
</div>
</div>
```
the contenteditable was added to the parent div.oe_editable, but was
removed by TranslationPlugin so the "test" text was not translatable.
Fix: move the code that adds data-oe-readonly class in the
after_setup_editor_handlers so it is run before contenteditable
attributes are set.
opw-5128618The vendor on-time rate report now shows purchase delivery data even when ordered products do not have a product category. This restores the graph on vendor records and gives purchasing teams a complete view of supplier delivery performance.
Original PR description
**Steps to reproduce:** 1-Install the purchase_stock module. 2-Create a Purchase Order with a new vendor. 3-In the Purchase Order line, add a product without a category. 4-Confirm the order and…
**Steps to reproduce:** 1-Install the purchase_stock module. 2-Create a Purchase Order with a new vendor. 3-In the Purchase Order line, add a product without a category. 4-Confirm the order and validate the generated receipt. 5-In the vendor form view, click the On-time Rate smart button → no graph is visible. **Issue:** https://github.com/odoo/odoo/blob/77b3956ed5635d79ae8dc19423140dc6a10098f1/addons/purchase_stock/report/vendor_delay_report.py#L46-L50 ``` The On-time Rate graph is not displayed in the Vendor Delay report. ``` **Cause:** - From version 18.2, `categ_id` was removed as a required field. The report query still uses an inner join on `categ_id`, which excludes products without a category and prevents data from being generated. - Commit which make `categ_id` non require - https://github.com/odoo/odoo/pull/166323/commits/b039caecbeb04057fbccb1cc88d03a4946f88e8e **Solution:** - Replace the inner join with a left join so that products without a `categ_id` are also included in the report (with null values when the category is not set). **opw** - 4991367 I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#230388 Forward-Port-Of: odoo/odoo#225557
This update ensures the right domestic fiscal position is selected for companies in the UAE, Italy, Mexico, and Cambodia. This helps apply the correct domestic taxes and removes a duplicate Italian setup that could cause confusion or incorrect tax behavior.
Original PR description
Since the fiscal position sequence is used to determin the domestic fiscal position, and hence, the domestic taxes - it is important to properly sequence the fiscal positions. This commit fixes the…
Since the fiscal position sequence is used to determin the domestic fiscal position, and hence, the domestic taxes - it is important to properly sequence the fiscal positions. This commit fixes the following localizations: **AE** Sequences are added making Dubai the domestic fiscal position. However this needs to be improved to automatically prioritize the fiscal position based on the company's state. **IT** (l10n_it_edi_doi) An additional domestic fiscal position was mistakenly added. The correct domestic FP is defined in it's dependency module l10n_it. The duplicate FP is removed. **MX** Sequences are added **KH** Sequences are added No Task - l10n's identified by the fiscal position checks in `test_all_l10n` 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#230654
This fixes several display issues when opening a single bank statement line from a reconciled entry. The reconciliation screen now restores the summary when filters are cleared, avoids expanding unrelated lines, hides an irrelevant Statement button for single-line views, and opens the ledger using the correct journal context.
Original PR description
When you open a statement line from a reconciled move, it opens the bank reconciliation widget with only the selected statement line, which is unfolded by default. However, there are a few issues with this behavior, which are fixed in this commit: 1 - When entering the bank reconciliation widget, the initial line is unfolded. If you remove the filter, all the other lines become unfolded as well. This should not be the case; only the original line should remain unfolded. 2 - By default, the statement summary line is hidden. When the filter is removed, the summary remains hidden. We now ensure the summary is displayed again when the filter is cleared. 3 - The Statement button on the statement line (which is meant to create a new statement) doesn't make any sense when there is only one line. It is now hidden in this case. task-5108118 Forward-Port-Of: odoo/enterprise#95558
Gantt group headers now keep their sticky behavior even when the timeline has many or wide columns. This improves usability, especially on mobile screens, by preventing headers from stretching beyond the visible page area.
Original PR description
Gantt group headers could stop being sticky because their width was fixed based on the number and size of columns. Even though they were set to position: sticky, oversized headers could no longer remain aligned when scrolling, as they extended beyond the viewport and were constrained by the document width. This was especially noticeable on mobile, where group headers are often wider than the screen. The fix applies a max-width style to these headers, capping their size to the available space so they remain sticky without overflowing the document. task-4970992 Forward-Port-Of: odoo/enterprise#96612 Forward-Port-Of: odoo/enterprise#96015
The appraisal skills list now allows horizontal scrolling again on mobile devices. This makes the justification field and add/remove buttons accessible, so employees and managers can complete appraisal skill updates from smaller screens.
Original PR description
Horizontal scrolling has been disabled on the appraisal skills list. An unwanted side effect of that is that the justification field along with the add and remove buttons are not visible on mobile. This PR re-enables the scrolling and removes some dead css. task-5001344 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#230310 Forward-Port-Of: odoo/odoo#222161
This fix ensures nested website content, such as product eCommerce descriptions, can be translated correctly when multiple languages are enabled. It prevents editable translation areas from being disabled too late, improving the translation experience for website managers.
Original PR description
Scenario: - enable second language on website - go to /shop/1 and try to translate description_ecommerce Result: this is not translatable Cause: Since at least…
Scenario:
- enable second language on website
- go to /shop/1 and try to translate description_ecommerce
Result: this is not translatable
Cause:
Since at least https://github.com/odoo/odoo/commit/b455ea85853dfc19ed01e33986ad270cf80ee5d6 the
contenteditable attribute in ContentEditablePlugin is not set on an
element if it has a contenteditable ancestor.
TranslationPlugin disable all editable nodes containing editable nodes.
So with this combination, if we had a node for example:
```
<div class="oe_editable" data-oe-model="product.template" data-oe-id="1"
data-oe-field="description_ecommerce" data-oe-type="html">
<div>
<span class="oe_editable" data-oe-model="product.template"
data-oe-id="1" data-oe-field="description_ecommerce">
test
</span>
</div>
</div>
```
the contenteditable was added to the parent div.oe_editable, but was
removed by TranslationPlugin so the "test" text was not translatable.
Fix: move the code that adds data-oe-readonly class in the
after_setup_editor_handlers so it is run before contenteditable
attributes are set.
opw-5128618Fixes an issue where choosing “Remote” as a job location on a website job application could trigger an error. The Remote option is restored in the location dropdown so users can edit job postings without interruption.
Original PR description
Steps to reproduce: 1. Go to Website → Jobs. 2. Open any job application. 3. Change the Job Location to the option "Remote". 4. A traceback occurs. Before this commit: When selecting a job location, initially no Many2One field is selected, that's why no many2oneid is find. which results in a null value being returned. This caused a traceback error After this commit: The "Remote" option is explicitly included in the dropdown, restoring the previous behavior.
The attendance process now checks whether an employee had an active contract before marking them absent for a missed check-in. This prevents employees from being incorrectly flagged as absent before their contract start date, improving payroll and attendance accuracy.
Original PR description
To register absence, the cron looks for all employees that did not check in the previous day. However, it was not checking if the employee was in contract for that day. This commit fixes the issue by adding a check on the contract date start. task-4987428
Fixes receipt printing for Italian point-of-sale setups using fiscal printers when the receipt screen is skipped. This ensures customers still receive the correct fiscal receipt for the completed sale, avoiding missed printouts and printer errors.
Original PR description
This PR fixes two related bugs happening when printing a receipt with the Italian Fiscal printer First, before this PR, if the option to skip the receipt screen was ticked, then the receipt would…
This PR fixes two related bugs happening when printing a receipt with the Italian Fiscal printer First, before this PR, if the option to skip the receipt screen was ticked, then the receipt would never print. This was caused by the printing logic being implemented on the receipt screen instead of on the pos itself. steps to reproduce: 1. install l10n_it_pos 2. configure one pos 3. configure the IT printer 4. select to skip the receipt screen (print automatically) 5. open the pos 6. make a sale => no ticket printed and the chrome console shows a printer error With this new verison the printing logic was moved to the pos so that printing of fiscal receipts with the italian fiscal printer works, even when receipt screen is skipped. This put to light another potential bug related to how the `order` variable was treated. Before this PR, the printReceipt logic in the module would not pass the order to be printed. This can become a problem upon context changes, where `pos.get_order()` does not return the completed order, but a newly created one. This can for example happen when skipping the receipt screen with the option to "print automatically" (iface_print_auto). After this PR, we keep order as an argument, so we always print the last completed order and not a newly created one. opw-4882480 Forward-Port-Of: odoo/enterprise#95985 Forward-Port-Of: odoo/enterprise#91412
This fixes a survey issue where respondents who entered a comment as their answer in certain multiple-choice questions were incorrectly told the question was unanswered. It also strengthens validation so single-choice questions cannot receive multiple answers, improving data quality and reducing confusing survey behavior.
Original PR description
Issue: When answering a question with a comment in multiple choice with roaming activated for the survey, the UI will display a warning message that says the question requires an answer. Cause: The backend creates a skipped record if none of the pre-created answers is chosen. Solution: Don't create a skipped record if a comment counts as an answer and a comment is provided. Added validation of input and unittests Task-5062984 Forward-Port-Of: odoo/odoo#226022