Daily updates from Odoo
Tuesday, June 23, 2026
38 changes · master
Resolved issues and error corrections
A recent payroll upgrade introduced an error preventing users from accessing the Wage Types configuration in Odoo. This change required restoring the action's update functionality to ensure the system correctly reflects the latest payroll structure settings. This fix resolves a critical issue impacting Swiss payroll functionality.
Original PR description
The Wage Types action record salary rules belonging to the CHMONTHLYELM payroll structure. During the payroll refactoring, salary rules were changed to support multiple payroll structures. in the…
The Wage Types action record salary rules belonging to the CHMONTHLYELM payroll structure.
During the payroll refactoring, salary rules were changed to support multiple payroll structures. in the 19.4 version here c2f18f3
Where struct_id M20 field is changes to [M2m](https://github.com/odoo/upgrade/pull/10294/changes#diff-541246af074f8ac598b0a274ef9861f5fe974e6ded22e906c3d35adecb284e8cR51) struct_ids
Opening Payroll > Configuration > Company > Wage Types will cause the issue as the action has not updated which Failes the [ci/upgrade_enterprise](https://runbot.odoo.com/runbot/batch/2598248/build/115031148)
Steps to reproduce.
- Create a database on saas-19.3.
- Install l10n_ch_hr_payroll.
- Upgrade the database to master.
- Open Payroll > Configuration > Company > Wage Types.
- Error raise error(message % (*args, self.field_expr, self.operator, self.value)) ValueError: Invalid field hr.salary.rule.struct_id in condition
('struct_id.code', '=', 'CHMONTHLYELM')
The issue will directly reproduce in the runbot maste too installed the l10n_ch_hr_payroll module and access the wage type.
```
Adding menu ('l10n_ch_hr_payroll.menu_l10n_ch_wage_types', 1598, 'Payroll > Configuration > Company > Wage Types', 2337) to the failing menus
Traceback (most recent call last):
File "[/data/build/odoo/odoo/orm/domains.py](https://github.com/odoo/odoo/blob/2fb276668c824d6fe759a7c51f9ef10677532508/odoo/orm/domains.py#L964)", line 964, in __get_field
field = model._fields[field_name]
~~~~~~~~~~~~~^^^^^^^^^^^^
KeyError: 'struct_id'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "[/data/build/upgrade/migrations/base/tests/test_mock_crawl.py](https://github.com/odoo/upgrade/blob/07bec6a1031093cf001642f8df21e40421cce3be/migrations/base/tests/test_mock_crawl.py#L346)", line 346, in crawl_menu
self.mock_action(action_vals)
File "[/data/build/upgrade/migrations/base/tests/test_mock_crawl.py](https://github.com/odoo/upgrade/blob/07bec6a1031093cf001642f8df21e40421cce3be/migrations/base/tests/test_mock_crawl.py#L377)", line 377, in mock_action
return self.mock_act_window(action)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "[/data/build/upgrade/migrations/base/tests/test_mock_crawl.py](https://github.com/odoo/upgrade/blob/07bec6a1031093cf001642f8df21e40421cce3be/migrations/base/tests/test_mock_crawl.py#L537)", line 537, in mock_act_window
mock_method(model, view, fields_list, domain, group_by)
File "[/data/build/upgrade/migrations/base/tests/test_mock_crawl.py](https://github.com/odoo/upgrade/blob/07bec6a1031093cf001642f8df21e40421cce3be/migrations/base/tests/test_mock_crawl.py#L675)", line 675, in mock_view_list
return self.mock_view_tree(model, view, fields_list, domain, group_by)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "[/data/build/upgrade/migrations/base/tests/test_mock_crawl.py](https://github.com/odoo/upgrade/blob/07bec6a1031093cf001642f8df21e40421cce3be/migrations/base/tests/test_mock_crawl.py#L688)", line 688, in mock_view_tree
self.mock_web_search_read(model, view, [domain], fields_list)
File "[/data/build/upgrade/migrations/base/tests/test_mock_crawl.py](https://github.com/odoo/upgrade/blob/07bec6a1031093cf001642f8df21e40421cce3be/migrations/base/tests/test_mock_crawl.py#L722)", line 722, in mock_web_search_read
data = model.search_read(domain=domain, fields=fields_list, limit=80, order=filter_order(model))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "[/data/build/odoo/odoo/orm/models.py](https://github.com/odoo/odoo/blob/2fb276668c824d6fe759a7c51f9ef10677532508/odoo/orm/models.py#L5187)", line 5187, in search_read
records = self.search_fetch(domain or [], fields, offset=offset, limit=limit, order=order)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "[/data/build/odoo/odoo/orm/models.py](https://github.com/odoo/odoo/blob/2fb276668c824d6fe759a7c51f9ef10677532508/odoo/orm/models.py#L1462)", line 1462, in search_fetch
query = self._search(domain, offset=offset, limit=limit, order=order or self._order)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "[/data/build/odoo/odoo/orm/models.py](https://github.com/odoo/odoo/blob/2fb276668c824d6fe759a7c51f9ef10677532508/odoo/orm/models.py#L4773)", line 4773, in _search
domain = domain.optimize_full(self)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "[/data/build/odoo/odoo/orm/domains.py](https://github.com/odoo/odoo/blob/2fb276668c824d6fe759a7c51f9ef10677532508/odoo/orm/domains.py#L472)", line 472, in optimize_full
return self._optimize(model, OptimizationLevel.FULL)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "[/data/build/odoo/odoo/orm/domains.py](https://github.com/odoo/odoo/blob/2fb276668c824d6fe759a7c51f9ef10677532508/odoo/orm/domains.py#L486)", line 486, in _optimize
previous, domain = domain, domain._optimize_step(model, next_level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "[/data/build/odoo/odoo/orm/domains.py](https://github.com/odoo/odoo/blob/2fb276668c824d6fe759a7c51f9ef10677532508/odoo/orm/domains.py#L670)", line 670, in _optimize_step
children = self._flatten(child._optimize(model, level) for child in self.children)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "[/data/build/odoo/odoo/orm/domains.py](https://github.com/odoo/odoo/blob/2fb276668c824d6fe759a7c51f9ef10677532508/odoo/orm/domains.py#L628)", line 628, in _flatten
for child in children:
File "[/data/build/odoo/odoo/orm/domains.py](https://github.com/odoo/odoo/blob/2fb276668c824d6fe759a7c51f9ef10677532508/odoo/orm/domains.py#L670)", line 670, in <genexpr>
children = self._flatten(child._optimize(model, level) for child in self.children)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "[/data/build/odoo/odoo/orm/domains.py](https://github.com/odoo/odoo/blob/2fb276668c824d6fe759a7c51f9ef10677532508/odoo/orm/domains.py#L486)", line 486, in _optimize
previous, domain = domain, domain._optimize_step(model, next_level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "[/data/build/odoo/odoo/orm/domains.py](https://github.com/odoo/odoo/blob/2fb276668c824d6fe759a7c51f9ef10677532508/odoo/orm/domains.py#L988)", line 988, in _optimize_step
field, property_name = self.__get_field(model)
^^^^^^^^^^^^^^^^^^^^^^^
File "[/data/build/odoo/odoo/orm/domains.py](https://github.com/odoo/odoo/blob/2fb276668c824d6fe759a7c51f9ef10677532508/odoo/orm/domains.py#L966)", line 966, in __get_field
self._raise("Invalid field %s.%s", model._name, field_name)
File "[/data/build/odoo/odoo/orm/domains.py](https://github.com/odoo/odoo/blob/2fb276668c824d6fe759a7c51f9ef10677532508/odoo/orm/domains.py#L951)", line 951, in _raise
raise error(message % (*args, self.field_expr, self.operator, self.value))
ValueError: Invalid field hr.salary.rule.struct_id in condition ('struct_id.code', '=', 'CHMONTHLYELM')
2026-06-22 12:02:29 [ERROR](https://github.com/odoo/upgrade-util/blob/f3431df77099e3df9299b9d6fb1ea001796c176c/src/testing.py#L483)
FAIL: TestCrawler.test_check
Traceback (most recent call last):
File "[/data/build/upgrade-util/src/testing.py](https://github.com/odoo/upgrade-util/blob/f3431df77099e3df9299b9d6fb1ea001796c176c/src/testing.py#L483)", line 483, in test_check
self.check(value)
File "[/data/build/upgrade/migrations/base/tests/test_mock_crawl.py](https://github.com/odoo/upgrade/blob/07bec6a1031093cf001642f8df21e40421cce3be/migrations/base/tests/test_mock_crawl.py#L230)", line 230, in check
self.assertFalse(diff, msg)
AssertionError: [('l10n_ch_hr_payroll.menu_l10n_ch_wage_types', 1598, 'Payroll > Configuration > Company > Wage Types', 2337)] is not false : At least one menu or view working before upgrade is not working after upgrade.
```
Soln:- Update the action with the correct field.
ref :- https://runbot.odoo.com/runbot/batch/2598248/build/115031148This update fixes a bug where rental order PDFs didn't show the pickup and return dates. The fix adds the necessary date fields to the PDF report, ensuring consistent information between the portal and the printed sales orders. This improves clarity for customers receiving rental order details.
Original PR description
**Problem:** On a rental order, the sales order / quotation PDF no longer shows the pickup and return dates. The line description only displays the rental duration (e.g. "2 Days"), so the actual…
**Problem:** On a rental order, the sales order / quotation PDF no longer shows the pickup and return dates. The line description only displays the rental duration (e.g. "2 Days"), so the actual dates are missing from the printout. **Steps to reproduce:** 1. Create a rental order with a rentable product and pickup/return dates 2. Print the order (Print > Quotation / Order) 3. Observe the PDF shows only the duration, with no pickup/return dates **Current behavior:** Neither the rental dates (removed from the description) nor any pickup/return field appear on the PDF. **Expected behavior:** The pickup and return dates are shown on the rental order PDF. **Cause of the issue:** The rental line description was intentionally reduced to only the duration (`_get_rental_duration_description`), the actual dates being meant to appear as dedicated Pickup/Return fields. This was added to the customer portal (`sale_rental_portal_details` inherits `sale.sale_order_portal_content`) but the equivalent was never added to the `sale.report_saleorder_document` PDF report, so the dates disappeared from the printout. **Fix:** Inherit the sale order report to render the order-level pickup and return dates for rental orders, mirroring the existing portal presentation so the PDF and the portal stay consistent. opw-6268640 Forward-Port-Of: odoo/enterprise#119736
A bug preventing users from adding cover images to Knowledge articles has been resolved. The issue stemmed from a missing callback function during the upload process, causing the upload to fail. This update ensures cover images can now be successfully added, improving the article creation workflow.
Original PR description
Steps to reproduce: 1. Install Knowledge. 2. Create an article. 3. Open the more actions menu. 4. Click "Add Cover". 5. Upload a cover image. Issue: - The upload crashes with the following traceback:…
Steps to reproduce: 1. Install Knowledge. 2. Create an article. 3. Open the more actions menu. 4. Click "Add Cover". 5. Upload a cover image. Issue: - The upload crashes with the following traceback: `Uncaught Promise > this.props.setAbortUploadsCallback is not a function` Cause: - `KnowledgeCoverSelector` extends the html_editor `ImageSelector`, whose upload flow registers an abort callback through setAbortUploadsCallback. The generic MediaDialog provides this callback, but KnowledgeCoverDialog renders KnowledgeCoverSelector directly and did not pass it. As a result, the inherited upload flow called a missing prop. Solution: - Pass setAbortUploadsCallback from KnowledgeCoverDialog to KnowledgeCoverSelector and abort pending uploads when the cover dialog is discarded. Alternative approach: - Make ImageSelector tolerate callers that do not provide setAbortUploadsCallback by calling it with optional chaining. opw-6176716 Forward-Port-Of: odoo/enterprise#121162 Forward-Port-Of: odoo/enterprise#116906
This update fixes an issue preventing users from accessing payslip lists within the employee departure process. The changes include making fields read-only to prevent unintended modifications and relocating currency data to improve data handling. This ensures accurate payslip access and avoids errors.
Original PR description
Bug 1: In the departure tab of the Employee, you can't open the payslip list Fix: Added a check to get the correct departure id depending on the model we are in Bug 2: You can select payslips for other employees than the departing employee and the payslips list is not affected Fix: made fields `l10n_be_payslip_n_ids` and `l10n_be_payslip_n1_ids` readonly so they can't be modified in the UI without being saved Bug 3: You get an error because you can't read `currency_id` when opening n payslips (happens when the monetary fields are shown in the list) Fix: moved the `currency_id` to be inside the list instead of the parent form task-id: 6265648 Forward-Port-Of: odoo/enterprise#119402
This update resolves an issue preventing the 'Send to SII' option from appearing on Chilean vendor bills. The fix adjusts internal settings to correctly display this functionality, ensuring accurate electronic invoice submission for Chilean businesses. This ensures compliance with local tax regulations.
Original PR description
**Steps to reproduce:** * Install the **l10n_cl_edi** module. * Go to **Accounting → Configuration → CAFs**, create a new CAF, and upload a valid CAF…
**Steps to reproduce:** * Install the **l10n_cl_edi** module. * Go to **Accounting → Configuration → CAFs**, create a new CAF, and upload a valid CAF [XML](https://www.odoo.com/mail/message/1097235975) file. * Create a new **Purchase Journal** with **Use Documents** enabled. * Create a vendor bill using this journal. * Set the **Document Type** to **46 - Liquidación-Factura Electrónica**. * Confirm the vendor bill. **Observed behavior:** * The Send button is not visible on the confirmed vendor bill despite the DTE being generated and `l10n_cl_dte_status` being set to `not_sent`. **Cause:** * `_compute_display_send_button` in `account` only returns `True` for sale documents (`is_sale_document()`), so the "Send" button — which opens the Send & Print dialog containing the "Send to SII" option — was never shown on vendor bills. * `_get_move_constraints` in `account.move.send` unconditionally adds a `not_sale_document` constraint for non-sale documents, blocking the Send & Print dialog from processing vendor bills even if the button were visible. * The cron's `cron_run_sii_workflow` only processes moves with `l10n_cl_dte_status = 'ask_for_status'`, skipping moves still in `not_sent` state. **Fix:** * Override `_compute_display_send_button` in `l10n_cl_edi` to also show the "Send" button on posted moves with `l10n_cl_dte_status == 'not_sent'`, matching the pattern used by `l10n_br_edi`. * Override `_get_move_constraints` in `l10n_cl_edi` to remove the `not_sale_document` constraint for Chilean purchase documents with `not_sent` status, matching the pattern used by `l10n_br_edi`. **REF** During this [refactor](https://github.com/odoo/enterprise/pull/103427/changes/f5617ecf7584cf019897408df94b002622f48d9d), these two methods were inadvertently missed and were not overridden opw-6300571 Forward-Port-Of: odoo/enterprise#121233 Forward-Port-Of: odoo/enterprise#120818
This update significantly speeds up how Odoo retrieves document access permissions, particularly for the 'my counters' route. By switching to a subquery, the system now utilizes an index more efficiently, resulting in a much faster response time for users accessing documents. This improves overall performance and user experience.
Original PR description
The '/my/counters' route is hit a lot of times on big databases like odoo.com One thing it does is a `self.env['documents.document].search_count([])` With this commit, we use a subquery for the…
The '/my/counters' route is hit a lot of times on big databases like odoo.com
One thing it does is a `self.env['documents.document].search_count([])`
With this commit, we use a subquery for the folder access instead of the current LEFT JOIN.
This ok since the number of folders is typically small compared to regular documents and the query is fast since it can use the index on 'type'
Before as portal user
------
2x Seq Scan
```
Aggregate (cost=1900290.73..1900290.74 rows=1 width=8) (actual time=282.271..282.276 rows=1 loops=1)
Buffers: shared hit=66629
-> Hash Left Join (cost=41649.94..1900044.55 rows=98472 width=0) (actual time=184.202..282.267 rows=3 loops=1)
Hash Cond: (documents_document.folder_id = documents_document__folder_id.id)
Filter: ((hashed SubPlan 2) OR ((documents_document.owner_id = 6) AND ((documents_document.shortcut_document_id IS NULL) OR (documents_document.shortcut_document_owner_id = 6))) OR (((documents_document.access_via_link)::text = ANY ('{edit,view}'::text[])) AND (documents_document.folder_id IS NOT NULL) AND ((hashed SubPlan 4) OR ((documents_document__folder_id.owner_id = 6) AND ((documents_document__folder_id.shortcut_document_id IS NULL) OR (documents_document__folder_id.shortcut_document_owner_id = 6)))) AND (documents_document.is_access_via_link_hidden IS NOT TRUE)))
Rows Removed by Filter: 28085
Buffers: shared hit=66629
-> Seq Scan on documents_document (cost=0.00..1857903.54 rows=187073 width=26) (actual time=0.022..109.288 rows=28088 loops=1)
Filter: ((active IS TRUE) AND ((hashed SubPlan 2) OR ((owner_id = 6) AND ((shortcut_document_id IS NULL) OR (shortcut_document_owner_id = 6))) OR (((access_via_link)::text = ANY ('{edit,view}'::text[])) AND (folder_id IS NOT NULL) AND (is_access_via_link_hidden IS NOT TRUE))))
Rows Removed by Filter: 342576
Buffers: shared hit=33313
SubPlan 2
-> Nested Loop (cost=0.85..357.41 rows=99 width=4) (actual time=0.008..0.009 rows=0 loops=2)
Buffers: shared hit=6
-> Index Scan using documents_access__partner_id_index on documents_access (cost=0.43..105.55 rows=103 width=9) (actual time=0.008..0.008 rows=0 loops=2)
Index Cond: (partner_id = 7)
Filter: ((expiration_date IS NULL) OR (expiration_date >= '2026-06-18 10:16:38'::timestamp without time zone))
Buffers: shared hit=6
-> Index Scan using documents_document_pkey on documents_document documents_access__document_id (cost=0.42..2.44 rows=1 width=9) (never executed)
Index Cond: (id = documents_access.document_id)
Filter: (((access_via_link)::text <> 'none'::text) OR ((documents_access.role)::text = ANY ('{view,edit}'::text[])))
-> Hash (cost=37016.64..37016.64 rows=370664 width=16) (actual time=164.521..164.521 rows=370664 loops=1)
Buckets: 524288 Batches: 1 Memory Usage: 17824kB
Buffers: shared hit=33310
-> Seq Scan on documents_document documents_document__folder_id (cost=0.00..37016.64 rows=370664 width=16) (actual time=0.005..100.491 rows=370664 loops=1)
Buffers: shared hit=33310
SubPlan 4
-> Nested Loop (cost=0.85..357.41 rows=99 width=4) (actual time=0.003..0.003 rows=0 loops=1)
Buffers: shared hit=3
-> Index Scan using documents_access__partner_id_index on documents_access documents_access_1 (cost=0.43..105.55 rows=103 width=9) (actual time=0.002..0.003 rows=0 loops=1)
Index Cond: (partner_id = 7)
Filter: ((expiration_date IS NULL) OR (expiration_date >= '2026-06-18 10:16:38'::timestamp without time zone))
Buffers: shared hit=3
-> Index Scan using documents_document_pkey on documents_document documents_access__document_id_1 (cost=0.42..2.44 rows=1 width=9) (never executed)
Index Cond: (id = documents_access_1.document_id)
Filter: (((access_via_link)::text <> 'none'::text) OR ((documents_access_1.role)::text = ANY ('{view,edit}'::text[])))
Planning:
Buffers: shared hit=69
Planning Time: 1.708 ms
Execution Time: 282.344 ms
```
After as portal user
-----
Only 1x Seq Scan
```
Aggregate (cost=2004948.33..2004948.34 rows=1 width=8) (actual time=116.161..116.165 rows=1 loops=1)
Buffers: shared hit=37942
-> Seq Scan on documents_document (cost=145660.16..2004490.36 rows=183187 width=0) (actual time=24.635..116.155 rows=3 loops=1)
Filter: ((active IS TRUE) AND ((hashed SubPlan 2) OR ((owner_id = 6) AND ((shortcut_document_id IS NULL) OR (shortcut_document_owner_id = 6))) OR (((access_via_link)::text = ANY ('{edit,view}'::text[])) AND (hashed SubPlan 5) AND (is_access_via_link_hidden IS NOT TRUE))))
Rows Removed by Filter: 370661
Buffers: shared hit=37942
SubPlan 2
-> Nested Loop (cost=0.85..357.41 rows=99 width=4) (actual time=0.008..0.009 rows=0 loops=1)
Buffers: shared hit=3
-> Index Scan using documents_access__partner_id_index on documents_access (cost=0.43..105.55 rows=103 width=9) (actual time=0.008..0.008 rows=0 loops=1)
Index Cond: (partner_id = 7)
Filter: ((expiration_date IS NULL) OR (expiration_date >= '2026-06-18 10:15:10'::timestamp without time zone))
Buffers: shared hit=3
-> Index Scan using documents_document_pkey on documents_document documents_access__document_id (cost=0.42..2.44 rows=1 width=9) (never executed)
Index Cond: (id = documents_access.document_id)
Filter: (((access_via_link)::text <> 'none'::text) OR ((documents_access.role)::text = ANY ('{view,edit}'::text[])))
SubPlan 5
-> Index Scan using documents_document__type_index on documents_document documents_document_1 (cost=0.42..145625.73 rows=13772 width=4) (actual time=11.688..11.689 rows=0 loops=1)
Index Cond: ((type)::text = 'folder'::text)
Filter: ((hashed SubPlan 4) OR ((owner_id = 6) AND ((shortcut_document_id IS NULL) OR (shortcut_document_owner_id = 6))))
Rows Removed by Filter: 28198
Buffers: shared hit=4629
SubPlan 4
-> Nested Loop (cost=0.85..357.41 rows=99 width=4) (actual time=0.002..0.002 rows=0 loops=1)
Buffers: shared hit=3
-> Index Scan using documents_access__partner_id_index on documents_access documents_access_1 (cost=0.43..105.55 rows=103 width=9) (actual time=0.001..0.002 rows=0 loops=1)
Index Cond: (partner_id = 7)
Filter: ((expiration_date IS NULL) OR (expiration_date >= '2026-06-18 10:15:10'::timestamp without time zone))
Buffers: shared hit=3
-> Index Scan using documents_document_pkey on documents_document documents_access__document_id_1 (cost=0.42..2.44 rows=1 width=9) (never executed)
Index Cond: (id = documents_access_1.document_id)
Filter: (((access_via_link)::text <> 'none'::text) OR ((documents_access_1.role)::text = ANY ('{view,edit}'::text[])))
Planning:
Buffers: shared hit=56
Planning Time: 1.544 ms
Execution Time: 116.216 ms
```
Before as internal user
--------
```
Aggregate (cost=1902165.43..1902165.44 rows=1 width=8) (actual time=332.919..332.925 rows=1 loops=1)
Buffers: shared hit=69223 read=370
-> Hash Left Join (cost=41649.94..1901908.04 rows=102955 width=0) (actual time=176.179..332.325 rows=10040 loops=1)
Hash Cond: (documents_document.folder_id = documents_document__folder_id.id)
Filter: ((hashed SubPlan 2) OR ((documents_document.owner_id = 1054906) AND ((documents_document.shortcut_document_id IS NULL) OR (documents_document.shortcut_document_owner_id = 1054906))) OR (((documents_document.access_internal)::text = ANY ('{view,edit}'::text[])) AND ((documents_document.company_id = 1) OR (documents_document.company_id IS NULL))) OR (((documents_document.access_via_link)::text = ANY ('{view,edit}'::text[])) AND (documents_document.folder_id IS NOT NULL) AND ((hashed SubPlan 4) OR ((documents_document__folder_id.owner_id = 1054906) AND ((documents_document__folder_id.shortcut_document_id IS NULL) OR (documents_document__folder_id.shortcut_document_owner_id = 1054906))) OR (((documents_document__folder_id.access_internal)::text = ANY ('{view,edit}'::text[])) AND ((documents_document__folder_id.company_id = 1) OR (documents_document__folder_id.company_id IS NULL)))) AND (documents_document.is_access_via_link_hidden IS NOT TRUE)))
Rows Removed by Filter: 27228
Buffers: shared hit=69223 read=370
-> Seq Scan on documents_document (cost=0.00..1859756.86 rows=190950 width=35) (actual time=15.029..155.718 rows=37268 loops=1)
Filter: ((active IS TRUE) AND ((hashed SubPlan 2) OR ((owner_id = 1054906) AND ((shortcut_document_id IS NULL) OR (shortcut_document_owner_id = 1054906))) OR (((access_internal)::text = ANY ('{view,edit}'::text[])) AND ((company_id = 1) OR (company_id IS NULL))) OR (((access_via_link)::text = ANY ('{view,edit}'::text[])) AND (folder_id IS NOT NULL) AND (is_access_via_link_hidden IS NOT TRUE))))
Rows Removed by Filter: 333396
Buffers: shared hit=33931 read=370
SubPlan 2
-> Nested Loop (cost=0.85..357.41 rows=99 width=4) (actual time=0.155..7.920 rows=148 loops=2)
Buffers: shared hit=1612 read=370
-> Index Scan using documents_access__partner_id_index on documents_access (cost=0.43..105.55 rows=103 width=9) (actual time=0.110..3.448 rows=200 loops=2)
Index Cond: (partner_id = 1800102)
Filter: ((expiration_date IS NULL) OR (expiration_date >= '2026-06-18 12:17:29'::timestamp without time zone))
Buffers: shared hit=192 read=190
-> Index Scan using documents_document_pkey on documents_document documents_access__document_id (cost=0.42..2.44 rows=1 width=9) (actual time=0.022..0.022 rows=1 loops=400)
Index Cond: (id = documents_access.document_id)
Filter: (((access_via_link)::text <> 'none'::text) OR ((documents_access.role)::text = ANY ('{view,edit}'::text[])))
Rows Removed by Filter: 0
Buffers: shared hit=1420 read=180
-> Hash (cost=37016.64..37016.64 rows=370664 width=25) (actual time=157.822..157.823 rows=370664 loops=1)
Buckets: 524288 Batches: 1 Memory Usage: 21336kB
Buffers: shared hit=33310
-> Seq Scan on documents_document documents_document__folder_id (cost=0.00..37016.64 rows=370664 width=25) (actual time=0.005..96.730 rows=370664 loops=1)
Buffers: shared hit=33310
SubPlan 4
-> Nested Loop (cost=0.85..357.41 rows=99 width=4) (actual time=0.019..0.372 rows=148 loops=1)
Buffers: shared hit=991
-> Index Scan using documents_access__partner_id_index on documents_access documents_access_1 (cost=0.43..105.55 rows=103 width=9) (actual time=0.005..0.078 rows=200 loops=1)
Index Cond: (partner_id = 1800102)
Filter: ((expiration_date IS NULL) OR (expiration_date >= '2026-06-18 12:17:29'::timestamp without time zone))
Buffers: shared hit=191
-> Index Scan using documents_document_pkey on documents_document documents_access__document_id_1 (cost=0.42..2.44 rows=1 width=9) (actual time=0.001..0.001 rows=1 loops=200)
Index Cond: (id = documents_access_1.document_id)
Filter: (((access_via_link)::text <> 'none'::text) OR ((documents_access_1.role)::text = ANY ('{view,edit}'::text[])))
Rows Removed by Filter: 0
Buffers: shared hit=800
Planning:
Buffers: shared hit=69 read=8
Planning Time: 2.116 ms
Execution Time: 333.013 ms
```
After as internal user
--------
```
Aggregate (cost=2006950.17..2006950.18 rows=1 width=8) (actual time=157.117..157.121 rows=1 loops=1)
Buffers: shared hit=39918
-> Seq Scan on documents_document (cost=145798.74..2006482.26 rows=187165 width=0) (actual time=16.595..156.590 rows=10040 loops=1)
Filter: ((active IS TRUE) AND ((hashed SubPlan 2) OR ((owner_id = 1054906) AND ((shortcut_document_id IS NULL) OR (shortcut_document_owner_id = 1054906))) OR (((access_internal)::text = ANY ('{view,edit}'::text[])) AND ((company_id = 1) OR (company_id IS NULL))) OR (((access_via_link)::text = ANY ('{view,edit}'::text[])) AND (hashed SubPlan 5) AND (is_access_via_link_hidden IS NOT TRUE))))
Rows Removed by Filter: 360624
Buffers: shared hit=39918
SubPlan 2
-> Nested Loop (cost=0.85..357.41 rows=99 width=4) (actual time=0.019..1.016 rows=148 loops=1)
Buffers: shared hit=991
-> Index Scan using documents_access__partner_id_index on documents_access (cost=0.43..105.55 rows=103 width=9) (actual time=0.012..0.262 rows=200 loops=1)
Index Cond: (partner_id = 1800102)
Filter: ((expiration_date IS NULL) OR (expiration_date >= '2026-06-18 12:16:29'::timestamp without time zone))
Buffers: shared hit=191
-> Index Scan using documents_document_pkey on documents_document documents_access__document_id (cost=0.42..2.44 rows=1 width=9) (actual time=0.004..0.004 rows=1 loops=200)
Index Cond: (id = documents_access.document_id)
Filter: (((access_via_link)::text <> 'none'::text) OR ((documents_access.role)::text = ANY ('{view,edit}'::text[])))
Rows Removed by Filter: 0
Buffers: shared hit=800
SubPlan 5
-> Index Scan using documents_document__type_index on documents_document documents_document_1 (cost=0.42..145763.43 rows=14124 width=4) (actual time=0.429..14.916 rows=4625 loops=1)
Index Cond: ((type)::text = 'folder'::text)
Filter: ((hashed SubPlan 4) OR ((owner_id = 1054906) AND ((shortcut_document_id IS NULL) OR (shortcut_document_owner_id = 1054906))) OR (((access_internal)::text = ANY ('{view,edit}'::text[])) AND ((company_id = 1) OR (company_id IS NULL))))
Rows Removed by Filter: 23573
Buffers: shared hit=5617
SubPlan 4
-> Nested Loop (cost=0.85..357.41 rows=99 width=4) (actual time=0.007..0.390 rows=148 loops=1)
Buffers: shared hit=991
-> Index Scan using documents_access__partner_id_index on documents_access documents_access_1 (cost=0.43..105.55 rows=103 width=9) (actual time=0.003..0.074 rows=200 loops=1)
Index Cond: (partner_id = 1800102)
Filter: ((expiration_date IS NULL) OR (expiration_date >= '2026-06-18 12:16:29'::timestamp without time zone))
Buffers: shared hit=191
-> Index Scan using documents_document_pkey on documents_document documents_access__document_id_1 (cost=0.42..2.44 rows=1 width=9) (actual time=0.001..0.001 rows=1 loops=200)
Index Cond: (id = documents_access_1.document_id)
Filter: (((access_via_link)::text <> 'none'::text) OR ((documents_access_1.role)::text = ANY ('{view,edit}'::text[])))
Rows Removed by Filter: 0
Buffers: shared hit=800
Planning:
Buffers: shared hit=56
Planning Time: 1.569 ms
Execution Time: 157.171 ms
```
portal user
before https://explain.dalibo.com/plan/e1e755fg7bb26a21
after https://explain.dalibo.com/plan/hb5fa1d201ff164g
internal user with few documents access
before https://explain.dalibo.com/plan/f753bf2aa244dg63
after https://explain.dalibo.com/plan/538dg5ecb120ch84
internal user with *lots* of documents access
before https://explain.dalibo.com/plan/cf76h84537f7ge4a
after https://explain.dalibo.com/plan/45317a5e3168c5bc
Forward-Port-Of: odoo/enterprise#120991This update resolves an issue impacting how Odoo calculates sick leave payments, specifically related to the 'DPV' (days of paid vacation) calculation for employees with extended absences. The fix ensures accurate assimilation of sickness periods, particularly when transitioning between long and partial absences, improving payroll accuracy and compliance. The changes primarily affect the Be payroll module.
Original PR description
Forward-Port-Of: odoo/enterprise#121086 Forward-Port-Of: odoo/enterprise#120868
This update resolves a critical issue where VoIP registration would fail due to idle sessions, causing error dialogs and preventing users from making calls. The fix ensures that registration requests are properly handled and retried, preventing indefinite waiting and ensuring reliable VoIP connectivity.
Original PR description
Leaving a session open and idle (page open, no activity at all) eventually pops an error dialog: UncaughtPromiseError > RequestPendingError REGISTER request already in progress, waiting for final…
Leaving a session open and idle (page open, no activity at all) eventually pops an error dialog:
UncaughtPromiseError > RequestPendingError
REGISTER request already in progress, waiting for final response
at Registerer.register (sip.js)
at Registerer.register (registerer.js)
at UserAgent.attemptReconnection (user_agent_service.js)
When the WebSocket transport drops while a REGISTER is in flight (which happens on an idle tab: SIP.js sends a periodic re-REGISTER before the registration expires, and the socket may be closed by an idle timeout or by the machine going to sleep in the meantime), the final response never comes back. SIP.js only clears its internal `waiting` flag from the REGISTER response callbacks (onAccept/onReject/onRedirect); it is never reset on transport loss or request timeout. The Registerer is then stuck `waiting` forever, and every subsequent register() rejects with a RequestPendingError.
On top of that, our wrapper's register() did not return the SIP.js promise, and attemptReconnection() called it without awaiting, so the rejection escaped the surrounding try/catch and surfaced as an unhandled promise rejection. Worse, the WebSocket error was resolved right after, so the user appeared reconnected while VoIP registration was actually dead until the page was reloaded.
This commit makes register() recreate the underlying SIP.js Registerer when it is stuck `waiting` (a clean instance starts with waiting=false), and return the promise so callers can await it. attemptReconnection() now awaits it, so any rejection goes through the existing retry/back-off logic instead of bubbling up as an uncaught error.
The recreation is intentionally conditional: disposing a healthy registerer would send an unregister (REGISTER expires=0) racing with the fresh register (expires=600) and could leave us unregistered, so we only recreate when a request is actually stuck.
Forward-Port-Of: odoo/enterprise#120487
Forward-Port-Of: odoo/enterprise#119701This update resolves an issue where changing multiple project names didn't update the associated folder names. The fix ensures that when users edit project names, the linked folder names are automatically updated, streamlining project management. This prevents inconsistencies and ensures data accuracy.
Original PR description
Currently, an error will occur when user multi edits name of projects. Steps to replicate: - Install `documents_project` and open any project's settings using kebab menu (3 dots). - Click new > name…
Currently, an error will occur when user multi edits name of projects. Steps to replicate: - Install `documents_project` and open any project's settings using kebab menu (3 dots). - Click new > name `Test` > open settings page and unselect `Documents` > Save. - Click new > name `Test1` > Save. - From the list view select `Test` and `Test1` and edit their name. Error: ``` ValueError: Expected singleton: project.project(9, 10) ``` Cause: - During `multi-edit`, self contains multiple project records. - When only one of the selected projects has a documents folder (i.e. `use_documents` enabled), `self.documents_folder_id` contains that single folder, making `len(self.documents_folder_id.project_ids) == 1` to be True [1]. - The condition then proceeds to access `self.name` on the `multi-recordset`, raising singleton. Solution: - Avoided accessing `self.name` on a `multi-recordset` during multi-edit. - Filtered projects individually and updated the document folders using the name in vals. [1]: https://github.com/odoo/enterprise/blob/3c2985ca6011700c271ed14e40e08c89be822753/documents_project/models/project_project.py#L101 sentry-7452096418 Forward-Port-Of: odoo/enterprise#120654 Forward-Port-Of: odoo/enterprise#119060
This update resolves an issue where manually added by-products on manufacturing orders caused errors when closing production in the shopfloor view. The fix ensures that serial numbers are correctly handled for by-products created outside of the standard BOM definition, preventing user errors and improving production workflow.
Original PR description
**Issue** Adding a serial-tracked by-product manually on a Manufacturing Order whose BOM does not define it, can lead to inconsistencies when assigning serial numbers in the shopfloor application.…
**Issue** Adding a serial-tracked by-product manually on a Manufacturing Order whose BOM does not define it, can lead to inconsistencies when assigning serial numbers in the shopfloor application. **Steps to reproduce** - Activate by-product in the settings - Create a product with an empty BOM (final product) - Create another product tracked by serial number (by-product) - Create and confirm a MO for the final product with 1 unit of the by-product - Go to Miscellaneaous -> operation Type -> shopfloor - Activate the option "Pre fill lot/serial numbers in shop floor" - Return to the MO and open the shopfloor view - Click on the '+' button next to the by-product and assign a serial number - Try to close the production -> A user error is raised stating that the by-product requires a serial number. **Cause** When the by-product is added manually on the MO, a stock move is created with an initial move line that does not contain any serial number. Later, when assigning a serial number from the shopfloor view: https://github.com/odoo/enterprise/blob/8030b105d3fce1eef9b8965a2bfc37195f71723c/mrp_workorder/models/stock_move.py#L121-L122 a new move line containing the serial number is created: https://github.com/odoo/enterprise/blob/8030b105d3fce1eef9b8965a2bfc37195f71723c/mrp_workorder/models/stock_move.py#L116-L119 However, the original empty move line is not removed (the issue): https://github.com/odoo/enterprise/blob/8030b105d3fce1eef9b8965a2bfc37195f71723c/mrp_workorder/models/stock_move.py#L124-L125 Because `self.picking_type_prefill_shop_floor_lots` is True, but `self.byproduct_id` is an empty recordset since: https://github.com/odoo/odoo/blob/8d14665af5acf1bd391d05a5048dc701986e8b15/addons/mrp/models/mrp_production.py#L1304-L1311 https://github.com/odoo/odoo/blob/8d14665af5acf1bd391d05a5048dc701986e8b15/addons/mrp/models/mrp_production.py#L1279 Indeed, `byproduct_id` is only populated from BOM-defined by-products. As a result, while confirming the production, there is 2 sml and among them, the original one without SN, which triggers the error: https://github.com/odoo/odoo/blob/8d14665af5acf1bd391d05a5048dc701986e8b15/addons/stock/models/stock_move_line.py#L590 https://github.com/odoo/odoo/blob/8d14665af5acf1bd391d05a5048dc701986e8b15/addons/stock/models/stock_move_line.py#L634-L635 https://github.com/odoo/odoo/blob/8d14665af5acf1bd391d05a5048dc701986e8b15/addons/stock/models/stock_move_line.py#L658-L659 https://github.com/odoo/odoo/blob/8d14665af5acf1bd391d05a5048dc701986e8b15/addons/stock/models/stock_move_line.py#L661-L669 opw-6223158 Forward-Port-Of: odoo/enterprise#120493 Forward-Port-Of: odoo/enterprise#118792
This update resolves an issue where the system wasn't properly validating partner banks when creating SEPA direct debit mandates. The change adds a constraint to ensure the correct bank is associated with each mandate, improving data accuracy and preventing potential errors in payment processing. This enhances the reliability of our SEPA direct debit functionality.
Original PR description
Forward-Port-Of: odoo/enterprise#121236 Forward-Port-Of: odoo/enterprise#120901
This update resolves a warning in the Odoo payroll system that was causing incorrect results or access errors. The fix corrected a misconfiguration that was incorrectly targeting employee data instead of the version model. This ensures accurate payroll calculations and reliable system performance.
Original PR description
The warning `hr_payroll_warning_wrong_work_code` was targetting the version model but was returning employee records which led to the wonrg result or access errors
This update cleans up the appearance of payslips by removing unnecessary trailing zeros from the line rate displayed in the salary section. This improves the clarity and professionalism of payroll reports for employees and managers. It's a small but important visual enhancement.
Original PR description
Problem: A lot of trailing zeros were displayed on the rate of each payslip line, in the salary tab of the payslip form. Solution: We simply hid trailing zeros. Task-6310227
This update resolves a bug where tests were failing due to outdated configurations after removing a field. The tests have been updated to correctly utilize the new 'is_live' field, ensuring accurate functionality for rental stock processes. This ensures the system continues to operate reliably.
Original PR description
Some tests were not adapted after removing state field, this caused the failed tests, the tests are now adatped to set up the is_live field instead. Community: https://github.com/odoo/odoo/pull/271052
This update fixes a technical issue in the Belgian payroll localization (l10n_be_hr_payroll) where a field was incorrectly configured to accept monetary values instead of the intended quantity. This change ensures accurate calculation of 'Forced # Months' compensation rules, aligning with Belgian accounting standards.
Original PR description
In Belgium localization salary rule "Forced # Months", the input unit type was monetary when it supposed to be quantity. This commit changes the input type to the correct one (quantity) Task: 6241599
This update fixes a potential issue where changes to the Point of Sale system could disrupt the display of receipt quantities. By using a more flexible method to locate the relevant data, the update ensures the receipt information remains accurate and consistent, regardless of future Point of Sale updates. This enhances the reliability of the receipt generation process.
Original PR description
In this commit - -------------- Use a more generic xpath on the receipt quantity span instead of matching the full class attribute, so the template inheritance does not break when point_of_sale updates the text size class.
This update corrects a previous issue where Odoo was incorrectly selecting unavailable couriers from Shiprocket. The fix now filters out ‘blocked’ couriers, ensuring only serviceable options are considered for rate calculations and shipment selection. Additionally, the system is now more robust in handling potential errors from Shiprocket’s data.
Original PR description
Shiprocket provides an odablock flag in the courier serviceability response. Couriers with odablock=True are not serviceable for the requested route and should not be considered for rate calculation or selection. Before this change, Odoo selected the first courier returned by Shiprocket regardless of its ODA status. As a result, unavailable couriers could be proposed to users and selected for shipments. The fix filters out ODA-blocked couriers before evaluating available services, ensuring that only serviceable couriers are considered. Additionally, freight charge parsing is hardened to gracefully handle non-numeric values returned by Shiprocket, preventing errors during AWB assignment and price computation. FYI: Shiprocket uses odablock=False for serviceable routes and odablock=True for routes that are blocked for a given courier. opw-6288768,6152279 Forward-Port-Of: odoo/enterprise#120374
This update optimizes how the system searches for documents, specifically addressing a slow and complex query when filtering by 'not SHARED'. The change aligns with the production database's approach, resulting in faster and more efficient searches. This improves overall user experience and system responsiveness.
Original PR description
Searching for "not 'SHARED'" results in a very complex query. Our own production DB prefers this implementation, also easier to read. credit: https://github.com/odoo/enterprise/pull/105915#discussion_r2745148099 Task-5893183 Forward-Port-Of: odoo/enterprise#121070 Forward-Port-Of: odoo/enterprise#120870
This update fixes a bug where changes to employee data didn't correctly update past payslips. The fix ensures that all affected payslips are accurately corrected when a user manages them, preventing discrepancies in payroll calculations. This improves data accuracy and payroll processing reliability.
Original PR description
Steps to reproduce: 1. Make sure you have an employee with a contract 2. Create 2 payslips for this employee 3. Change any field of the employee (ex: job position) 4. Go to one of the payslips…
Steps to reproduce: 1. Make sure you have an employee with a contract 2. Create 2 payslips for this employee 3. Change any field of the employee (ex: job position) 4. Go to one of the payslips created before 5. Click on the "Manage Payslips" link appearing because of the change of data Problem: When the data of an employee has been modified and past payslip are affected, the popup currently states 0 payslip has been affected. When clicking the "Correct" button, the correct amount briefly shows before we are sent to the payslip page where only one payslip gets corrected. Source of the problem: - The `employee_id` field was missing from the wizard form view. Since it was not referenced anywhere in the view, the web client did not include it in the initial payload / default_get calls. As a result, the wizard was initialized without `employee_id`, causing the payslip computation to use an empty employee and return a count of 0. - The window action did not explicitly call the intended wizard form view. Odoo therefore selected an unintended inherited view (salary increase wizard) due to view resolution rules (inheritance and priority ordering). This inherited view specifically replaces the description and the correction choice with nothing, which explains why it didn't show before. Fix: - Add an invisible `employee_id` field in the form view to ensure it is included in the initial form payload and properly initialized from context defaults. - Explicitly specify the correct view in the `views` parameter of the window action to prevent fallback to an inherited or unintended view. - Add an explicit priority on the salary increase wizard view to avoid ambiguous view selection in the future. Task-6304311
This update resolves an issue where the 281.XX report generation failed due to missing employee first and last names. The fix adds a check to ensure these fields are populated, preventing errors and ensuring accurate report creation. This improves the reliability of payroll reporting.
Original PR description
Steps to reproduce: 1. Create a Belgian company with a full address, phone and VAT 2. Create an employee of this company with a full private address, valid NISS (or "/"), and give him a contract. 3. Make sure the employee doesn't have a first name or a last name set. 4. Create some payslips for the employee (one is enough). 5. Go to Reporting > 281.XX Sheets and try creating a new report for the corresponding year. Problem: When pressing "Compute", you will see a traceback indicating us that an error occured because of the first name not being set. Fix: Adding a check to make sure that the first and last name are set, and raising an error if it is not the case. Task-6318024
This update resolves a crash that occurred when users viewed real Instagram videos within Odoo. The fix now displays the video link instead of attempting to show the video as an image, preventing the previewer from failing. This ensures a smoother user experience when viewing Instagram content.
Original PR description
Purpose ======= When we have a real on Instagram, we try to show the video as an image. When clicking on the broken image, the previewer crash. To fix that issue, we know show the link of the video in the message. Task-5491124 Forward-Port-Of: odoo/enterprise#121176 Forward-Port-Of: odoo/enterprise#113487
This update strengthens the testing process for the AI call debrief feature in Odoo Enterprise. Previously, the test relied on a quick trigger, which wasn't reliable due to the time it takes for browsers to complete audio seeking. This fix adds a deliberate wait, ensuring the test accurately reflects real-world scenarios and improves the overall stability of the AI call debrief functionality.
Original PR description
Before (in the test), we trigger the media loading by manually dispatching the loadeddata event. Once loadeddata runs, our component updates the <audio> element with the new time and triggers the seek. But because the actual seek in the browser takes time, we cannot just do a simple animationFrame(). We must explicitly wait for the browser to finish seeking and that's what we do in this fix. task-6321435 **community counter-part** https://github.com/odoo/odoo/pull/271300
This update corrects a bug where RCM entries for service imports were missing from the GSTR2B report. The team added a necessary section to the report's domain, ensuring accurate reporting of import service charges as required by Indonesian tax regulations. This ensures compliance and accurate financial reporting.
Original PR description
In commit https://github.com/odoo/odoo/commit/16c0ef65b3b755ddb8256914d8f16ecf48e36409, a new section purchase_imp_services_rcm was introduced, but it was not added to the GSTR2B domain. As a result, RCM entries for import of services were missing from the GSTR2B report. This commit adds the new section to the GSTR2B report line domain.
This update resolves an issue where opening attendance records from the Gantt view was failing. The fix removes a redundant process that caused a system deadlock, ensuring that attendance records can now be opened as expected. This improves the usability of the attendance tracking feature.
Original PR description
Steps To Reproduce: Try to open any existing attendance record from gantt view. It doesn't open the Dialog. Issue: onPillClicked wraps `openDialog` in mutex.exec(), but openDialog() already calls mutex.exec() internally, causing a deadlock — the inner exec waits for the outer to finish, while the outer awaits the inner. Fix: Remove redundant mutex.exec() wrapper in onPillClicked. task-6317228
This update prevents users who aren't designated approvers from directly accepting or rejecting approval requests through the system's activity interface. Previously, this allowed unintended actions, creating a potential security risk. This change ensures that approval workflows remain controlled and secure.
Original PR description
Currently when a user submits an approval request, an activity is created for the approver who can validate or refuse the request directly from the activity, however these options are also visible to other users who will trigger an error if interacting with the options. This commit removes these options for users who are not the approver. **Steps to reproduce:** - Log in as admin - Go to approvals - Select dropdown menu of General Approval and Edit - Change documents to optionnal - Make sure admin is in the approvers list - Log in as demo - Go to approvals -> General Approval -> New Request - Submit the request - You'll see an activity be created for admin, with Accept and Refuse options - If you select any of these options you will get an access error opw-5423528 Forward-Port-Of: odoo/enterprise#120767 Forward-Port-Of: odoo/enterprise#109047
This update improves the security of ESG management by separating ESG user access from accounting data. Previously, ESG users automatically had full accounting access, creating potential risks. Now, ESG users have restricted access, allowing organizations to control data visibility and adhere to confidentiality requirements.
Original PR description
Purpose: In some corporate structures, the ESG manager should not have the rights to access all details in the accounting apps (bank journals, invoices etc), for reasons of confidentiality. The idea is to let the user decide whether the ESG manager should have all rights (then put him as ESG Administrator) or restricted/read only rights (then put him as ESG User). Before this PR, once a user got the ESG user role, they also got the accounting user role, which gave them access to all the accounting features. This was not ideal, as it could lead to security issues. After this PR, the ESG user role no longer inherits from the accounting user role by default. It involves that we make some adjustments to the ESG dashboard and emissions views to ensure that they still work correctly without the accounting user role. task-6127245
This update addresses a technical change within the ChatGPT plugin for Odoo Enterprise. The plugin's formatting functionality has been updated to align with a recent API change, ensuring continued compatibility and proper operation. This ensures the plugin continues to function correctly.
Original PR description
The format plugin's shared removeFormats was renamed to removeSelectionFormats. task-6216233 community: https://github.com/odoo/odoo/pull/266372
This update resolves an issue where recurring plans were incorrectly removed from the website when updating product quantities. The fix ensures that recurring plan selections remain active even after changes to the product's price or variant, improving the subscription experience for users. This was caused by a misinterpretation of the 'allow_one_time_sale' flag.
Original PR description
# Introduction note This PR fixes two bugs introduced by the same commit : https://github.com/odoo/enterprise/commit/106d70a1ef0ddbd61a74b7cac82dfce1e316beaa The original commit fixed multiple issues…
# Introduction note This PR fixes two bugs introduced by the same commit : https://github.com/odoo/enterprise/commit/106d70a1ef0ddbd61a74b7cac82dfce1e316beaa The original commit fixed multiple issues regarding the display of recurring plans when the One-time purchase option was enabled, but it also introduced new ones. Theses new issues are due to multiple new checks on `allow_one_time_sale`, but this variable only indicates that the One-time purchase option is available to the user, not that it is actually selected. So the fixes of the original commit works when first loading the page, but fails when the content of the page is updated. # Shared steps - Activate Subscriptions & eCommerce modules - Create a subscription product, enable 'Accept One-Time' and publish it on the website # Bug 1 ## How to reproduce - Add atleast two recurring plans to the product - Go to the product page on the website - Select one of the recurring plans - Increase the quantity of the product ## The problem The recurring plan selection is removed ## Cause The condition `!combination_info.allow_one_time_sale` was added on the `t-att-checked` of the recurring plan selection display. This correctly fixed the issue when first loading the page, but when the user changes the price or the variant, the recurring plan are recomputed and rerendered : https://github.com/odoo/enterprise/blob/0b408acbadb2cfcbc844521f3244a06b7ae7be22/website_sale_subscription/static/src/interactions/product_page.js#L37-L40 When that is the case, that condition blocks the proper display of the selected recurring plan. ## Proposed Solution When loading the recurring plan selection, what defines wich plan is selected is the `subscription_default_pricing_plan_id` variable, which is based on the `plan_id` value given in the request to the server : https://github.com/odoo/enterprise/blob/0b408acbadb2cfcbc844521f3244a06b7ae7be22/website_sale_subscription/models/product_template.py#L222 We make it so if no `plan_id` is sent to the server and `allow_one_time_sale` is enabled, then the server does not give back any `subscription_default_pricing_plan_id` opw-6131532 # Bug 2 ## How to reproduce - Add an attribute with values A & B for the product - Define atleast two recurring plans for the variant with attribute B - Publish the product - Go to the product page - Select the variant with attribute B ## The problem The recurring plan is not displayed. If the order of the attribute is reversed, then it works as expected. ## Cause The pricings are correcly sent to the front-end but they are not added to selection because of the check on `allow_one_time_sale` : https://github.com/odoo/enterprise/blob/0b408acbadb2cfcbc844521f3244a06b7ae7be22/website_sale_subscription/static/src/interactions/product_page.js#L42-L50 opw-6132160 Forward-Port-Of: odoo/enterprise#120873 Forward-Port-Of: odoo/enterprise#115446
This update resolves a crash that occurred when creating payslips for employees in Belgium with overtime. The issue stemmed from a data structure mismatch during payslip generation, specifically related to overtime calculations. The fix removes a problematic function and corrects the data handling to ensure accurate overtime pay processing.
Original PR description
When creating a payslip for an employee in the Belgian localization with an hourly wage and an attendance-based work entry source contract, a traceback occurs if there is an attendance with overtime.…
When creating a payslip for an employee in the Belgian localization with an hourly wage and an attendance-based work entry source contract, a traceback occurs if there is an attendance with overtime. This happens because the overridden `_preprocess_work_hours_data_split_half` method in `l10n_be_hr_payroll_attendance` attempts to unpack `work_entries` assuming it is a list of triplets, but it is passed as a `defaultdict` with composite keys instead. This data structure mismatch results in a `ValueError: not enough values to unpack (expected 3, got 2)`. Even if updated to handle the `defaultdict` structure, `_preprocess_work_hours_data_split_half` would improperly delete the overtime line hours without adding them back elsewhere (the code responsible for adding them back seems to have been removed). Since this function serves no purpose anymore, we omit the call to it. However, because `saas-19.2` is a stable version Task Id: 6253707 Forward-Port-Of: odoo/enterprise#119177 Forward-Port-Of: odoo/enterprise#118676
This update resolves a problem where validating rental orders for kit products (specifically, products with a component BOM) would trigger an error. The fix ensures that the system correctly handles the explosion of the kit BOM during validation, preventing the 'record does not exist' error. This ensures rental orders involving kit products function as expected.
Original PR description
### Steps to reproduce: - Enable rental transfer - Create a rentable product R - Create and confirm a rental order for 1 unit of R - Create a kit bom for R: 1 x COMP - Validate the delivery of your…
### Steps to reproduce:
- Enable rental transfer
- Create a rentable product R
- Create and confirm a rental order for 1 unit of R
- Create a kit bom for R: 1 x COMP
- Validate the delivery of your unit of R
#### > Missing Error: Record does not exist or has been deleted.
### Cause of the issue:
Confirming your rental order will generate a confirm moves of R. However, since at this point the product was not a kit, these will not be exploded. Now, the issue is that at validation The move will be exploded and deleted in the super call:
https://github.com/odoo/enterprise/blob/7cceddaf086d849b8e2121e1023ef3479397534f/sale_mrp_renting/models/stock_move.py#L10-L13 https://github.com/odoo/odoo/blob/0f2f222a431627a672daf10c86ec2578a27f97bb/addons/mrp/models/stock_move.py#L550-L555 https://github.com/odoo/odoo/blob/0f2f222a431627a672daf10c86ec2578a27f97bb/addons/mrp/models/stock_move.py#L591-L593 However, since the overrides of the sale_{mrp,stock}_renting modules call self rather than the result of the super call, they still expect to work with the original move rather than its exploded result: https://github.com/odoo/enterprise/blob/7cceddaf086d849b8e2121e1023ef3479397534f/sale_mrp_renting/models/stock_move.py#L10-L13 https://github.com/odoo/enterprise/blob/7cceddaf086d849b8e2121e1023ef3479397534f/sale_stock_renting/models/stock_move.py#L61-L65
opw-6191841
Forward-Port-Of: odoo/enterprise#120866
Forward-Port-Of: odoo/enterprise#120051This update fixes an issue where the 'Consolidation' filter wasn't visible on multi-company General Ledger reports. The change ensures the filter appears by defaulting to the report's grouping settings, improving reporting accuracy and providing a more complete view of financial data.
Original PR description
The Consolidation filter doesn't appear on the General Ledger, even in multi-company. Since `user_groupby` can also be defined on the `account.report`, this commit adapts the logic for `show_consolidation`, to fallback to the report's groupby. no-task Forward-Port-Of: odoo/enterprise#120533
This update streamlines the process for submitting account returns by focusing validation and submission locking on the final stage. Previously, returns required a validation step before submission; now, the system automatically locks returns for submission once validation is complete, ensuring data accuracy and a more efficient workflow.
Original PR description
task-6167737
This update corrects inconsistencies between Odoo module manifests and actual file usage. The changes ensure that all dynamically loaded files are properly listed, and outdated files have been removed, resolving warnings flagged by the Odoo linter. This improves the stability and maintainability of the core Odoo Enterprise modules.
Original PR description
Since that they are already loaded from python scripts
This update corrects a technical issue preventing the 'Invoicing & Banks' user group from accessing the necessary data within the account reports module. The missing access control record was corrected, ensuring proper functionality for this user group. This resolves a potential restriction on reporting capabilities.
Original PR description
The `ir.model.access` giving read access to model `account.return` for group "Invoicing & Banks" hasn't been migrated properly.
This update fixes an issue where preparation times weren't accurately calculated when order stages changed and where reports incorrectly included data from all companies. The changes ensure preparation times are correctly updated and that reports now only display data for the active company, leading to more reliable order time tracking.
Original PR description
Issues: - Preparation time for order lines was not computed when the preparation order stage changed. - Preparation time report aggregated orders across all companies instead of showing records for the active company only. Fixes: - Ensure preparation time is properly recomputed when the order stage changes. - Add company domain filtering to the preparation time report. Task-6250974 Forward-Port-Of: odoo/enterprise#118738
This update fixes an issue where custom fields linked to employee or applicant records weren't automatically updating after a signature was completed. The fix ensures that these fields are correctly synchronized, allowing for accurate record-keeping. This improves the reliability of the signature process.
Original PR description
Version - saas-19.3 Steps to Reproduce: 1. Create a custom Sign field (e.g. "Passport No") with "Update Field" enabled, linked to Employee (For Applicant, same any custom field). 2. Employee flow:…
Version - saas-19.3 Steps to Reproduce: 1. Create a custom Sign field (e.g. "Passport No") with "Update Field" enabled, linked to Employee (For Applicant, same any custom field). 2. Employee flow: Employee app -> open an employee -> gear icon -> Signature Request -> send. Applicant flow: Recruitment app -> Applicants list view -> select an applicant -> Actions -> Signature Request -> send. 3. Complete the signature. 4. Check the linked record's "Passport No" field -> field is not updated. Issue: The field linked to the employee/applicant record is not updated after signing. Cause: Both the `hr.contract.sign.document.wizard` and `hr.recruitment.sign.document.wizard` create the `sign.request` in `validate_signature()` without setting `reference_doc`. `_get_auto_field_target_record()` relies on `reference_doc` to resolve the record to sync auto fields against. With `reference_doc` empty, it returns `None`, so `sign.request._sync_auto_field_value()` skips the item before it ever reaches the write step. Solution: Set `reference_doc` to the corresponding `hr.employee`/`hr.applicant` record when building the `sign.request` values in `validate_signature()`, so auto fields linked to those models can resolve their target record and sync back normally after signing. taskid-6308532 Forward-Port-Of: odoo/enterprise#121152
This update corrects an issue where task buffers weren't being applied correctly during rescheduling, particularly with multiple dependencies. The fix ensures that buffers are calculated and respected accurately, preventing tasks from overlapping and maintaining project timelines. This improves the reliability of the project scheduling feature.
Original PR description
Steps to reproduce: ---------------------------------------- - Have the company calendar work from 9 to 17 on weekdays - In project gantt view, create tasks with dependencies like this: ``` - [Task…
Steps to reproduce:
----------------------------------------
- Have the company calendar work from 9 to 17 on weekdays
- In project gantt view, create tasks with dependencies like this:
```
- [Task 1] (June 08, 09:00 AM - 10:00 AM) ──┐
├─> [Task 3] (June 11, 09:00 AM - 10:00 AM)
- [Task 2] (June 10, 09:00 AM - 10:00 AM) ──┘
```
- Make sure the "Auto-Reschedule (Keep Buffer)" is selected
- Then reschedule task 1 to `(June 09, 09:00 AM - 10:00 AM)`
- Task 3 is rescheduled to `(June 15, 09:00 AM - 10:00 AM)`
**Expected behavior:**
To respect the buffers, task 3 should have been rescheduled to `(June 12, 09:00 AM - 10:00 AM)`:
- The buffer from task 1 is 23 working hours after `June 09, 10:00 AM` is `June 12, 09:00 AM`
- The buffer from task 2 doesn't affect the rescheduling.
Cause:
----------------------------------------
From `_web_gantt_update_next_candidates_dates()` we call `_get_new_dates()` with `seconds_between_tasks` being the duration of working hours between the end of task 1 and the start of task 3. We also call it with `first_possible_start_date_per_candidate` being the end of task 3 also depends on task 2.
Then `_get_new_dates()` counts the working hours from `first_possible_start_date_per_candidate` until it reaches `seconds_between_tasks`. Which means the buffer between task 1 and task 3 is actually applied between task 2 and task 3.
Solution:
----------------------------------------
The value contained in `first_possible_start_date_per_candidate` is irrelevant, the correct value to give to `_get_new_dates()` would be `compute_end_date` as we want to keep the buffer to be calculated from this datetime.
So we create a fake dictionary with only the task and `compute_end_date` and give it to `_get_new_dates()`.
The same logic applies when moving tasks backwards.
opw-5973597
Forward-Port-Of: odoo/enterprise#119161This update significantly speeds up appointment scheduling, particularly when managing a large number of resources (like tables in a restaurant). The change optimizes how available resources are checked, reducing processing time by up to 70% in scenarios with many linked resources. This results in a faster and more responsive user experience.
Original PR description
In the current code, for each slot, and for each "available" resource, we check if the resource is available on the slot, based on availability values. Then, we check the remaining capacity of that…
In the current code, for each slot, and for each "available" resource, we check if the resource is available on the slot, based on availability values. Then, we check the remaining capacity of that resource. Also, linked resources information is added when computing the original resource remaining capacity. If many linked resources exist, this will be done several times and is not useful. This commit makes that loop disappear. We now check all resources at once in terms of availability, and linked resources that could be selected (in the appointment resources, in the slot resources (if any restricted resource)) at the same time. Then, the total capacity is the sum of the resource remaining capacity and the ones of available linked resources. Therefore, _slot_availability_is_resource_available is renamed to _slot_available_resources, as it now takes more than one resource and returns all resources among 'resources' that are valid on the slot, based on the availability_values, slot restrictions and booking lines. A noticeable difference is mainly seen when using many resources (and linked resources). For instance, a restaurant with a lot of small tables will have their slot availability check much shorter. BENCHMARK, LOCAL (time only, as number of requests does not change) Only appointment installed For a restaurant with - 10 tables of 2 - 5 tables of 2 linked, 2 times - 10 tables of 4 - 2 table of 2 - time then auto assign On loading /appointment/id: ~ 3.1s -> ~ 1.6s On selecting any number of people (1 to 10): [2s, 2.5s] -> [0.6s, 0.8s] Task-4144524 Forward-Port-Of: odoo/enterprise#121212 Forward-Port-Of: odoo/enterprise#107711