Monday, August 25, 2025
40 changes · saas-18.4
Resolved issues and error corrections
The Unsplash image picker now handles duplicate images returned in the same search results. This prevents users from seeing an error when searching for images, such as with the term "Inventory", and keeps the media selection flow working smoothly.
Original PR description
This commit fixes an OwlError when we try to render the images received from Unsplash after a search. The issue is that Unsplash can send duplicate images in the same batch of images. When we render those in a `t-foreach` and use the image `id`s as the keys, we get a duplicate key error. This is fixed by expanding on the previous filtering code, which ignored duplicates over multiple batches. We now ignore duplicates within batches as well. At the time of writing, Unsplash is sending us duplicate images on a search for "Inventory". opw-5027032 Forward-Port-Of: odoo/odoo#223789
The time off wizard now opens bulk employee time off and allocation actions in the right navigation context. This prevents confusing breadcrumbs or URLs that made the action look tied to a single leave request.
Original PR description
Task ID: 5004005 **Description of the issue this PR addresses:** When generating time off or allocations for multiple employees using the wizard, the resulting breadcrumb and navigation incorrectly inherit the active_id from the parent view (a specific leave request) appearing as a child of a specific leave record in the breadcrumb and URL. Forward-Port-Of: odoo/odoo#222764
Lead enrichment now stops retrying when a lead has no valid email after the first attempt. This prevents repeated error loops and reduces unnecessary processing for CRM teams.
Original PR description
If a lead has no valid email, try to enrich it only once This will avoid infinite loop of errors of "Enrichment could not be done because…" Forward-Port-Of: odoo/odoo#223967 Forward-Port-Of: odoo/odoo#223874
This fix prevents translated field values from being accidentally overwritten when working with temporary records in different languages. It helps businesses using multiple languages avoid unexpected data loss or blank product names during record creation and editing.
Original PR description
Steps to reproduce the issue:
1. Install a second language (eg, ar_001) and `product`
2. Start odoo shell
3. Run the following:
`(env := env(context=dict(env.context,lang='ar_001')))['product.template'].new({'name':'test'}).with_context(lang='en_US').name`
Current behavior before PR:
The `get` method of the cache will not find a value for the `en_US` language and set the value stored in the env language to a default (`None` in this case) after raising `CacheMiss`
Desired behavior after PR is merged:
We can read and write translatable fields in different languages on transient records without data loss.
opw-4943458
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#223997
Forward-Port-Of: odoo/odoo#222474This fix ensures invoices created from sales orders with global discounts show accurate unit prices and subtotals on down payment lines. It prevents duplicate price adjustments when invoice and company currencies match, improving invoice accuracy for customers and accounting teams.
Original PR description
Steps to reproduce 1. Create a sale order with a product line for 14,990.00 and a 0% tax. Make sure the product's product category has a Downpayment Account set on it. 2. Create a global discount for 990.00. 3. Create an invoice. Notice how the invoice lines' price_unit are respectively 2909.29 and -70.71, and differ from the lines' subtotals. Analysis Propagating the manual tax amounts in `_reduce_base_lines_with_grouping_function` causes a delta amount to be present in `_apply_base_lines_manual_amounts_to_reach` whereas there shouldn't be. Solution Drop manual tax amounts in `_reduce_base_lines_with_grouping_function`. We also take the opportunity to fix the `delta_currency == currency` condition which caused the price_unit to be adjusted twice if the invoice currency is the same as the company currency. opw-5001935 Forward-Port-Of: odoo/odoo#223876
The online store product feed for Google Merchant Center now checks for product images in a lighter way. This prevents memory failures when exporting very large product catalogs, helping merchants keep their product listings available and up to date.
Original PR description
**Traceback:** ```python File "/home/odoo/src/odoo/saas-18.3/addons/website_sale/controllers/gmc.py", line 63, in gmc_data_source 'items': products._prepare_gmc_items(), ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^…
**Traceback:**
```python
File "/home/odoo/src/odoo/saas-18.3/addons/website_sale/controllers/gmc.py", line 63, in gmc_data_source
'items': products._prepare_gmc_items(),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-18.3/addons/website_sale/models/product_product.py", line 251, in _prepare_gmc_items
**product._prepare_gmc_image_links(base_url),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-18.3/addons/website_sale/models/product_product.py", line 281, in _prepare_gmc_image_links
'image_link': urljoin(base_url, self._get_image_1920_url()) if self.image_1920 else '',
^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/fields.py", line 1495, in __get__
self.compute_value(recs)
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/fields_binary.py", line 137, in compute_value
super().compute_value(records)
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/fields.py", line 1661, in compute_value
records._compute_field_value(self)
File "/home/odoo/src/odoo/saas-18.3/addons/mail/models/mail_thread.py", line 468, in _compute_field_value
return super()._compute_field_value(field)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/models.py", line 4627, in _compute_field_value
determine(field.compute, self)
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/fields.py", line 73, in determine
return needle(*args)
^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-18.3/addons/product/models/product_product.py", line 164, in _compute_image_1920
record.image_1920 = record.image_variant_1920 or record.product_tmpl_id.image_1920
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/fields.py", line 1458, in __get__
recs._fetch_field(self)
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/models.py", line 3452, in _fetch_field
self.fetch(fnames)
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/models.py", line 3490, in fetch
fetched = self._fetch_query(query, fields_to_fetch)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/models.py", line 3607, in _fetch_query
field.read(fetched)
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/fields_binary.py", line 149, in read
att.res_id: att.datas
^^^^^^^^^
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/fields.py", line 1495, in __get__
self.compute_value(recs)
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/fields_binary.py", line 137, in compute_value
super().compute_value(records)
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/fields.py", line 1661, in compute_value
records._compute_field_value(self)
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/models.py", line 4627, in _compute_field_value
determine(field.compute, self)
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/fields.py", line 73, in determine
return needle(*args)
^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-18.3/odoo/addons/base/models/ir_attachment.py", line 229, in _compute_datas
attach.datas = base64.b64encode(attach.raw or b'')
^^^^^^^^^^
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/fields.py", line 1495, in __get__
self.compute_value(recs)
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/fields_binary.py", line 137, in compute_value
super().compute_value(records)
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/fields.py", line 1661, in compute_value
records._compute_field_value(self)
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/models.py", line 4627, in _compute_field_value
determine(field.compute, self)
File "/home/odoo/src/odoo/saas-18.3/odoo/orm/fields.py", line 73, in determine
return needle(*args)
^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-18.3/odoo/addons/base/models/ir_attachment.py", line 235, in _compute_raw
attach.raw = attach._file_read(attach.store_fname)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-18.3/odoo/addons/base/models/ir_attachment.py", line 118, in _file_read
return f.read()
^^^^^^^^
MemoryError
```
Issue:
-------
Loading product data in Google Merchant Center was causing memory error when handling a large number of products (14k+ in this case). The problem occurred because `self.image_1920` was used to check if a product had an image. This field is computed and non-stored, so accessing it for many records loads the full high-resolution image into memory, leading to excessive memory usage.
Solution:
------------
Use `image_128` instead of `image_1920` for image existence checks, significantly reducing memory usage and preventing the error.
upg-3063427
opw-4981983
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#222584The online shop now loads smaller product images when processing large product lists. This prevents out-of-memory errors and helps keep shopping pages stable for customers and staff.
Original PR description
This commit fixes an "out of memory" error caused by loading the `image_1920` field into memory for a large `product.product` recordset. To resolve this, the code has been changed to use `image_128` to limit the memory impact. See also: - https://github.com/odoo/odoo/pull/222584 opw-4981983 Forward-Port-Of: odoo/odoo#223949
This fix prevents product image imports from failing when images are provided as URLs and a file type detection library is installed. Businesses can reliably test and import records with image fields without encountering an unexpected upload error.
Original PR description
Currently on SaaS it is not possible to import field image_1920 from file as the operation fails Steps to reproduce: - Have python-magic library installed - Products > Import records - Upload a file containing an URL as image_1920 - Test upload Upload fill fail with error `Could not retrieve URL: <img-url> [image_1920: L1]: argument 2: TypeError: wrong type` Investigation: It occurs because the python-magic library does not work with bytearray opw-5004768
Approved and partially approved time off entries opened from the calendar now keep the Employee field read-only. This prevents accidental changes to the employee linked to an already validated request and keeps the calendar dialog consistent with the main form.
Original PR description
Issue: - In Management > Time Off > Calendar View clicking on an approved or partially approved time off opens a dialog where the Employee field is currently editable even if it's read-only on the form view Fix: - The employee_id field has been set to read-only, same as the form view. task- 4862383 Forward-Port-Of: odoo/odoo#214332
The website editor now shows only the relevant color options when choosing dot and line colors for timeline image sections. This removes confusing choices and makes it easier for users to style timelines consistently.
Original PR description
The goal of this commit is to display only the ‘solid’ and ‘custom’ tabs for the color pickers used to choose the color of the dots and dot lines for the timelineImages snippet. 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
Portal users sharing Microsoft Office documents will now have files saved with the correct file type information. This prevents spreadsheets or similar documents from being treated as zip files, so users can open and interact with them as expected in Odoo.
Original PR description
Issue: Inconsistent mimetype vs filename for documents shared by portal users. 1. Share a ms office document logged in as a portal user. 2. filename is correct, but mimetype is `application/zip`. 3.…
Issue: Inconsistent mimetype vs filename for documents shared by portal users. 1. Share a ms office document logged in as a portal user. 2. filename is correct, but mimetype is `application/zip`. 3. User can't interact with the file to open the file in the Odoo spreadsheet because the mimetype is zip. <img width="387" height="192" alt="image" src="https://github.com/user-attachments/assets/be31f05f-e4e3-44d9-b12a-66f96feba950" /> <img width="274" height="67" alt="image" src="https://github.com/user-attachments/assets/93064e5c-c028-41ce-8e79-1b9c9447c018" /> With `fix_filename_extension()`, the following two commits fixed the issue of Odoo's `guess_mimetype` function incorrectly guessing extensions such as `.xlsx` as `.zip` extension. https://github.com/odoo/odoo/commit/8842b6b43e7c3d1cc67d7d399bb210e4009e23b2 https://github.com/odoo/odoo/commit/34d9375e2efb604f8e7036cb710f44b01f9ccaee However, the `_from_request_file()` still uses the incorrectly guessed mimetype to create an attachment, leading to inconsistent extension from the filename (.xlsx) vs attachment record's mimetype (.zip). This commit fixes the issue by using the mimetype from the filename's extension. This is safe if the following assumption is correct: At the point where the `from_request_file()` attempts to create the attachment record, the `filename` contains correct extension name, presumably fixed by the `fix_filename_extension()`. Commit message to be changed after with Julien's confirmation; opw-4753670 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#222643 Forward-Port-Of: odoo/odoo#222368
This update prevents Point of Sale loyalty barcode activation from failing when a customer record has not been loaded yet. It also makes sales testing more reliable by avoiding duplicate customer names that could cause the wrong customer to be selected.
Original PR description
In PoS loyalty when activating a barcode we were not checking if the partner was already loaded in the PoS resulting of an undefined var. Now we check if the partner is loaded before trying to access it. In PoS sale tour, we were creating a partner called "Test Partner" which was already created in the setup, so sometime the tour was selecting the wrong one. Now, the name of this partner is changed to "AAA - Test Partner invoice" Runbot error: 230992, 230995 https://runbot.odoo.com/odoo/runbot.build.error/230992 https://runbot.odoo.com/odoo/runbot.build.error/230995 Forward-Port-Of: odoo/odoo#223785 Forward-Port-Of: odoo/odoo#223618
This fixes an issue where event time slots could be created on the next day when the user's timezone differed significantly from the event timezone. It ensures calendar slots are displayed on the intended date, reducing scheduling confusion for event organizers.
Original PR description
Purpose ======= Fix the creation of slots at the wrong date. Specification ============= User timezone: Europe/Brussels Event timezone: America/Los Angeles When creating a slot on the calendar on the 10th of April from 8PM to 11PM, the slot is created from 8PM to 11PM but on the 11th of April. In scenario where the conversion from the local tz to the event tz changes the date, the slot is created at the wrong date. => After conversion, the DateTime 'zone' param needs to be set back to the local tz (without changing the converted times) to make sure the calendar renders records correctly as it always expects datetimes expressed in the 'local' tz. related PR: https://github.com/odoo/odoo/pull/205945 Task-4743999 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#217476
This fix prevents errors in workflows that handle very large selections, such as paying or reconciling more than 1,000 accounting records at once. It improves reliability for bulk operations by avoiding a crash caused by temporary record references during data loading.
Original PR description
Currently, an error occurs in any wizard or flow that fetches a many2many field with more than 1000 records. For example, attempting to reconcile more than 1000 journal items at once or making…
Currently, an error occurs in any wizard or flow that fetches a many2many field with more than 1000 records. For example, attempting to reconcile more than 1000 journal items at once or making payment of more than thousands invoices. **Steps to reproduce:** 1. Install Accounting(without demo data). 2. Create a new product (e.g., "p1"). 3. Import a [file](https://docs.google.com/spreadsheets/d/1QslJXsDB70jzJwW64AQ4Br6pVcqGCDLP/edit?usp=sharing&ouid=113889705772170635141&rtpof=true&sd=true) to a customer invoice and confirm it to post the entries. 4. Select all (1010 records) invoices and click on the "Pay" button. Tip: Posting the invoice may take some time. Alternatively, the issue can be reproduced by adjusting the PREFETCH_MAX limit. **Error:** `KeyError - <NewId origin=4250>` **Cause:** Previously, the method used `get_until_miss`, which was handled by stopping on missing keys. However, after [this](odoo/odoo@670897c#diff-720a85988e5f3afc3b2596b9521964ef4a99e03e5b1ea8bea2e8ee476187526aL66) commit, the error handling was removed, leading to the error. - [1] Here, the `move_line_ids` field is a many2many field. When fetching is involved at [2]: - If the number of records is less than PREFETCH_MAX(1000), both the real id and its corresponding origin ids are available in the `field_cache`. - When the number of records exceeds, only the real id will be fetched; new/origin ids are not. This leads to an error. **Fix:** This commit only performs fetching for real IDs and skips for new/origin IDs to avoid KeyError. [1] - https://github.com/odoo/odoo/blob/b424877f9d9f9dd03b766ba57ef7c1dd822fc1d9/odoo/orm/environments.py#L781-L791 [2] - https://github.com/odoo/odoo/blob/b424877f9d9f9dd03b766ba57ef7c1dd822fc1d9/odoo/orm/fields_relational.py#L63-L71 sentry-6789677009
Default self-order kiosk images and backgrounds no longer appear as suggestions in the website image picker. This keeps the website editor cleaner while still allowing user-uploaded kiosk images to be reused on website pages.
Original PR description
Commit [1] forced kiosk image and background attachments to be public, therefore making them displayed in the website media dialog in edit mode, as a suggestion. Steps to reproduce: 1. Install…
Commit [1] forced kiosk image and background attachments to be public, therefore making them displayed in the website media dialog in edit mode, as a suggestion. Steps to reproduce: 1. Install website and pos_self_order 2. Go to the website, edit a page, add a block with an image 3. Double-click on the image => the media dialog is empty 4. Save, go to "Point of Sale" 5. Create a "bar", configure it to be a self-order kiosk, add products with the right categories, etc. 6. Go back to the website, edit the page, double-click on an image => the media dialog now shows the kiosk images and background images This commit solves the issue by making it so that the *default* ones are not displayed in the website media dialog. To do that, while keeping them public, we use "url" type attachment pointing to Odoo static files, which are excluded from the website media dialog because that's the way default snippet images for the website are defined too. For user-chosen kiosk images, we keep the behavior of suggesting them in the website media dialog. After all, they are user-chosen public images, it make sense for the user to potentially use them in their website. At least, with this commit, the website media dialog is less polluted while demoing on runbot. [1]: https://github.com/odoo/odoo/commit/b12c49bffafaf81e59ec97a3a51d1b4e05726142
This fixes the placement of action buttons on log notes written by the current user. The buttons now appear consistently next to the date, making message threads easier to read and use.
Original PR description
**Purpose of this PR:** Fix the position of message actions on log notes authored by the current user. Messages authored by self appear on the right side of the thread. This PR mirrors the action placement logic so that, like messages from others (which show actions to the right of the date), self-authored messages show actions to the left of the date. Before: <img width="392" height="638" alt="image" src="https://github.com/user-attachments/assets/8b66c98f-3e00-47c8-abf3-ee62169ed499" /> After: <img width="392" height="638" alt="image" src="https://github.com/user-attachments/assets/d1a425da-fb0c-4793-a080-735286bc2bb9" /> task-[4689357](https://www.odoo.com/odoo/project/1519/tasks/4689357) Forward-Port-Of: odoo/odoo#223788 Forward-Port-Of: odoo/odoo#216520
Uploading GIF images in the website or HTML editor no longer causes an error when image size details cannot be calculated. Instead of showing invalid size information, the interface now hides unavailable size details, making image uploads smoother for users.
Original PR description
When uploading a gif image file, no transformation occurs and therefore no data URL is being generated. Because of this, trying to compute the size from the data URL fails. This commit avoids computing the size from non transformed images, and hides the size info when it is unavailable. (vs "NaN kb") task-4367641
Fully flexible employees can now request time off in hours as well as full days. This helps related apps like Timesheets and Planning accurately record paid holidays and unavailable time slots.
Original PR description
In this PR, we allow fully flexible employees to take time off in hours or days, to allow other apps such as timesheet to encode their paid holidays or planning to use unavailable intervals Forward-Port-Of: odoo/odoo#217938
This fix reduces memory consumption when generating the French POS inalterability check report, especially for companies with large volumes of orders. By avoiding unnecessary data loading, the report is less likely to fail and should run more reliably on large datasets.
Original PR description
### Problem: Following this pr https://github.com/odoo/odoo/pull/217348, field prefetching is unnecessary when fetching orders, as all required fields are already fetched explicitly. Keeping prefetching enabled causes excessive memory usage. ### Benchmark | Orders | Before | After | |--------|---------|--------| | 1k | 6MB | 5.8MB | | 10k | 42MB | 27MB | | 100k | 534MB | 320MB | | 200K | 1.1GB | 646MB | opw-4901994 Forward-Port-Of: odoo/odoo#223535 Forward-Port-Of: odoo/odoo#222416
Fixed an issue in the HTML editor where removing text color formatting could freeze the editor in certain selections, especially when color was applied to larger content blocks. The toolbar also avoids opening for non-editable content, making editing more reliable and preventing disruptive hangs for users.
Original PR description
**Current behavior before PR:** - When color was applied to a block-level element, attempting to remove it caused the `removeFormat` method to enter an infinite loop. - Removing formatting on a contenteditable false element infinite loop when removing format. **Desired behavior after PR is merged:** - Now, the color is correctly removed from the selected text, even when it is applied to a block-level element. - Now, the toolbar no longer opens when the selected element is contenteditable false task: 4575459 Forward-Port-Of: odoo/odoo#223939 Forward-Port-Of: odoo/odoo#198319
The website builder’s automated checks have been updated to match recent page structure changes, allowing a previously disabled scrollbar-related test to run again. This helps maintain confidence that popup and scrolling behavior in website snippets continues to work as expected, while a separate animation issue remains pending.
Original PR description
This PR aligns tour steps with new DOM structure and re-enable related tests. The `snippet_popup_and_scrollbar` tour was broken due to changes in DOM structure from the new website builder. This tour disabled. Split `test_snippet_popup_with_scrollbar_and_animations` into two separate tours: `snippet_popup_and_scrollbar` and snippet_popup_and_animation`. Note that animation behavior remains unfixed as `On Hover` option is missing.
Fixes a website header styling issue where certain active link styles made the sign in button appear larger than intended. This keeps the header layout consistent and avoids a visual glitch for visitors who are not logged in.
Original PR description
Before this commit, using the "outline" or "border bottom" options for the Link Style (active page link in the header) would make the sign in button bigger. This was due to the fact the selector was not precise enough and would also include the button. This commit improves the selector to avoid impacting the sign in button. Steps to reproduce: - Go to Edit mode - Click on the header - Set the Navbar > Links Style option to "Border Bottom" - Save - Log Out Forward-Port-Of: odoo/odoo#223441
The sales order availability indicator is now hidden when a remaining delivery/backorder has been canceled. This avoids showing a red “No future availability” warning when the customer or user has intentionally decided not to deliver the remaining quantity, reducing confusion for sales teams.
Original PR description
Issue ----- The availability widget states "No future availability" when the backorder of a product has been cancelled, despite the product being available in stock. Steps to reproduce ----- - Create…
Issue ----- The availability widget states "No future availability" when the backorder of a product has been cancelled, despite the product being available in stock. Steps to reproduce ----- - Create a stored product with some on hand quantity - Create a SO for the product - Register a partial delivery (or set the move quantity to 0) - Cancel the backorder - Go back to the SO --> The widget is red and states that there is "No future availability". While the logic is sound, this is confusing the user because they have some qty in stock. Solution ----- The user has manually decided not to deliver the (full qty of) product. There is aready some logic to set the `display_qty_widget` field to False when the user deletes the move from the the picking. https://github.com/odoo/odoo/blob/693e1ef2f934f58209a78787404d5c76be550af4/addons/sale_stock/models/sale_order_line.py#L61 Since the user specified that there is nothing left to deliver, we can extend the condition to include our use case. To do so, we can simply check the existence of a move that is not in a 'done' or 'cancel' state. This will make the widget invisible because it is conditionally invisible. https://github.com/odoo/odoo/blob/693e1ef2f934f58209a78787404d5c76be550af4/addons/sale_stock/static/src/widgets/qty_at_date_widget.xml#L7 ----- Ticket: opw-4942277 Forward-Port-Of: odoo/odoo#220796
Editing the Contact Us page could fail in Italian when the form question block contained duplicate translated labels. The update uses a unique internal identifier so the website editor opens reliably for affected pages.
Original PR description
**Steps to reproduce:** -Install the Website module. -Change the language to Italian. -Enable debug mode. -Go to the website. -Navigate to Website → Contact Us page -Click to Edit -Try to edit the Question block. **Issue:** A traceback occurs with the error: Uncaught Promise > Got duplicate key in t-foreach: Contatto **Cause:** https://github.com/odoo/odoo/blob/d69402a13914428fe6e05cc772157f0eec2c5627/addons/website/static/src/builder/plugins/form/form_option.xml#L7-L11 The t-foreach loop was using model.name as the key, which can result in duplicates (e.g., multiple records with the name 'Contatto'), causing the editor to fail. **Solution:** Use model.id as the t-foreach key instead, ensuring uniqueness for all records. **opw**- 4972693
Refunds made through Viva payment terminals now keep their connection to the original payment even if the Point of Sale page is refreshed mid-process. This prevents staff from having to ask customers to present a card again for refunds that should be completed automatically.
Original PR description
Steps to reproduce: 1. Configure a PoS with a Viva payment terminal 2. Complete an order using the Viva payment method 3. Start to refund the order, and get to the Payment screen 4. At this point, refresh the page 5. Now continue with the refund, using the Viva payment method **Expected behaviour**: The refund is referenced to the original payment, meaning it can be automatically completed without a card. **Actual behaviour**: The refund reference is lost after refreshing, so an unreferenced refund is performed, requiring a card to be presented. The fix is to store the refund reference in the `uiState`, not just on the plain JS object. This way it is saved to the indexedDB. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#223908
This fix prevents an error screen when users cancel an import from areas like Contacts or Products. Instead of attempting to continue with missing record information, Odoo now returns users to the previous screen as expected.
Original PR description
To reproduce: ============== 1. Go to any import screen in Odoo (e.g., Contacts, Products). 2. Click on *Cancel*. → Traceback occurs. Problem: ======== In the old behavior clicking on cancel will call historyBack function which will call restore. https://github.com/odoo/odoo/blob/93451453deff4b316cb1865055d1817f8290f8ef/addons/web/static/src/webclient/actions/action_service.js#L1668 and now in this version. this PR changed the way the exit works. https://github.com/odoo/odoo/pull/211187 So when you click on `Cancel` it will call doAction with resIds undefined instead of calling restore. Solution: ========== In case the resIds is undefined we can proceed with the old behavior which is calling the historyBack function. opw-5015226 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Installing the Belgium Accounting localization now continues successfully even if a referenced demo account tag was previously deleted. This prevents setup failures in demo or test environments and makes the installation process more reliable.
Original PR description
Currently, installing the Belgium Accounting localization fails with an error, if the user has deleted referenced demo account tags. **Steps to reproduce:** - Install the Accounting app. - Delete the "Demo Capital Account" account tag. - Install the Belgium Accounting localization (`l10n_be`). **Error:** `ValueError - External ID not found in the system: account.demo_capital_account` At [1], if the `account.demo_capital_account` is not found, it raises an error if the external ID is missing. This commit prevents the error by explicitly passing `raise_if_not_found=False` and skipping the tag if not found. [1] - https://github.com/odoo/odoo/blob/71d1c9a4bef1c29485e3b0390713a9301ad6db4f/addons/l10n_be/demo/account_demo.py#L13-L19 sentry-6776341713 Forward-Port-Of: odoo/odoo#221227
Opening a Project could fail if a saved embedded action still pointed to Timesheets after that app was uninstalled. The change safely hides unavailable embedded actions instead of crashing, helping users keep working after module changes.
Original PR description
The system failed to evaluate the embedded action, which refers to the Uninstalled module.
**Steps to produce:-**
- Install `Project` and `Timesheets`.
- Go to Project and open any project.
- Click on the `embedded action` icon and select Timesheets.
- `Save` the view from the `embedded action's icon`.
- Now, `Uninstall Timesheets`.
- Now, go to that Project and try to open it.
**Error:-**
`KeyError: 'allow_timesheets'
ValueError: Invalid field in filter of project.project:
[('allow_timesheets', '=', True)]`
**Solution:-**
- Added a safe check using `hasattr` to verify if the `python_method` defined on the embedded action actually exists on the specified model.
- Ensured that if the method is missing or invalid, the embedded action is marked as not visible instead of causing a crash.
**sentry-6738596751**
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#218564This fixes the help link in Timesheets settings so it opens the correct documentation page after a recent documentation structure change. Users clicking the info icon for Time Off settings will no longer be sent to a missing or outdated page.
Original PR description
**Description:** - In a recent documentation commit, the overview section was removed from the timesheets path. commit: odoo/documentation@3bceee836fa8b8edfd8a35c20b21867354f6c078 **Steps to reproduce:** - In a `v18` runbot - open settings > Timesheets settings > under Time off - click on the info icon <img width="1905" height="944" alt="2025-08-21_18-25" src="https://github.com/user-attachments/assets/34ec76ed-46f7-4b08-8c23-10269e105b43" /> --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#223711
Fixed an error that prevented users from printing the yearly salary report in Indian Payroll after payslips were paid. The report now uses the correct translated payroll structure name, avoiding the crash and allowing payroll teams to access annual salary details as expected.
Original PR description
Steps to Reproduce:
- Install Indian Payroll Localization
- create a payslip of any employee and mark as paid
- try to print yearly salary report from reporting menu
Issue:
- A traceback error occurs when printing the yearly salary report.
Reason:
- the sturcture name is translatable field, which gives the value in a dict with the key as languages like {'en_US': 'India: Regular Pay'}.
- The error occurs because the code attempts to use this dictionary with setdefault, which expects a string.
Solution:
- Get the structure name corresponding to the appropriate language from the dictionary before passing it to setdefault.
task-5002518
Forward-Port-Of: odoo/enterprise#91901This fixes an issue where automatic subscription invoicing could stop looking too early if some subscriptions in the first batch were not ready to invoice. The scheduler now applies its limit after checking invoice eligibility, helping ensure ready subscriptions are found and future invoice runs are triggered correctly.
Original PR description
Change was introduced here https://github.com/odoo/enterprise/pull/82340 that caused the cron trigger to not be activated if any of the 31 in the batch of subscriptions found was not to invoice. This is problematic because there could be other subscriptions that meet the domain requirements and the to invoice requirements that are not being seen because the limit is on the search before we check invoiceability. Added a limit to _get_subscriptions_to_invoice instead of on the search in order to take into account both filters for next cron triggers. opw-4850884 Forward-Port-Of: odoo/enterprise#92690 Forward-Port-Of: odoo/enterprise#89787
Bank statement creation no longer fails when a bank journal has no outstanding receipt or payment account configured. This prevents an unexpected error and lets accounting users continue creating statements in this valid setup.
Original PR description
**Steps to reproduce:** - Install Accounting - Make sure that no outstanding receipt/payment account is configured on the Bank journal - Go to the Bank journal - Create a new statement **Issue:** A traceback is raised while executing a SQL query. **Cause:** A IN condition is evaluated with an empty tuple: `AND aml.account_id IN ()` This is due to the fact that there is no outstanding account. **Solution:** Only execute the SQL query if there is an outstanding account. opw-5039931 Forward-Port-Of: odoo/enterprise#93091
Knowledge now saves an article's pending edits before reloading it after another article is moved nearby. This prevents users from accidentally losing work while organizing content in the sidebar.
Original PR description
Currently, when a user moves another article beneath the one they are editing, the record is reloaded, causing any unsaved changes to be lost. This can be especially frustrating if the user has made significant edits. To address this issue, we will ensure that any changes made to the article are saved before the record is reloaded. This will prevent accidental loss of work and improve the editing experience. Steps to reproduce the issue: 1. Open an article 2. Make changes to the article (e.g, type or edit text) 3. Drag and drop another article beneath the current one. => The article reloads, and your unsaved changes are lost. In the `moveArticle` function, we will check if the record has unsaved changes (i.e., if it's "dirty"). If so, we will save the current content before triggering a reload via `this.props.record.model.load()`. Task-4636494 Forward-Port-Of: odoo/enterprise#87937
Belgian payroll users now see a clear message when trying to change an employee working schedule without a contract start date. This prevents an unexpected system error and helps users correct the missing employee information before continuing.
Original PR description
Prevent the apparition of a traceback when changing the schedule through the wizard
Appointment users can now create resource-based appointments directly from calendar events without hitting an access error. This fixes a permissions issue that blocked bookings when an appointment type had a single resource, improving reliability for day-to-day scheduling.
Original PR description
Before this commit, trying to create an appointment through a calendar event as an user will raise an AccessError. This is because in this fix #76653 we needed to make sure the appointment_resource_id is being set on the calendar event and for this we needed to make it readonly. This causes that an user, is not able to get the proper access rights to read on to the 'appointment.booking.line' which is being triggered since inside each booking line, we have an appointment_resource_id which is a many2one to the appointment resource. To fix this, we are adding a sudo on the booking lines when we only have 1 booking line and the appointment resource is set on the calendar event. This way, the user will be able to read the booking lines and create the appointment. opw-4614976 Forward-Port-Of: odoo/enterprise#90149 Forward-Port-Of: odoo/enterprise#88373
The Indian Payroll EPF report export has been corrected so users can download the XLSX file without encountering an error. This ensures payroll teams can generate required provident fund reports reliably after payslips are paid.
Original PR description
Steps to Reproduce: - Install Indian Payroll Localization - enable the Provident Fund of the employee in Payroll tab of employee form view - Create a payslip of the employee and mark as paid - Try to export XLSX file of EPF report from the reporting menu Issue: - A traceback error occurs when click on 'Export XLSX File' report. Reason: - The domain used to search payslips uses an incorrect date format. Solution: - Use the correct date format for the search. task-5002518
Payroll configuration screens now show Python code correctly for salary rules and rule parameters. This helps payroll administrators review and maintain calculation logic more reliably, reducing confusion when checking pay rule settings.
Original PR description
Forward-Port-Of: odoo/enterprise#92960 Forward-Port-Of: odoo/enterprise#91811
Helpdesk ticket portal pages no longer show an unrelated “Timesheets” breadcrumb just because the ticket contains timesheet entries. This keeps customer-facing navigation clearer and avoids confusion when viewing tickets online.
Original PR description
**Steps to reproduce:** 1. Install 'Helpdesk Sale Timesheet' and 'Website' modules. 2. Create a helpdesk ticket and add timesheets to it. 3. Go to the portal view of helpdesk tickets. **Issue:** If the ticket contains timesheets, the "Timesheets" breadcrumb appears in the portal ticket view. **Cause:** The reason the timesheet variable is used in the hr_timesheet portal template is because it makes the condition evaluate to true, which is why the breadcrumb for Timesheets is displayed. **Fix:** Renamed the loop variable in `t-foreach` to avoid shadowing the `timesheet` variable from the outer context. task-4744294 Forward-Port-Of: odoo/enterprise#85411
Manufacturing users can now search for employees to assign without hitting errors caused by records from companies they cannot access. The employee selection is limited to companies available to the current user, making work order assignment more reliable in multi-company setups.
Original PR description
Task ID: 5005753 Prev behavior: when searching for employees to assign (employee_assigned_ids) from the manufacturing page and using "Search More", it was raising an error bc it was fetching data even from employees part of companies he's not part of New behavior: added domain over employee_assigned_ids such that the accessed users are only the ones being part of a company the current user can access
This change prevents Accounting from crashing when users reconcile more than 1,000 journal items at once. It improves reliability for high-volume accounting workflows by ensuring large reconciliations can complete without a key error.
Original PR description
Currently, an error occurs when attempting to reconcile more than 1000 journal items at once. **Steps to reproduce:** 1. Install the **Accounting** app. 2. Create a new product (e.g., "p1"). 3.…
Currently, an error occurs when attempting to reconcile more than 1000 journal items at once. **Steps to reproduce:** 1. Install the **Accounting** app. 2. Create a new product (e.g., "p1"). 3. Import a [file](https://docs.google.com/spreadsheets/d/1QslJXsDB70jzJwW64AQ4Br6pVcqGCDLP/edit?usp=sharing&ouid=113889705772170635141&rtpof=true&sd=true) to a customer invoice and confirm it to post the entries. 4. Open Journal Items and filter by `Account = 12100 Account Receivable`. 5. Click on the _Reconcile_ button. **Tip:** Posting the invoice may take some time. Alternatively, the issue can be reproduced by adjusting the PREFETCH_MAX limit. **Error:** `KeyError - <NewId origin=4250>` **Cause:** Previously, the method used `get_until_miss`, which was handled by stopping on missing keys. However, after [this](https://github.com/odoo/odoo/commit/670897c6dc8e744f0622cb96c78fdab0a6249fb6#diff-720a85988e5f3afc3b2596b9521964ef4a99e03e5b1ea8bea2e8ee476187526aL66) commit, the error handling was removed, leading to the error. - [1] Here, the `move_line_ids` field is a **many2many** field. When fetching is involved at [2]: - If the number of records is **less** than `PREFETCH_MAX`, both the **real ids** and its corresponding **origin ids** are available in the `field_cache`. - When the number of records **exceeds**, **only the real ids** are prefetched and cached; origin ids are not. This leads to an error. **Fix:** This commit ensures that real ids are used when accessing the field cache, preventing a key error when the number of records exceeds the prefetch limit. [1] - https://github.com/odoo/odoo/blob/b424877f9d9f9dd03b766ba57ef7c1dd822fc1d9/odoo/orm/environments.py#L781-L791 [2] - https://github.com/odoo/odoo/blob/b424877f9d9f9dd03b766ba57ef7c1dd822fc1d9/odoo/orm/fields_relational.py#L63-L71 sentry-6789677009