Thursday, August 27, 2026
36 changes · 19.0
Enhancements to existing features
This update adds dedicated UAE overtime work entry types for weekday, night, and overtime day work. It helps payroll teams calculate overtime pay more easily and consistently by using clearer overtime categories in salary rules.
Original PR description
Add UAE overtime work entry types (OVTWD, OVTWDN, OVTOD) to simplify overtime salary rule calculations using `worked_days['<code>'].amount`. Task: 6469393 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The UAE payroll module now uses the latest overtime calculation rules from a newer release. This helps payroll teams calculate regular, night, and day-off overtime more consistently in version 19.0.
Original PR description
Backport overtime salary rules (OVTWD, OVTWDN, OVTOD) from 19.4 to 19.0, updating the computation logic to directly use `worked_days['<CODE>'].amount`. Task:6469393
This update makes internal mail tests more resilient when running in parallel test environments. It prevents unusual test data from crashing the test suite, helping maintain smoother validation without changing customer-facing behavior.
Original PR description
If the value needs to be serialized for IPC (cough cough pytest-xdist) and a weirdo sets recordsets as message values, the serialization fails and the test suite crashes. Since this is just subtest identification it shouldn't be too much of an issue. Forward-Port-Of: odoo/odoo#284279 Forward-Port-Of: odoo/odoo#284178
The live chat settings now clarify that automatic chat opening only happens on larger screens. This helps teams avoid confusion when testing on phones or small screens, where visitors must tap the chat button manually.
Original PR description
The 'Open automatically' action only triggers the auto popup on larger screens (`ui.isSmall` is checked in `AutopopupService. allowAutoPopup`). On mobile/small viewports, only the chat button is shown and the visitor must tap it manually. The existing help text does not mention this, which could lead to confusion when the auto popup does not trigger during testing on mobile. Update the field's help text to explicitly state that automatic opening is limited to larger screens. opw-6459279 Forward-Port-Of: odoo/odoo#284785
The UAE localization setup now reuses one shared state mapping instead of rebuilding the same information repeatedly. This reduces small initialization overhead and keeps the implementation simpler, with no expected change to user-facing behavior.
Original PR description
This PR optimizes the `l10n_ae` module by eliminating repetitive code and improving performance during module initialization. The UAE state mapping used by `_get_ae_res_company` and…
This PR optimizes the `l10n_ae` module by eliminating repetitive code and improving performance during module initialization. The UAE state mapping used by `_get_ae_res_company` and `_get_ae_account_fiscal_position` was previously defined separately inside each method. As a result, the same dictionary was recreated on every function call, introducing unnecessary code duplication and runtime overhead. This PR extracts the mapping into a reusable module-level `_AE_STATE_MAPPING` constant. The template methods now reference this shared mapping and dynamically construct the corresponding XML IDs, avoiding repeated dictionary allocation and simplifying the implementation. ### Cause In `_get_ae_res_company` and `_get_ae_account_fiscal_position`, the UAE state mapping dictionary was recreated every time the methods were executed. Since these methods can be called repeatedly during chart of accounts template evaluation, this resulted in unnecessary allocations and duplicated code. ### Fix * Extract the UAE state mapping into a module-level `_AE_STATE_MAPPING` constant. * Reuse the shared mapping across template methods. * Dynamically construct the required XML IDs from the mapping. * Remove duplicated dictionary definitions from individual methods. * Reduce unnecessary object allocation during template evaluation. ### Benchmark The state mapping evaluation was benchmarked over 1,000,000 iterations: | Version | Execution Time | | ------- | -------------: | | Before | 0.0841s | | After | 0.0272s | This results in approximately **68% faster execution** for the benchmarked operation.
This change reduces unnecessary data loading when Odoo processes very large sets of records. It can lower memory use and improve reliability for heavy operations such as large reports, while keeping the existing behavior for smaller workloads.
Original PR description
This pr is a prototype proposing to not always fetch all fields if we have a lot of records. The idea is that reading all (prefetch) fields **for a few record** makes sense because it will be less…
This pr is a prototype proposing to not always fetch all fields if we have a lot of records.
The idea is that reading all (prefetch) fields **for a few record** makes sense because it will be less expensive to make a slightly bigger request than needed that having to make multiple request to the database (due to the ping)
In some cases, with **very large recordset**, prefetching all fields can be an issue
- memory consumption
- putting data in cache takes some time
In this case, we can improve performances by prefething only the needed fields, but this is fragile, can vary per module installed, ....
The idea behind this pull request is that it _could_ be less expensive to prefetch the field one by one, but for all records
In a minimal database we have 7000 ir.model.data, lets take 5000 of them
```python
env.invalidate_all()
start = env.cr.sql_log_count
for rec in env['ir.model.data'].search([], limit=5000):
rec.model
rec.name
env.cr.sql_log_count - start
```
Before this change, we would have 6 queries and all fields in cache,
-> 1 for the search
-> 5 to prefetch data, 1 per 1000 id
After this change we would have 7 queries and only the model and name field in cache.
-> 1 for the search
-> 2 to prefetch model and name on the first 1000 records
-> 4 to prefetch data of the 4000 remaining records
Note that this strategy could become slower if we need to prefetch a lot of fields, especially if some of those fields are needed in a place where the prefetch set is broken. If we have 1000 fields and read 10 records, we would go from 2 queries to 11 queries. The only additional query of the previous example is there to show the best possible case for a large recordset needing only a few fields.
If we decide to introduce that, it could be an opt-in behaviour (using the context) on some specific reports generation that tends to have memory error.Deleting accounts is made faster by adding supporting database indexes used during dependency checks. This reduces delays for businesses managing or cleaning up their accounting records, especially in larger databases.
Original PR description
Without these indexes, the foreign key check when deleting an account can take a long time.
Deleting accounts in German reports is now more efficient. The change adds database indexes that help the system confirm whether an account is referenced, reducing delays during account cleanup.
Original PR description
Without these indexes, the foreign key check when deleting an account can take a long time.
Resolved issues and error corrections
Fixes an issue where changing a product quantity in the customer portal could overwrite a manually adjusted unit price on a sales quotation. This helps ensure customers see the agreed price and prevents unintended repricing from pricelists.
Original PR description
Steps to reproduce: - Create a Sales Order in the backend - Add section order line and enable 'Set Optional' - Add a product under this section - Manually change the unit price of this product - Save and share quotation to open it from customer portal - In the portal, change the quantity of the product Expected Behavior: The unit price should stay the same Current Behavior: The unit price changes Why this issue occurs: There's currently no check to see if the price was manually changed. This leads to the permanent application of pricelist unit prices and disobeys inline pricing. Bugfix-6485742
Customers can no longer set optional products on a sales order to zero or negative quantities through the portal. This prevents invalid order lines and keeps quantity adjustments that may reduce an order under salesperson control.
Original PR description
Since the fusion of `sale.order.option` model into `sale.order.line` model, the optional products (editable from portal) lines are not deleted when reaching a quantity of 0 or below. This could allow some customers to set negative quantities, which makes no sense as it's only something that should be set by the salesman if necessary. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fixes a compatibility issue that prevented documents from being signed when running Odoo on Python 3.13 or newer. The PDF handling layer now supports the expected page box fields, allowing the signing flow to complete normally.
Original PR description
On Python 3.13 and above, `requirements.txt` installs `pypdf` instead of `PyPDF2`: ``` PyPDF2==2.12.1 ; python_version > '3.10' and python_version < '3.13' # (Noble and below) PyPDF==5.4.0 ;…
On Python 3.13 and above, `requirements.txt` installs `pypdf` instead of `PyPDF2`:
```
PyPDF2==2.12.1 ; python_version > '3.10' and python_version < '3.13' # (Noble and below)
PyPDF==5.4.0 ; python_version >= '3.13' # (Trixie)
```
so `odoo.tools.pdf` resolves to the `_pypdf` backend. That backend back-fills the PyPDF2 1.x camelCase API onto pypdf so callers do not have to branch on the installed library, but the alias set is incomplete in two ways:
- `cropBox` is missing entirely, while `mediaBox` is aliased.
- `mediaBox` is a getter-only property, so assigning to it raises.
## Impact
`sign/models/sign_document.py` needs all three forms, and dies on the first:
```python
box = page.cropBox if page.get('/CropBox') else page.mediaBox # 436
...
overlay_page.mediaBox = page.mediaBox # 444
if page.get("/CropBox"):
overlay_page.cropBox = page.cropBox # 446
```
```
File ".../sign/models/sign_document.py", line 436, in render_document_with_items
box = page.cropBox if page.get('/CropBox') else page.mediaBox
AttributeError: 'PageObject' object has no attribute 'cropBox'
```
`render_document_with_items` is reached from `sign_request_item.sign` → `_post_fill_request_item` → `_send_completed_documents` → `_generate_completed_documents`, so **signing any document fails on Python 3.13+**.
Fixing the shim rather than the caller keeps the design the module already follows: `_pypdf2_2.py` does the same normalisation for the 2.x backend, and `sign` is written against the camelCase API on purpose.
## Why CI does not catch it
The test that covers exactly this branch, `sign/tests/test_sign_request.py::test_origin_offset_translation`, patches the backend by module path:
```python
with patch('PyPDF2._page.PageObject.add_transformation', create=True) as mock_add, \
patch('PyPDF2._page.PageObject.cropBox', new_callable=PropertyMock, return_value=offset_box):
```
On 3.13+ `PyPDF2` is not installed at all, so the target never resolves and the branch is never exercised. A follow-up on the enterprise side should patch through the backend `odoo.tools.pdf` resolved instead of naming `PyPDF2`; that change is what would keep this from regressing.
## Verification
Reproduced and fixed on Python 3.14.4 with pypdf 5.4.0, running the exact `sign_document.py` sequence:
```
L436 read cropBox/mediaBox -> (0.0, 0.0)
L444/L446 writes -> OK
round-trip cropbox: (0.0, 0.0)
```
Before the patch, line 436 raises the `AttributeError` above.
## Forward-port
Not needed. On `master` the monkeypatch block is gone and the remaining shims carry `@deprecated("PyPDF2 1.x compatibility shims are deprecated, switch to modern API")`, so callers there are expected to move to the modern API rather than lean on these aliases. This is a 19.0-only stable-branch fix.This fixes an accounting issue where undoing a payment reconciliation could place the reversing cash basis tax entry in today’s period instead of the original tax period. Tax reports now correctly cancel the original and reversing amounts in the same period, reducing reporting discrepancies.
Original PR description
When unreconciling a payment from an invoice with a cash basis tax, the tax cash basis (CABA) entry is reversed. The reversal is supposed to land in the same period as the origin entry so the tax…
When unreconciling a payment from an invoice with a cash basis tax, the tax cash basis (CABA) entry is reversed. The reversal is supposed to land in the same period as the origin entry so the tax report nets to zero for that period. Steps to reproduce: - Enable cash basis and create a cash basis tax (exigibility on payment) - Post an invoice dated in the past with that tax - Reconcile a bank statement line to the invoice - Resequence the cash basis entry so the month is dropped from the name (CABA/08/2026/0001 -> CABA/2026/0001) - Unreconcile the statement line Issue: The reversal CABA entry created on the last unreconcile is dated today instead of the origin entry's month. In the tax report the original tax amount stays in the statement's month while the reversal amount appears in the current month, so the two no longer cancel out. Analysis: While under a monthly journal sequence a past date returns the last day of that month, under a yearly sequence a past date within the current year returns the latter between the move date and today, moving the reversal out of the origin period. opw-6301553 Forward-Port-Of: odoo/odoo#284408 Forward-Port-Of: odoo/odoo#281250
This fix stops users from deleting a card's cover image in a way that leaves the page editor in an inconsistent state. It helps prevent confusing editor behavior and avoids an error that could occur when using the Cover Image options.
Original PR description
It was possible to remove the image inside a card cover while keeping the figure wrapper. The card option would then still consider that there was a cover image even though the image was gone, which could also lead to a traceback. Steps to reproduce: - Insert the `s_three_columns` snippet - Click on the image of one card - Either press "Enter", "Delete", "Backspace" - Hover the "Cover Image" options => The image is removed but the `<figure>` is still there, so the option is still considered active (leading to a traceback) task-6081728 Forward-Port-Of: odoo/odoo#280086
This fixes a display issue in the website builder where resize controls could be hidden behind the sidebar when editing animated page elements. Users can now clearly see and resize selected content, making page editing more reliable.
Original PR description
Steps to reproduce: - Drop a few snippets to make the page scrollable - At the bottom, drop the `s_three_columns` snippet - Click on the last Card - Add an animation "onScroll" (Effect - Slide, Intensity - 100) - Scroll top slightly to hide a part of the card behind the sidebar => The resize overlay is partially hidden The elements `.hb-row` have a z-index of 2, so they appear in front of the overlay which has a z-index of 1. It was decided to fully show the overlay to allow resizing. Keeping the overlay visible in front of the sidebar also allow the user to see where animated element is. task-6476269 Forward-Port-Of: odoo/odoo#282796
Invoice printing no longer fails when an Argentine company partner has a VAT number that is valid in another country but not as an Argentine CUIT. This prevents an error screen and allows users to continue printing invoices reliably.
Original PR description
When printing an invoice, a traceback will occur if the company's partner has an invalid CUIT. Steps to reproduce the error: - Install ``l10n_ar_edi`` module with demo data - Switch to ``(AR)…
When printing an invoice, a traceback will occur if the company's partner has an invalid CUIT. Steps to reproduce the error: - Install ``l10n_ar_edi`` module with demo data - Switch to ``(AR) Exento`` Company - Go to Invoicing > Configuration > Journals > Open ``Ventas Preimpreso`` journal > ARCA POS System: ``Electronic Invoice - Web Service`` > Save - Create a new invoice with ``ADHOC SA`` partner > Confirm the invoice - Open the ``(AR) Exento`` partner and set the VAT to ``BE0477472701`` - Open the Invoice > print Traceback: ```py ValueError: invalid literal for int() with base 10: 'BE0477472701' ``` After this [commit], companies outside the EU can use European VAT numbers. Consequently, an Argentine partner can have a CUIT number such as ``BE0477472701``, which is valid as a Belgian VAT number but not as a CUIT. When printing the invoice, the ``l10n_ar_vat`` field is computed from the partner's VAT and its value is passed to ``int()``, causing a traceback at the following line: https://github.com/odoo/enterprise/blob/d55486866d09f8aa87c2003dab722cfa323068b4/l10n_ar_edi/models/account_move.py#L138 [commit]: https://github.com/odoo/odoo/commit/a2afe3292e1cd0a4f339dc47707e469653d13ea0 Enterprise PR: https://github.com/odoo/enterprise/pull/127820 sentry-7666042143 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282454
This fix ensures costs linked to projects are correctly matched to the related sales order for reinvoicing, even when projects share the same analytic account or a bill line uses multiple analytic accounts. This helps prevent missed reinvoicing opportunities and keeps customer billing more accurate.
Original PR description
### Before this fix --- The `_get_so_mapping_from_project()` method returns a mapping where the key is the move line ID and the value is a `sale.order` record (or `None`). Because of the issues…
### Before this fix
---
The `_get_so_mapping_from_project()` method returns a mapping where the key is
the move line ID and the value is a `sale.order` record (or `None`).
Because of the issues described below, a valid `sale.order` could be available
for reinvoicing, but the corresponding move line might still not be mapped to
that sale order. As a result, the move line is not added to the reinvoiceable
sale order.
However, the implementation has two issues:
#### 1. Projects are overwritten when they share the same analytic account
`project_per_accounts` is built as a dictionary mapping an analytic account ID
to a single project. If multiple projects reference the same analytic account,
each new assignment replaces the previous one. As a result, only the last
project associated with a given analytic account is retained.
**Example:**
* Analytic Account **AA1** is linked to **Project A** and **Project B**.
* The dictionary becomes `{AA1: Project B}`.
* **Project A** is lost, even though it also references **AA1**.
**Steps to reproduce:**
1. Create an analytic account **AA1**.
2. Create **Project A** and **Project B**, both linked to **AA1**.
3. Create **Sale Order SO1** linked only to **Project A**.
4. Create a vendor bill (or expense) that generates an AML using **AA1** for a
product configured with **Reinvoice Costs = At Sales Price**.
5. Validate the document.
**Expected behavior:**
The product should be added to **SO1** for reinvoicing.
**Actual behavior:**
The move line is not mapped to **SO1**, so no sale order line is created.
#### 2. Previously found projects are overwritten during iteration
The `project` variable is reassigned on every iteration of the loop. After the
loop completes, it only contains the project (or lack of one) corresponding to
the last processed analytic account. This can cause valid projects found earlier
in the loop to be discarded.
**Example:**
* Move line has analytic accounts **AA1** and **AA2**.
* **AA1** maps to **Project A**.
* **AA2** has no linked project.
* After the loop, `project` is `None`, even though **Project A** was found.
**Steps to reproduce:**
1. Create analytic accounts **AA1** and **AA2**.
2. Create **Project A** linked to **AA1** only.
3. Create **Sale Order SO1** linked to **Project A**.
4. Create a vendor bill (or expense) whose AML is distributed between **AA1**
and **AA2**, where **AA2** is processed after **AA1**.
5. Validate the document.
**Expected behavior:**
The move line should still be mapped to **SO1** because **AA1** references
**Project A**.
**Actual behavior:**
The last processed analytic account (**AA2**) overwrites the previously found
project, causing the move line not to be linked to **SO1**.
### After this fix
---
* `project_per_accounts` stores **all** projects associated with each analytic
account instead of keeping only the last one.
* The project lookup preserves all valid project candidates instead of
overwriting previously found results during iteration.
* As a result, the method can resolve the related `sale.order` in more cases,
improving the overall accuracy of the mapping.
> **Note:** This change prevents valid project associations from being lost
> when multiple projects share an analytic account or when multiple analytic
> accounts are processed for the same move line.
**OPW:** 6294615
Forward-Port-Of: odoo/odoo#277110Delivery fee lines are now excluded from the Invoiced not Delivered report because they are not physical items to deliver. This prevents accounting teams from seeing incorrect outstanding delivery entries and improves the accuracy of revenue review data.
Original PR description
Issue: --- Delivery lines are included in `invoiced not delivered` report, which is wrong as delivery lines are not deliverable. Steps: 1- Create a SO with a good product and add a delivery line. Set the product line as delivered and create an invoice. 2- Open accounting, and from review tab, open `Invoiced not Delivered`. As you see, delivery lines are included in the report. Fix: --- On stable we could fix it inside `_get_accrual_domain` by checking if `delivery` is installed. On master we need to implement a solution to be able to differentiate the lines that won't be delivered. opw-6360894
The HTML editor now converts tables with merged rows or columns into regular tables when content is inserted or pasted. This prevents broken table behavior and keeps editing features working consistently for users.
Original PR description
Description of the issue this PR addresses: We don't support colspan/rowspan in the editor, so tables containing them can break other functionality that assumes a rectangular grid (equal cell count per row). This PR expand any rowspan/colspan into individual cells on insert. opw-6347233 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283840 Forward-Port-Of: odoo/odoo#281114
This fixes an issue where unsaved translation text could disappear when a user dragged the translation dialog. Users can now reposition the dialog without losing work they have just entered, improving reliability when editing multilingual content.
Original PR description
Step to reproduce: - have atleast two language and install sale - open any product, hover over product, and click on Translation button - Enter a value for one of language - drag the dialog Observation: - we lose the data, we just entered and fallback to original data Cause: - Inputs used `t-att-value="term.value"`, bound to original data. Since this content is passed to Dialog via slot, it is rendered/patched as part of Dialog's render cycle, - Dragging updates Dialog's state, triggering a patch that re-evaluated the slotted template and reset input values (which comes from `term.value`) Fix: - bind value to `updatedTerms[term.id] ?? term.value` so edits survive patches triggered by the parent Dialog opw-6431521 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283514
Fixed an issue where printing an Argentine electronic invoice could fail if the company's tax ID was incorrectly formatted. The change prevents the invoice print process from crashing, helping users continue their invoicing workflow more reliably.
Original PR description
When printing an invoice, a traceback will occur if the company's partner has an invalid CUIT. Steps to reproduce the error: - Install ``l10n_ar_edi`` module with demo data - Switch to ``(AR)…
When printing an invoice, a traceback will occur if the company's partner has an invalid CUIT. Steps to reproduce the error: - Install ``l10n_ar_edi`` module with demo data - Switch to ``(AR) Exento`` Company - Go to Invoicing > Configuration > Journals > Open ``Ventas Preimpreso`` journal > ARCA POS System: ``Electronic Invoice - Web Service`` > Save - Create a new invoice with ``ADHOC SA`` partner > Confirm the invoice - Open the ``(AR) Exento`` partner and set the VAT to ``BE0477472701`` - Open the Invoice > print Traceback: ```py ValueError: invalid literal for int() with base 10: 'BE0477472701' ``` The issue occurs because when the partner's identification type is CUIT, At [1] ``_run_check_identification()`` method does not include partners whose identification type has ``is_vat=True``. As a result, CUIT is not validated by ``_run_check_identification()`` method in ``l10n_ar`` module at [2]. So, partner's ``l10n_ar_vat`` field can be computed as ``BE0477472701``. Passing this value to ``int()`` raises the traceback during invoice printing at below line. https://github.com/odoo/enterprise/blob/d55486866d09f8aa87c2003dab722cfa323068b4/l10n_ar_edi/models/account_move.py#L138 [1]:https://github.com/odoo/odoo/blob/c2a39085ba0fbcf8a0e6a55228191e764499caea/addons/l10n_latam_base/models/res_partner.py#L24-L30 [2]:https://github.com/odoo/odoo/blob/c2a39085ba0fbcf8a0e6a55228191e764499caea/addons/l10n_ar/models/res_partner.py#L55-L65 Community PR: https://github.com/odoo/odoo/pull/282454 sentry-7666042143 Forward-Port-Of: odoo/enterprise#127820
Changing a project’s visibility no longer fails when the project folder contains document shortcuts. This keeps project access updates working as expected while still preventing users from changing shortcut access directly.
Original PR description
Changing a project's visibility fails when its documents folder contains a shortcut. The visibility change is never applied and the following error is raised: "You can not update the access of a…
Changing a project's visibility fails when its documents folder contains a shortcut. The visibility change is never applied and the following error is raised: "You can not update the access of a shortcut, update its target instead." ### Reproduction steps - Create a project and add a document to its folder. - Create another document outside the project's folder. - Create a shortcut to that document in the project's folder. - Change the project's visibility. ### Cause Changing a project's visibility updates the access rights of its folder and documents together. The shortcut access check is meant to reject operations performed only on shortcuts. However, reading `shortcut_document_id` on a recordset returns the shortcut targets found across that recordset. Therefore, the presence of a single shortcut makes the check reject the whole operation. This prevents regular documents and the project folder from having their access updated. ### Fix Only reject access updates when all records involved are shortcuts. This preserves the protection against changing shortcut access directly while allowing project access updates to include shortcuts alongside regular documents and folders. opw-6472637 Forward-Port-Of: odoo/enterprise#128756
Swedish ISO20022 batch payments now generate files that truly match the selected pain.001.001.09 format. This prevents compliant payment files from being rejected by banks when companies use the Swedish outgoing payment method.
Original PR description
**Steps to reproduce:** - Install Accounting and l10n_se - Switch to a Swedish company (e.g. SE Company) - In Accounting settings, set "Identification" with anything - In Bank journal: * Set an…
**Steps to reproduce:** - Install Accounting and l10n_se - Switch to a Swedish company (e.g. SE Company) - In Accounting settings, set "Identification" with anything - In Bank journal: * Set an account number * Make sure "Swedish ISO20022" is available in "Outgoing Payments" * Set "pain.001.001.09" as "XML Format" in "Outgoing Payments" - Create a vendor payment: * Vendor: [a vendor with a trusted bank account] * Payment Method: Swedish ISO20022 * Amount: [any] - Confirm the payment - From the payments list, select the payment and create a batch - Validate the batch payment **Issue:** When the batch is validated, a `pain.001.001.09` file should be generated. However, its content is that of a `pain.001.001.03` file, even if the version reported in the file is `pain.001.001.09`. For example, `<ReqdExctnDt>` should contains a subnode `<Dt>` in `001.001.09`, which is not the case. It leads to the file being rejected as non-compliant to `pain.001.001.09`. opw-6472050 Forward-Port-Of: odoo/enterprise#129234 Forward-Port-Of: odoo/enterprise#128598
Projects that contain document shortcuts can now have their visibility changed without triggering an error. This removes a frustrating blocker for teams managing project access, especially when project folders include many shortcuts.
Original PR description
A project with a shortcut cannot change its visibility. STR: - Create a project with and add a document to its folder - Create a document in another folder - Create a shortcut to that document in the project folder - Change the project's visibility Current behavior before PR: An error is raised and the project's visibility doesn't change: > You can not update the access of a shortcut, update its target instead. That blocks the project visibility change unless all the project folder shortcuts are moved away to somewhere else (pretty unconvenient if there are many of them) Desired behavior after PR is merged: The project's visibility changes successfully. https://www.loom.com/share/160cecf57fa440e5a57b09b74cab7c73 cc @moduon MT-15648 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This change restores the previous GIF resizing approach because the newer version caused slowdowns and memory errors when pages displayed multiple GIFs. It helps keep views such as Kanban pages responsive and reduces the risk of crashes or failed image loading.
Original PR description
Revert commit d9fae40571c4f10c17fe00efc087cb25b30b85ab as it's slow on odoo.com and the call to `frame.copy()` is raising a MemoryError when loading a KanbanView with multiple gifs. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284479
This update fixes an internal automated test for mail mentions so it waits for the right suggestion list instead of being interrupted by unrelated status updates. It helps keep Odoo's validation pipeline stable without changing the experience for end users.
Original PR description
Before this commit, the test "select @ mention from the suggestion list being filtered" could fail on runbot, on the check that follows the first "@": Failed to find 2 of ".o-mail-Composer-suggestion" (Timeout of 10 seconds). Found 0 instead. This happens because the test holds a render open on ImStatus, a component the member list renders as well as the composer. The composer tells the server that the user is typing, the bus sends the status back, and the member list re-renders its ImStatus with another class. The hold catches that render, the one that also brings the suggestions on screen. This commit gives the children of NavigableList an inNavigableList environment flag, and holds the render only on an ImStatus that has it. https://runbot.odoo.com/odoo/error/946282 Forward-Port-Of: odoo/odoo#284484
Sending or replying to chatter messages on tax returns will no longer incorrectly change their status to Paid. Payment finalization is now limited to the intended tax payment instructions flow, reducing the risk of inaccurate tax return tracking.
Original PR description
Before this fix: Replying to or sending a message from the chatter of a tax return could incorrectly change its state to Paid. This happened because action_send_mail() automatically called _action_finalize_payment() for account.return records. After this fix: Payment finalization only happens when the composer is opened from the tax payment instructions flow. Normal chatter messages and replies will no longer change the tax return state to Paid. task-6469536
Turkish e-invoices are now checked before sending to Nilvera to catch missing taxes on invoice lines, helping users avoid rejected submissions. The update also improves product-related warnings so note and section lines are not incorrectly flagged.
Original PR description
Nilvera does not accept invoices with lines that do not have taxes, so we added a valiation check for sending the invoice to warn the user. Additionally, we raise a warning when a line does not have a product and has an empty CTSP. However, if the line is a note or section, this warning should not be triggered. task-6404409 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
Deleting a project no longer incorrectly moves folders from archived projects to the trash. This protects documents linked to archived projects so teams do not lose access to files that still belong to existing projects.
Original PR description
Deleting a project also sends the folders of every archived project to the trash. ### Steps to reproduce - Install `documents_project`, where each project has its own Documents folder linked through…
Deleting a project also sends the folders of every archived project to the trash.
### Steps to reproduce
- Install `documents_project`, where each project has its own Documents folder linked through `project.project.documents_folder_id`.
- Create `Project 1`, `Project 2`, and `Project 3`, then archive the first two.
- Delete `Project 3`.
- The folders of `Project 1` and `Project 2` are moved to the trash with their contents, although both projects still exist and still reference them.
### Cause
`_archive_folder_on_projects_unlinked` only archives folders that are no longer used by any project. This was checked through a `documents.document` domain on `project_ids`.
The domain mixed two conditions on the same relation:
- `('project_ids', '!=', False)` checks that a folder has users,
- `('project_ids', 'not any', [('id', 'not in', self.ids)])` checks that it has no users outside the projects being deleted.
Those conditions are not evaluated the same way by the ORM. The first one keeps archived projects visible by disabling `active_test` internally, while the second one searches `project.project` normally and hides archived projects.
An archived project can therefore be counted as a folder user by one condition and ignored by the other, causing its folder to be archived.
### Fix
Check remaining users directly on `project.project` with `active_test=False`, so archived projects are included. Since only folders of deleted projects can become unused, the search starts from those folders instead of scanning all Documents.
opw-6442976
Forward-Port-Of: odoo/enterprise#127217This fixes a payroll attendance issue where early or batch-created attendance records could accidentally lose their link to the correct work entry. The cleanup now only reviews relevant overlapping entries, helping keep attendance and payroll data accurate.
Original PR description
When an early attendance starts on the previous UTC day, the cleanup uses full UTC days as boundaries. This can include an unrelated work entry and remove its attendance link. Use the generated work entries as cleanup boundaries so only entries that can overlap the new entries are considered. opw-6412221 Forward-Port-Of: odoo/enterprise#129059 Forward-Port-Of: odoo/enterprise#127040
Invoice sequence gap warnings now check each suffix-based sequence separately. This prevents invoices from being incorrectly highlighted as having numbering gaps when similarly numbered invoices with different suffixes exist, improving confidence in accounting sequence checks.
Original PR description
### Issue: When moves share the same `sequence_number` in a journal but have different suffixes (e.g. `INV/2026/00010` and `INV/2026/00010A`), the `made_sequence_gap` flag was incorrectly set ###…
### Issue: When moves share the same `sequence_number` in a journal but have different suffixes (e.g. `INV/2026/00010` and `INV/2026/00010A`), the `made_sequence_gap` flag was incorrectly set ### Cause: `_update_sequence_made_gap`, introduced in commit https://github.com/odoo/odoo/commit/17893089e8b21c0ecab5e61ed8e2c33f3731b3ac selects the previous and next moves ordered by `sequence_number` without filtering by suffix This causes two issues: - Moves from different suffix sequences are used as neighbors, leading to incorrect gap detection - Duplicate `sequence_number` values across suffixes are not accounted for, so only one move is considered per number ### Steps to reproduce: - Install `account` - Post 12 invoices to get a sequence up to `INV/2026/00012` - Reset `INV/2026/00012` to draft, rename it to `INV/2026/00010A` - Reset `INV/2026/00010A` to draft, rename it to `INV/2026/00009A` and confirm Before the fix: `INV/2026/00011` is red Expected: `INV/2026/00011` should not be red because `INV/2026/00010` exists - Delete `INV/2026/00009` Before the fix: `INV/2026/00010` is red Expected: `INV/2026/00010` should be red (gap in no-suffix sequence) - Reset `INV/2026/00009A` to draft and confirm it again Before the fix: `INV/2026/00010` is not red Expected: `INV/2026/00010` should still be red (different suffix) ### Notes: Suffix changes are treated as distinct sequences following the same gap rules as any other sequence This was agreed with R&D — the gap flag is meant to signal inconsistencies within a sequence, not across suffixes opw-6454823
This fix prevents an error when users create or edit partner-related tasks and change the project field. It keeps the workflow stable for teams using Timesheets, Sales, Projects, and Studio customizations.
Original PR description
Steps to reproduce: -------------------------------------- 1. Install `sale_timesheet` and studio modules 2. Go to any partner record: * Add a new page from the studio * In the created page add…
Steps to reproduce:
--------------------------------------
1. Install `sale_timesheet` and studio modules
2. Go to any partner record:
* Add a new page from the studio
* In the created page add `task_ids` field and close studio
3. Go to Created Page and click on Add a line for new task
4. In the dialog, change the Project field.
Observation:
--------------------------------------
Traceback occurs
```
TypeError: unhashable type: 'list' in _compute_last_sol_of_customer (sale_timesheet/models/project_task.py)
```
Issue:
--------------------------------------
When you're on a form view and change a field (like `project_id`), the web client sends an onchange RPC to the server. The server doesn't work with the real database records during this process, instead, it creates virtual (in-memory) snapshots of the form data using `new()`. These virtual records always have `NewIds`.
Then in `_get_last_sol_of_customer_domain()`
https://github.com/odoo/odoo/blob/ea58c5b1c3efb609799fba8fb0782af0d7a65bb8/addons/sale_timesheet/models/project_task.py#L105
And `DomainCondition.checked()` converts NewId values to `[]`, resulting domain as `('order_partner_id', 'in', [])` https://github.com/odoo/odoo/blob/ea58c5b1c3efb609799fba8fb0782af0d7a65bb8/odoo/orm/domains.py#L833-L836
The `[]` is a Python list, which is unhashable. When `tuple()` is called on the Domain, `DomainCondition.__iter__` yields the leaf as `(field, operator, value)` preserving the list value. The resulting outer tuple contains a list element, making the entire tuple unhashable and crashing
Using the Domain object directly as a dict key is also not viable, because `DomainCondition.__hash__` calls `hash(self.value)`, which likewise fails on list values.
Solution:
--------------------------------------
The fix uses `repr(domain)` as the cache key. `repr()` always returns a hashable string and produces consistent output for identical domains, preserving the deduplication behavior. The Domain object itself is still passed to `search()` unchanged.
opw-6297102This fixes an issue where some grouped data queries could return duplicated or incomplete results when the same grouping was requested more than once. The change prevents crashes in callers that rely on the expected result format and improves reliability of aggregated business data.
Original PR description
`_read_grouping_sets` dispatches each SQL result row to the grouping set(s) that requested it, using the `GROUPING()` bitmask as the key. When a grouping set repeats a groupby spec, e.g. `['foo',…
`_read_grouping_sets` dispatches each SQL result row to the grouping set(s) that requested it, using the `GROUPING()` bitmask as the key.
When a grouping set repeats a groupby spec, e.g. `['foo', 'foo']`, it computes the exact same bitmask as the grouping set for the deduplicated column alone (`['foo']`).
Two grouping sets sharing a bitmask were treated as interchangeable duplicates: only the first one seen got its extractor registered, and its already-extracted result list was later blindly copied onto every other grouping set sharing that bitmask.
This is wrong whenever those sets don't actually share the same shape:
`_read_grouping_sets(grouping_sets=[['foo'], ['foo', 'foo']])`
returned
`[('my_foo_val', <aggregate>), ('my_foo_val', <aggregate>)]`
instead of
`[('my_foo_val', <aggregate>), ('my_foo_val', 'my_foo_val', <aggregate>)]` (repeating the value, as `_read_group` does).
Callers unpacking crashed with `ValueError: not enough values to unpack`.
Fix:
- Deduplicate the SQL terms of each grouping set before building its `GROUPING SETS (...)`, so that grouping sets which are physically the same always produce a single row from PostgreSQL.
- Register every grouping set's own extractor under its bitmask in a list, instead of keeping only the first one seen, and dispatch each result row to all of them.
task-6511626
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prThis update replaces an older loop style with the preferred clearer wording in core server-related code. It helps automated quality checks pass and keeps the codebase consistent, with no expected impact on users or business workflows.
Original PR description
Ruff checks on runbot flagged `while 1:` Preferred syntax is to use `while True` [UP048](https://docs.astral.sh/ruff/rules/while-one) runbot-945983 Forward-Port-Of: odoo/odoo#284490 Forward-Port-Of: odoo/odoo#283962
A small configuration error in Accounting report protections was fixed. The change ensures two important invoice-related reports are correctly protected from accidental deletion, helping preserve expected system behavior.
Original PR description
On `ir.actions.report` we want to block the unlinking of specific reports in odoo. However, when the list was created a comma was missed between `action_account_original_vendor_bill` and `account_invoice_without_payment` which means we were actually protecting against people unlinking `action_account_original_vendor_billaccount_invoice_without_payment`. Adding in that comma will allow these two records to be properly protected. task-none Forward-Port-Of: odoo/odoo#283323
The Brazilian AvaTax sales module now includes the dependency it needs to install reliably in special installation scenarios. This prevents setup failures caused by missing sales tax fields, helping businesses using Brazilian localization avoid blocked deployments.
Original PR description
When installing l10n_br_avatax_sale with --skip-auto-install, you'll get an error about the l10n_br fields listed in views/sale_order_views.xml, because these fields don't fully exist without sale_external_tax. This happens because they're defined on a mixin, which is an abstract model. Abstract models only add their fields to a model that actually lists them in `_inherit`. sale.order should list this mixin, but currently doesn't. Adding that dependency is an unstable fix, so it will be added in master (20.0, or 20.1) runbot-237866 Forward-Port-Of: odoo/enterprise#128142
This fix prevents duplicate emails from being sent when employees submit expenses across multiple companies. It helps keep expense communication cleaner and avoids confusion for approvers and employees.
Original PR description
Fix a small issue resulting in mail duplication when submitting expenses from multiple companies that appeared in the infamous 704a5a19 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283013