Thursday, July 16, 2026
19 changes · 19.0
Resolved issues and error corrections
The website editor now shows the real file size for company logos instead of a misleading placeholder size. This helps users trust that their uploaded logo has not been unexpectedly compressed or degraded when editing their website.
Original PR description
Steps to reproduce: - Go to Website - Upload an image in the company logo (navbar) - Open the website editor and click on the logo - Open the image info panel Current behavior: When clicking on the…
Steps to reproduce: - Go to Website - Upload an image in the company logo (navbar) - Open the website editor and click on the logo - Open the image info panel Current behavior: When clicking on the website logo in the editor, the size shown is always a constant number ~5.9kB, regardless of the actual size of the uploaded logo. The correct size is visible in the browser DOM. This gives users the wrong impression that their image is being heavily compressed or losing quality when it isn't. Reason: When clicking the logo, the editor tries to find the original, unprocessed version of the image so it can support cropping and other edits. It does this by asking the server to match the image's URL to a stored attachment. The website logo is served through a dynamic link (`/web/image/website/<id>/logo/<name>`) that isn't tied to a regular attachment record the way normal content images are, since it isn't uploaded through the usual media picker. Because of this, the server can't find a matching original, and the editor is left without a valid image source to work with. As a fallback, the editor tries to load a placeholder path instead of a real image. This request fails and silently resolves to Odoo's generic "image not found" placeholder. All further processing (and the size calculation) then happens on this small placeholder image instead of the actual logo, which is why the size shown never changes. Fix: When `get_image_info` does not return a usable `original`, `loadImageInfo` now falls back to using the image's own current src as `originalSrc`, instead of leaving it unset. This ensures `loadImage` always receives a valid, resolvable URL, so image processing (and the size shown) reflects the actual logo. opw-6260496 Forward-Port-Of: odoo/odoo#273542
The HTML editor now keeps the toolbar and color picker open when users hover over colors inside an empty unbreakable section. This prevents a distracting flicker and makes formatting content in apps like Todo more reliable.
Original PR description
### Steps to reproduce: - Open the Todo app - In the editor, insert the following content: `<p>a</p><div class="oe_unbreakable"><br></div><p>b</p>` - Double-click the empty unbreakable node - Open the color picker and hover over a color - Toolbar and color picker get closed ### Root cause: - Hovering a color in an empty unbreakable node replaces the `<br>` with a `<font data-oe-zws-empty-inline>` containing a ZWS (`\u200b`). This triggers a selectionchange where `isToolbarVisible()` finds no `<br>` and no visible text, returns false, and closes the toolbar — which reverts the preview, reopens the toolbar, and causes a flicker loop. ### Solution: - Instead of calling `fillEmpty()` (which inserts a ZWS placeholder) on empty blocks containing `<br>` preserve the `<br>` element by appending it directly inside the `<font>` tag which keeps toolbar open. task-6312933 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix improves how the website editor processes image shape conversions so that each step completes in the intended order. It helps prevent inconsistent or incomplete updates when editing page visuals.
Original PR description
`forEach` is a synchronous operation, so it doesn't support promises. We refactor its usage to use `for` loops. task-4794299 Forward-Port-Of: odoo/odoo#276353 Forward-Port-Of: odoo/odoo#275262
The HTML editor now handles tables that do not include body rows without failing to load. This prevents blank editor fields and page errors when users open content copied or imported from other sources.
Original PR description
### Before this PR Opening a record with a content that contains a table with no `<tbody>` (for example a table that was pasted/imported and left with only a caption or header) crashes the editor: the field never renders and the page throws an OwlError instead. Steps to reproduce: 1. Open a record with an html field (e.g. a product description, or a knowledge article). 2. Insert/paste a table that has no body rows. 3. Reload the record. 4. The editor fails to mount — the field is blank and the console shows an OwlError. ### After this PR If there is not tbody, the error doesn't appear Bug introduced by https://github.com/odoo/odoo/pull/272977 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update corrects a typo in an accounting field used when calculating analytic profitability. It helps ensure profitability information is computed from the intended data, reducing the risk of incorrect accounting insights.
Original PR description
Fix field name typo. @qrtl QT6381 Forward-Port-Of: odoo/odoo#276119
A website performance check now handles unusual or unclassified URL query cases without crashing. This keeps internal monitoring more reliable and reduces the chance of avoidable failures during website checks.
Original PR description
Rather than assign `log_target` and blow up if the third branch is taken, use a `defaultdict` and just increment the count in each branch on hit. The continue in the third branch is not strictly necessary, but that way it protects us if anyone decides to add post-processing which also doesn't account for the third branch. Forward-Port-Of: odoo/odoo#276481
User-defined filters now show archived users who are still assigned to them. This prevents administrators from mistaking a filter assigned to archived users as a global filter, improving clarity when reviewing filter ownership and access.
Original PR description
Since 414e55cf7c397, we can assign multiple users to a user-defined filter but because it's now a many2many, any user that got archived won't be shown in the `user_ids` fields anymore, it could mislead the filter being a global filter; whereas it's not. This commit also display archived users so we can see all users effectively assigned to the user-defined filter. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275841
The invoice portal table now keeps the Pay Now button column aligned with the correct header. This prevents a small visual mismatch that could confuse customers viewing or paying invoices online.
Original PR description
Update the XPath of the empty header of the 'Pay Now' button column to the last header of the table. Use a deterministic XPath expression for the 'Pay Now' button column cell. as the last cell of the table.
This fix keeps PDF handling compatible across supported PyPDF2 versions. It prevents older-style PDF operations from triggering deprecated behavior or compatibility issues, helping PDF-related workflows remain stable.
Original PR description
The PdfWriter of the pypdf2 2.x backend only wrapped getFields and _addObject. Other camelCase calls such as appendPagesFromReader, getNumPages and getPage fell through to PyPDF2's own deprecated methods. Callers must keep the camelCase API to stay compatible with the PyPDF2 1.26.0 branch. Related: odoo/enterprise#124482 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
This update corrects internal test setup for push notifications so that only eligible internal users are registered. It helps keep automated checks aligned with the real user flow and reduces false test failures.
Original PR description
The service worker required for push notifications is only available to internal users. This commit fixes the test setup by ensuring non-internal users are no longer registered, matching the expected flow. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#269210
This fixes an issue where users could connect a PayU payment provider but then could not disconnect it because the system incorrectly demanded cleared setup fields. Businesses can now remove or reset PayU payment connections without hitting an unnecessary validation error.
Original PR description
Steps to reproduce: - Install the PayU payment provider. - Click the Connect button and complete the onboarding flow. - After being redirected back to the PayU provider form, click Disconnect. Issue: - After confirming the disconnection, a validation error is raised stating that the PayU fields must be filled in. Cause: - During disconnection, the PayU-specific fields are cleared. At that point, `_check_required_if_provider` is triggered, and since those fields have the `required_if_provider` attribute, it raises a `ValidationError` because their values are now `None`. Fix: - Remove the `required_if_provider` attribute from the PayU-specific fields. opw-6391133
This fixes an issue where deleting text in website tab labels could accidentally remove the tab control while leaving hidden content behind. Website editors can now clear or edit tab names without causing crashes when adding new tabs.
Original PR description
Steps to reproduce: 1. Drop a .s_tabs snippet 2. Click inside a tab to move the selection in it 3. Press backspace (remove each tab name + the last one should be empty) 4. Click on the "+" in the sidebar to add a Tab => Crash or on step 3: 3. Press backspace to delete one tab => Check the DOM: the tab has been removed, but the tab-pane element is still in the DOM and won't be deleted. This is easily fixed by adding `oe_unremovable` on tab links. task-4671317 Forward-Port-Of: odoo/odoo#275240
When a file title is changed, the link popover now shows the updated title instead of the original upload name. This keeps file links clearer and avoids confusion for users reviewing or editing uploaded documents.
Original PR description
Problem: After updating a file name, the link popover still shows the original file name. Cause: The link popover always displays the attachment name instead of the current link content. Solution: Use the link content as the popover title so it reflects the updated file title. Steps to reproduce: - Go to To-Do → Create New. - Upload a file. - Change its title. - Observe that the title shown in the link popover still uses the original file name. task-6213840 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276549 Forward-Port-Of: odoo/odoo#264127
Scheduled generation of missing work entries now applies the language configured for the system user running the job. This prevents work entry names from defaulting to English, making payroll records appear in the expected language for the business.
Original PR description
Issue: ---------------------------------------- When generating work entries with the CRON "Generate Missing Work Entries", the name of the work entries is always in English. Steps to reproduce: ---------------------------------------- - Create a new employee, setup a running contract for them - Run the schedule action "Generate Missing Work Entries" - In Payroll > Work Entries, search for the work entries of the new employee - Their name are in French Cause: ---------------------------------------- When running the cron, `self.env.lang` is `False` so the text aren't translated. Solution: ---------------------------------------- In `_cron_generate_missing_work_entries()` we specify `self.env.user.lang` in the context. As `_cron_generate_missing_work_entries()` uses the root user to run, the language of the work entries will be the one specified on Odoobot. opw-6369109 Forward-Port-Of: odoo/odoo#275952
Fixes an issue where the voice detection sensitivity indicator in call settings could remain frozen at the last detected level if users stopped a test immediately after starting it. This makes the voice and video settings feedback more reliable and avoids confusion during microphone setup.
Original PR description
**Purpose of this PR:** Before this PR, the Voice detection sensitivity indicator in call settings could get stuck showing the last detected level after clicking "Stop" right after "Test". The…
**Purpose of this PR:** Before this PR, the Voice detection sensitivity indicator in call settings could get stuck showing the last detected level after clicking "Stop" right after "Test". The `AudioWorkletNode`'s port kept receiving tic messages briefly after `disconnect()`, since disconnecting only unroutes the audio graph and does not stop the worklet from posting pending messages. <img width="546" height="73" alt="voice_test_bug" src="https://github.com/user-attachments/assets/05a10d23-fe60-4a85-b906-bfec6d235ec5" /> Steps to reproduce: 1. Open Voice & Video Settings. 2. Start the Voice detection sensitivity test. 3. Quickly click Stop immediately after clicking Test. 4. It may take a few tries, but eventually the Voice detection sensitivity indicator remains stuck at the last detected level. > [!NOTE] > this is timing-dependent. A tic message must already be in-flight from the worklet thread when `disconnect()` runs, so it won't happen every attempt. This race condition existed in the `disconnect` callback of `_loadAudioWorkletProcessor` since #66611, but stayed silent until #183969 introduced the Voice detection sensitivity feature in call settings, exposing it. This PR clears `port.onmessage` before disconnecting so late tic messages can no longer update the Voice detection sensitivity indicator after monitoring has stopped. Forward-Port-Of: odoo/odoo#275933
This fixes a compatibility issue that caused PDF flattening in the Sign app to fail in Python 3.10 environments. It helps keep automated builds stable and ensures signed PDF handling continues to work as expected on the supported version.
Original PR description
The flatten_pdf helper was written against the snake_case pypdf API (append_pages_from_reader, .pages, get_object), but that API does not exist on PyPDF2 1.26.0, the camelCase-only release still pinned on Python 3.10 in 19.0, so runbot builds on 3.10 failed with an AttributeError on 'BrandedFileWriter' object has no attribute 'append_pages_from_reader'. Switching to the camelCase spelling (appendPagesFromReader, getNumPages/getPage, getObject) fixes it. RunbotError: https://runbot.odoo.com/odoo/runbot.build.error/941405
The test setup for push notifications now only registers devices for internal users, matching how Odoo Cloud Notifications work in practice. This prevents misleading test coverage for users who should not receive these notifications and improves reliability of the mail enterprise test suite.
Original PR description
Only devices of internal users are registered in order to send them Odoo Cloud Notifications (OCN). However, the test setup registers devices for non-internal users as well. This commit ensures devices are only registered for internal users. Forward-Port-Of: odoo/enterprise#119956
A typo in the payroll accounting code was fixed so the intended customization now matches the standard payroll method name. This helps ensure payroll-related accounting behavior runs as expected and avoids issues caused by the method not being properly recognized.
Original PR description
An incorrect underscore "_" was added when overriding the method, so we're removing it Correct name as it appears here: https://github.com/odoo/odoo/blob/1b8f6802832cfa4d146193a912af1f4445d09f0a/addons/hr/models/hr_version.py#L443
Uruguayan electronic invoices now avoid extra blank lines when combining addenda text with terms and conditions. This helps keep addenda content on the expected page when it fits, preventing unnecessary page breaks in generated CFE PDFs.
Original PR description
## Context When generating a CFE (Comprobante Fiscal Electrónico) that contains both a configured addenda (e.g. bank account details stored in `l10n_uy_edi_addenda_ids`) and terms & conditions from…
## Context
When generating a CFE (Comprobante Fiscal Electrónico) that contains both a configured addenda (e.g. bank account details stored in `l10n_uy_edi_addenda_ids`) and terms & conditions from the invoice's `narration` field, the resulting addenda string could end up with unnecessary blank lines between the two sections, causing the addenda to be rendered on a separate page even when the logical content fits within the 6-line threshold.
## Root Cause
`_l10n_uy_edi_get_addenda` joins both parts without stripping whitespace from either of them first, and adds two lines between addendas and terms and conditions:
addenda = addenda + "\n\n" + term_and_conditions if addenda else term_and_conditions
Two sources independently introduce extra newlines around the separator:
1. **Addenda content** — `_get_legends` returns the raw `content` field value of each addenda record. These fields commonly end with a trailing `\n`, so the addenda string already ends with a newline before the `"\n"` separator is concatenated.
2. **`html2plaintext`** — the `narration` field is stored as HTML. When converted to plain text, `html2plaintext` typically wraps paragraph content in leading/trailing newlines.
The combination of the trailing `\n` from the addenda, the explicit `"\n\n"` separator, and the leading/trailing `\n` from `html2plaintext` produces 2–3 consecutive newlines, which `splitlines()` counts as blank lines.
A realistic 4-line addenda + 1-line narration thus produces **7 lines** instead of the expected 5, crossing the 6-line threshold in `_get_report_params` and triggering `adenda=true` — which forces the addenda onto a separate page unnecessarily.
## Steps to Reproduce
1. Configure a `l10n_uy_edi.addenda` record of type `addenda` with multi-line content (4 lines)
2. Create and confirm an invoice with `narration` set to a short single-line term
3. Generate the CFE PDF via Uruware.
4. Observe that the addenda is rendered on a separate page despite the logical content being only 5 lines.
<img width="1042" height="448" alt="image" src="https://github.com/user-attachments/assets/b538211c-5f37-4648-979d-99cd75cf31c2" />
## Fix
Strip leading and trailing whitespace (including newlines) from both parts before joining them. The ternary is also replaced with an explicit `if/else` for clarity:
def _l10n_uy_edi_get_addenda(self):
addenda = self.l10n_uy_edi_document_id._get_legends("addenda", self)
if self.narration:
term_and_conditions = html2plaintext(self.narration).strip()
if addenda:
addenda = addenda.strip() + "\n" + term_and_conditions
else:
addenda = term_and_conditions
return self._l10n_uy_edi_clean_non_ascii_chars(addenda)
This guarantees exactly one `\n` separator between sections regardless of how the content fields were stored or how `html2plaintext` formatted the narration.
The threshold logic in `_get_report_params` is unchanged: addendas that genuinely exceed 6 lines (after wrapping at 140 chars) continue to be printed on a dedicated page.
Result
<img width="1117" height="456" alt="image" src="https://github.com/user-attachments/assets/3a2d942c-8c37-40f6-bc25-470c0bd25b08" />
Forward-Port-Of: odoo/enterprise#119283