Saturday, August 29, 2026
15 changes · master
Resolved issues and error corrections
When an X (Twitter) account token becomes invalid, Odoo now disconnects the account instead of showing a generic error while refreshing the feed. This reduces confusion for social media users and helps them reconnect the account to restore the feed.
Original PR description
Bug === If the token because invalid, then an UserError is raised instead of disconnecting the account. This is because we "blind raise" all errors we get from X, instead of filtering the error linked to the stream configuration. Task-6499283 Forward-Port-Of: odoo/enterprise#129560 Forward-Port-Of: odoo/enterprise#129110
This update prevents barcode EPC generation from failing when tracked and untracked stock move lines are processed together. It keeps valid tracked items aligned correctly and gives untracked items a clear error instead of causing broader list-view failures.
Original PR description
Problem: `_compute_electronic_product_code` built `tracking_number_list` by filtering out move lines without a lot_id/lot_name, but kept iterating over the full, unfiltered `move_line_ids`. As soon…
Problem: `_compute_electronic_product_code` built `tracking_number_list` by filtering out move lines without a lot_id/lot_name, but kept iterating over the full, unfiltered `move_line_ids`. As soon as a tracked product had an untracked move line mixed in with tracked ones (e.g. a manufacturing byproduct move line with no lot), the two lists fell out of sync: at best tracking numbers got assigned to the wrong move line, at worst `tracking_number_list[i]` went out of range and raised an IndexError. Solution: Exclude untracked move lines from `move_line_ids` before building `tracking_number_list`, so both stay the same length and index- aligned. Untracked lines get their own explicit "no tracking number" error instead of breaking the alignment for the rest. Steps to reproduce: Open runbot V19 -> go to moves history (Inventory) -> add `electronic_product_code` to list view using studio -> remove filter/select all records -> https://anotepad.com/notes/jwxyskc2 Forward-Port-Of: odoo/enterprise#128521 Forward-Port-Of: odoo/enterprise#123859
Website helpdesk forms now keep the translations from the original form template. This ensures customers see the form in the website or visitor language, instead of the language used by the employee who created the helpdesk team.
Original PR description
When a helpdesk team has its website form enabled, a dedicated qweb view is generated from the `ticket_submit_form` template. The arch was read in the language of the user creating or editing the…
When a helpdesk team has its website form enabled, a dedicated qweb view is generated from the `ticket_submit_form` template. The arch was read in the language of the user creating or editing the team, so a team set up by an English user language produced an English form even when the website served another language. Steps to reproduce ================== 1. Set a language other than English as the website default language. 2. While your user language is English, create a helpdesk team with the website form enabled. 3. Open the team form on the website. => The form is rendered in English instead of the website language. Root cause ========== `_ensure_submit_form_view` read the template arch without forcing a language, so it used the current user's language and stored only that value on the generated per-team view. Fix === Read the template arch in the default language of the team's website, so the generated form matches the website language regardless of the user's own language. opw-6303903 Forward-Port-Of: odoo/enterprise#129265 Forward-Port-Of: odoo/enterprise#121164
This fixes an error that could block website editors from saving SEO cover images when unrelated expense attachments existed in the system. The change keeps attachment checks focused on the selected file, avoiding unnecessary access checks on records the user should not see.
Original PR description
### Issue: A user with Website Editor rights but no Accounting or Expense access gets an `AccessError` when using Optimize SEO to add a cover image, if another user has an expense with an attachment…
### Issue:
A user with Website Editor rights but no Accounting or Expense access gets an `AccessError` when using Optimize SEO to add a cover image, if another user has an expense with an attachment
### Steps to reproduce:
- Install `ai` and `hr_expense`
- With Admin, upload an image as an expense
- Update Demo user rights (Accounting: No, Expenses: No, Website: Editor and Designer)
- Log in as Demo
- Go to Website > Site > This page > Optimize SEO
- In Cover Image, add any image, select it and save
Before the fix, an `AccessError` is raised
### Cause:
When Save is triggered, `ai.attachment.vacuum.mark_attachments_used()` is called with the outer domain:
`[('attachment_id', 'in', [3868])]`
The `ai.attachment.vacuum` security rule uses:
`domain_force = [('attachment_id.res_access_write', '=', True)]` https://github.com/odoo/enterprise/blob/474ee25f82a3980530aae4c3450c69d925b6076e/ai/security/security.xml#L1-L24
During optimization, the dotted path is decomposed into an `any` condition:
`('attachment_id', 'any', [('res_access_write', '=', True)])` https://github.com/odoo/odoo/blob/dcb9ad42ba302a5ed0ec334daf4c8a4cb5ec0931/odoo/orm/domains.py#L999-L1004
`_optimize_any_domain_at_level` tries to narrow the comodel scan by reusing the outer constraint on `attachment_id` via `search_domain` in context
It finds `c` where `c.field_expr == 'attachment_id'`, but `c.value` is `{3868}` — a plain set of ids, not a `Domain`
Since the code only recognized `Domain` values, it treated this as unknown and fell back to `comodel_domain = Domain.TRUE`
`_search_res_access` for `ir.attachment` then scanned every attachment using:
`['&', ('res_model', '!=', False),
'|', ('res_id', '!=', False), ('create_uid', '!=', 5)]`
This matches a lot of attachments because the initial `attachment_id` constraint is missing
This scan included an attachment linked to an expense the current user cannot read
`_inaccessible_comodel_records` in `hr_expense` then checked all expenses linked to the found attachments and raised the `AccessError`
This fallback also triggers a second failure when the database contains more than 10 000 attachments: `_search_res_access` caps its unbounded scan at `MAX_SEARCH_LIMIT` and raises `ValueError("Cannot search, too many attachments")`
Confirmed by seeding ~10 500 attachments and reproducing the error
### Notes:
A new test in `test_any_domain_search_context.py` verifies in isolation that unrelated attachments are never scanned when a plain `in` condition narrows the search
opw-6467087
Forward-Port-Of: odoo/odoo#285384
Forward-Port-Of: odoo/odoo#284678This fixes an error that prevented sale timesheet reports from being printed when using German or other translated languages. The report now identifies the description column in a language-independent way, making printing more reliable for multilingual users.
Original PR description
Currently a traceback is occurring when the user tries to print a sale timesheet report in the German language. **<h3>To reproduce the issue:</h3>** 1) Install the `sale_timesheet` module. 2) Create…
Currently a traceback is occurring when the user tries to print a sale timesheet report in the German language. **<h3>To reproduce the issue:</h3>** 1) Install the `sale_timesheet` module. 2) Create a `service` product configured to create a `project and task` on the order. 3) Create a confirmed SO with that product 4) Add a timesheet line to the SO from the `Recorded` stat button 5) Switch to the German language 6) Print `Timesheet(Zeiterfassung)` report 7) A traceback occurs **<h3>Error:</h3>** ``` ValueError: Element „<xpath expr="//th/span[text()='Description']">“ kann nicht in der übergeordneten Ansicht lokalisiert werden ``` **<h3>Cause:</h3>** The xpath relies on the plain text `Description` to identify the `<th>` element. https://github.com/odoo/odoo/blob/5f63fb1af418cc75ff2e382b0455002e7a798071/addons/sale_timesheet/report/report_timesheet_templates.xml#L3-L4 The xpath cannot find the Description header when the language is changed because the text is translated in the base template. As a result, the xpath fails due to the missing target. **<h3>Fix:</h3>** Use a language-independent `name` attribute as the `xpath` anchor. So the `Description` column can be reliably matched regardless of the active language. **<h3> Note:</h3>** This fix requires both modules update, which is generally risky in stable branches. However, this template was recently introduced in [saas-19.4](https://github.com/odoo/odoo/pull/191969/changes#diff-9f4f0d28ffb3aa9fcc4a5f1037bdf532e1e71c4e9703e13b6856ddfd79e15e69R4). So there should be no existing customers using it yet, except saas customers coming from a new database. Therefore, applying this fix in 19.4 is less risky. opw- 6475623 Forward-Port-Of: odoo/odoo#284096
Tax returns will no longer be incorrectly marked as paid when someone replies to or sends a normal chatter message. Payment finalization now only happens from the intended tax payment instructions flow, preventing accidental status changes and reducing accounting confusion.
Original PR description
Before this fix: Replying to or sending a message from the chatter of a tax return could incorrectly change its state to Paid. This happened because action_send_mail() automatically called _action_finalize_payment() for account.return records. After this fix: Payment finalization only happens when the composer is opened from the tax payment instructions flow. Normal chatter messages and replies will no longer change the tax return state to Paid. task-6469536 Forward-Port-Of: odoo/enterprise#129161
Point of Sale settings now keep all employees added to Advanced rights when saving configurations that also use employee login and delivery integrations. This prevents selected staff from disappearing after save, so access permissions remain as intended.
Original PR description
Steps to reproduce ------------------ 1. Go to the Point of Sale settings and select a shop. 2. Enable UrbanPiper and set a delivery provider. 3. Enable "Log in with Employees". 4. Add two employees…
Steps to reproduce ------------------ 1. Go to the Point of Sale settings and select a shop. 2. Enable UrbanPiper and set a delivery provider. 3. Enable "Log in with Employees". 4. Add two employees to the "Advanced rights" field. 5. Save. Observation -> Only the employee of the PoS manager is left, the two employees are gone. Why it's happening ------------------ When saving from the settings, `pos.config` is written with the context key `from_settings_view`, which tells `_preprocess_x2many_vals_from_settings_view` that the commands received for an `x2many` field are the complete new list, so every record not in these commands is unlinked. The `write` of `pos_hr` always puts `advanced_employee_ids` in the values, even when the caller does not write this field, to keep the employees of the PoS managers in the list (1766d6d40a45). Since b48ddcce7bf in enterprise, UrbanPiper writes on the config a second time during the same save, still with `from_settings_view`. That write does not touch the employees, so `pos_hr` fills `advanced_employee_ids` with the managers only (cf 1766d6d40a45), and the ones that were just saved are unlinked. The fix ------- When `pos_hr` adds the field by itself, keep the employees already set on the config instead of starting from an empty list. opw-6500207 Forward-Port-Of: odoo/odoo#284961
New onsite learning events created from the Onsite view or an employee resume now appear immediately after creation. The update relaxes the visibility rules and automatically links the current user's employee record, reducing confusion and duplicate event creation.
Original PR description
Onsite events created from the "Onsite" view or the employee resume selector do not appear immediatlely after creation This occurs because currently the domain for onsite events requires that the…
Onsite events created from the "Onsite" view or the employee resume selector do not appear immediatlely after creation This occurs because currently the domain for onsite events requires that the event to have multiple slots as well as to have at least one employee registered to it. Therefore, newly created records often fail these criteria and remain hidden. In further versions, this pr: https://github.com/odoo/odoo/pull/246285/ changes the domain of the event selector in the employee resume by removing the dependency on the multiple slots and filtering by the specific employee for registration. This change is not stable to backport as it indroduces the `employee_id` field as an invisible field in the xml to be able to compare in the domain. This commit partly changes both domains to not require the multiple slots anymore, while still showing all events for which an employee is registered. This commit also ensures that when an event is created from the Onsite view or selector, the current user's employee will be registered to it. Steps to reproduce - Go to employees->Learning->Onsite - Select New and create an event - Go back to Onsite Courses - You will not see the created event (unless it is multi_slot and an employee was registered) opw-5915686 Forward-Port-Of: odoo/odoo#284200 Forward-Port-Of: odoo/odoo#258952
Fixes an issue where confirming a sales order again after cancellation could fail to create the expected event registration. This helps ensure customers who re-confirm event orders are properly registered, including through portal or preview flows.
Original PR description
**Steps to reproduce:** - Create a SO and add the Event Registration - Standard product and specify an event - Confirm the SO then select "Create/Update registrations" - Cancel the SO, then "Set to…
**Steps to reproduce:** - Create a SO and add the Event Registration - Standard product and specify an event - Confirm the SO then select "Create/Update registrations" - Cancel the SO, then "Set to Quotation" - Select "Preview" and confirm the Sale Order again - There will not be any new registration created when there should be one. **Behavior:** Usually when a sale order is confirmed the `action_sale_order_event_registration` form will be opened which when filled correctly creates registrations. However in certain cases: confirming from the customer portal, or simply closing the form when it is opened, will not trigger `action_make_registration` which creates registrations if it is not already the case `action_confirm()` should be creating the registrations correctly on its own anyway by calling ´init_registrations()´ : https://github.com/odoo/odoo/blob/beed378cde592bc96c1e79a976ac775264b843ed/addons/event_sale/models/sale_order_line.py#L49-L66 This function tries to create each missing registrations by looking at the amount in the so_line and deducting the already created registrations, however since some of them can be cancelled, this computation is wrong. And leads to registration not being created when they should. opw-6444127 Forward-Port-Of: odoo/odoo#280426
This fixes an issue where manually adjusted prices on optional quotation items could be overwritten when a customer changed the quantity in the portal preview. Sales teams can now trust that custom prices remain as intended while still allowing standard pricelist updates when no manual price was set.
Original PR description
When a user manually sets a price on an optional line (overriding the pricelist), and then changes the quantity in the portal preview, the manual price is lost and gets reset to the pricelist price.…
When a user manually sets a price on an optional line (overriding the pricelist), and then changes the quantity in the portal preview, the manual price is lost and gets reset to the pricelist price. Steps to reproduce: --- - Install Sales module and enable Pricelists. - Create a product with qty-based pricelist rules: - min qty: 1 → price: 100 - min qty: 10 → price: 80 - Create a quotation with an optional section containing this product. - Manually change the product price to 150 (overriding pricelist). - Mark the section as optional and preview the quotation. - Change the quantity to 10 in portal preview. Issue: --- - The manually set price (150) is incorrectly reset to the pricelist price (80). Root cause: --- - After [commit], if there is no config parameter set and if there is an active pricelist, we simply call `_reset_price_unit()` without checking whether the price was manually set or not. - Additionally, `_reset_price_unit()` calls `update()` which writes `price_unit` and `technical_price_unit` one by one as separate `write()` calls. The `write()` method has a guard([1]) that strips a lone `technical_price_unit` write unless `sale_write_from_compute` is set in context. Without this flag, `technical_price_unit` is silently discarded, causing it to drift from `price_unit`. On the next qty change, this mismatch is detected as a manual price, permanently blocking further pricelist updates. Solution: --- - Check whether the price was manually set before calling `_reset_price_unit()`, and pass `sale_write_from_compute=True` in context so both `price_unit` and `technical_price_unit` are written correctly. [commit]: https://github.com/odoo/odoo/commit/93b6bdd6a4909bc0b45b90ab6a2d0734a218292d [1]https://github.com/odoo/odoo/blob/fffd987cc98d1ea0cd04e24dda2ed8b64a219cdc/addons/sale/models/sale_order_line.py#L1391-L1399 opw-6426634 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285212 Forward-Port-Of: odoo/odoo#281107
This fixes a problem that could stop PDF quotes from being generated when Quote Builder documents included dynamic fields. Sales teams can now print these quotes without encountering an error in affected Python and PDF library setups.
Original PR description
Issue: --- Due to this issue, generating PDF Quote using Quote Builder with dynamic fields leads to a traceback. This was partially fixed by: e16edc7b0d9f56cb7769068ecf7d64ff8b0f6359 Steps: --- 1-…
Issue: --- Due to this issue, generating PDF Quote using Quote Builder with dynamic fields leads to a traceback. This was partially fixed by: e16edc7b0d9f56cb7769068ecf7d64ff8b0f6359 Steps: --- 1- Using a python 3.13 env, install requirements.txt. (You could instead uninstall pypdf2 and install pypdf==5.4.0) 2- Enable Quote Builder. 3- Create a SO and in quote builder tab, select a document. This document should have dynamic fields. e.g. you could use`Office Furnitures Header` document. 4- Print -> PDF Quote. Cause: --- In previous fix, we fixed the traceback when no dynamic field is set. However, if you have a dynamic field, then inside `PdfWriter._update_field_annotation()`, the font is get from `DR` dict inside acroform: https://github.com/py-pdf/pypdf/blob/f20954f2241640feb484800e191373f8fbdfa44b/pypdf/_writer.py#L917-L929 Even if we are not setting a font, we need to have an empty `DR` dict inside acroform, in order to avoid calling `get` on a none object, which is leading to the traceback. Also in previous fix we were losing `NeedAppearances` inside `AcroForm` by creating new dict which wasn't right. opw-6392001 Forward-Port-Of: odoo/odoo#278881
Checkout now checks whether an invoicing step is required for each shopper individually instead of relying on a shared published state. This prevents customers from skipping required Taiwanese e-invoice details by going directly to payment and avoids one visitor's checkout state affecting another's.
Original PR description
Commit 1facbc0646bd made the checkout validate every `website.checkout.step` through a single flow, but the localization invoicing steps were left out. These steps are shown or hidden by publishing…
Commit 1facbc0646bd made the checkout validate every `website.checkout.step` through a single flow, but the localization invoicing steps were left out. These steps are shown or hidden by publishing or unpublishing a shared step record on each checkout. This is unreliable: the cart and payment pages don't refresh it, so the step can be left in the state set by another visitor. The step is also only validated when the visitor goes through it, so opening `/shop/payment` directly skips it. An override also forces `try_skip_step` off so the checkout page cannot jump past the step. This commit filters the invoicing step out of the breadcrumb steps domain per request when it is not needed, instead of toggling the shared record. The cart is passed to the breadcrumb helpers and their cache is removed so the filter runs for each visitor. The Taiwanese step is now checked by the validation flow, so opening `/shop/payment` redirects back to it while incomplete. The checkout page now jumps to the next step, which is the invoicing step when required, so forcing `try_skip_step` off is no longer needed. task-6045788 See also: - https://github.com/odoo/enterprise/pull/124616 - https://github.com/odoo/upgrade/pull/10806
The attendance Gantt view now correctly displays progress bars for employees who have no attendance records, making empty schedules easier to understand. It also improves the warning display so managers can better distinguish attendance-based employees with missing entries.
Original PR description
[FIX] hr_attendance_gantt: missing progress bars for empty employees On the attendance gantt view, employees without any attendances would have their progress bar hidden, and their label being something like `0h / 8h (+-8h)` in orange. This PR changes it to get the expected behavior: we should also show the progress bar for employees without attendances, and, instead of showing it in yellow all the time, show it when attendance-based employees have no attendances. Since we need to `patch` a renderer class from `hr_attendance_gantt` in `hr_work_entry_attendance`, we need to make the link between those two modules explicit in the manifest file (otherwise hoot tests will fail) This should not change much, as `hr_work_entry_attendance` already installs `hr_attendance_gantt` through an auto_install chain. See this [Discord discussion](https://discord.com/channels/678381219515465750/687338039717920792/1537826154814378167) (in french, sorry) task-6453757
The social media comments window now shows the correct like icon and supports liking Tweets from the comments view. Users also get clearer feedback when creating leads from social posts, and social users have more consistent access to live post information.
Original PR description
Bug === Since https://github.com/odoo/enterprise/commit/86659741990de2ba9c8bf738207edf0e8a0ba8c4 the like icon in the comment in the modal view is broken. We added a new getter `likesIcon` but we didn't use it... Improvements ==== Show create message when creating lead. Make the access of `social.live.post` consistent with the access of `social.post`. Task-6425391
Employer-initiated departure reasons in Belgian payroll are now handled like dismissals. This ensures notice details, 13th month eligibility, and outplacement information are calculated and shown correctly during the end-of-collaboration process.
Original PR description
New departure reasons were added previously but their computation for the end of collaboration process was missing. Selecting employer-initiated reasons (codes `358` and `359`) didn't treat them as "Fired" so the notice duration, notice start date, and 13th month eligibility were not calculated and the `Outplacement` field stayed hidden. This treats these reasons as "Fired" in the departure model and updates the view to display the `Outplacement` field when needed. Task-6499788