Daily updates from Odoo
Wednesday, July 22, 2026
24 changes · master
New functionality added to Odoo
Adds support for Uruguayan electronic e-Resguardos so businesses can issue, submit, store, and print compliant withholding documents directly in Odoo. The feature integrates with the existing Uruware electronic invoicing flow and includes standard withholding setup, automatic calculations, regulatory threshold handling, and document validation.
Original PR description
This PR introduces support for electronic e-Resguardos for Uruguay, fully compliant with DGI regulations and integrated into the existing electronic invoicing flow via Uruware. The implementation is…
This PR introduces support for electronic e-Resguardos for Uruguay, fully compliant with DGI regulations and integrated into the existing electronic invoicing flow via Uruware. The implementation is based on the existing withholding framework from Ecuador, with references to Argentina’s approach, and includes the necessary adaptations to meet Uruguayan legal and functional requirements. Key features included in this PR: - Out-of-the-box availability: once the module is installed, users can create e-Resguardos without additional activation steps. - Base configuration for withholdings: - Common withholding taxes (VAT and IRPF). - New field for DGI Withholding Code on taxes. - Configurable destination and default withholding accounts. - Two main functional flows: - Creation of e-Resguardos from a single vendor bill. - Creation of e-Resguardos with multiple or no invoice references, including proper reference handling in XML depending on payment context. - Automatic calculations of withheld amounts based on selected tax and base. - XML generation and submission through the existing Uruware connection, including reception of validated XML (CAE) and PDF with electronic stamp. - Compliance with the 10,000 UI threshold, enabling users to generate e-Resguardos when required by regulation. - Storage and visualization of XML and PDF directly in Odoo, with printing support. - Comprehensive testing coverage: single and multi-invoice cases, without reference, threshold logic, XML validation, and PDF rendering. This PR lays the foundation for future extensions to additional e-Resguardo types while delivering a complete and compliant solution for the most common withholding scenarios in Uruguay. Task: 4557347
Enhancements to existing features
This draft change appears to update Argentinian localization withholding support, likely improving how businesses handle local tax withholding requirements. Because no changed files or commit details were provided, the exact user impact cannot be determined from the available information.
Payroll batch processing has been optimized so companies can generate and calculate large payroll runs much more quickly. In benchmark testing, a run of 1,000 payslips dropped from about 1 minute 36 seconds to 8.42 seconds, reducing waiting time for payroll teams during busy periods.
Original PR description
## Description This PR improves payroll batch performance around payrun and payslip computation. More details are available on each commit. ## Benchmark Individual benchmarks and reproduction are available on each commit. The following is an aggregate timing for this PR: Running a payroll run for 1,000 payslips went from 1m36s to 8.42s, **11.4x** faster. ## Reference task-6395469
Payroll salary computation lines now show routine values in a muted style instead of hiding them. This keeps payslip screens easier to scan while still allowing users to edit quantities, rates, or amounts when needed.
Original PR description
In this previous PR (https://github.com/odoo/enterprise/pull/109925) we made obvious values in the salary computation (quantity = 1, rate = 100, amount = total) invisible to decrease the visual clutter. However, this way we are no longer able to modify them since they don't show up. To allow for modifying while maintaning the distinction between important values and obvious ones, we make them muted instead of invisible. Task: 6324418
The timesheet assistant now offers clearer placeholder text, more useful default filters, and rounded billable and non-billable percentages. This makes the timesheet experience easier to read and navigate for users managing time entries.
Original PR description
This PR improves a few UX elements in the timesheets assistant, namely placeholders, default filters, and rounding (non-)billable percentages to the nearest integer. Task-6218571
Embedded views in Knowledge now show the move handle in a more natural position during drag-and-drop interactions. This makes arranging embedded content easier and reduces visual misalignment while editing pages.
Original PR description
This commit adjusts the drag handle position for embedded views to keep the move widget visually aligned with the embedded content during drag interactions. Task-5951196
Printed payslips now include an employee’s departure date when applicable. This gives payroll teams and employees clearer documentation, especially for final payslips and Belgian termination fee reports.
Original PR description
task-6227661
Dominican companies can now generate the DGII Format 606 purchase report directly from the Journal Audit report. This helps prepare monthly vendor bill and refund data for submission to the DGII portal, reducing manual work and improving compliance support.
Original PR description
Dominican companies must report their monthly purchases to the DGII in the Format 606 layout, pasted into the DGII pre-validator and uploaded to the DGII portal. Add a 'Generate 606 DGII' button on the Journal Audit report for DO companies, exporting one row per posted vendor bill/refund of the period: - partner RNC/Cédula and identification type (foreign suppliers are not reported) - NCF, and the NCF of the reversed bill for refunds - goods/services amounts split per invoice line product type, lines without a product counting as services - ITBIS/ISR amounts summed per tax grid, ISC and tip per tax group - payment date from the last reconciled counterpart - purchase type, payment type and ISR withholding type from the new l10n_do fields task-6118976
Resolved issues and error corrections
Fixes Peruvian electronic invoices so detraction withholding amounts are converted and shown in Peruvian soles even when the company currency is USD. This prevents incorrect XML submissions to SUNAT and wrong currency symbols on invoice PDFs.
Original PR description
**Steps to reproduce:** * Install the **l10n_pe_edi** module. * Change the company currency to **USD**. * Configure **Peruvian detraction** settings: * Set a **withholding percentage** on a product.…
**Steps to reproduce:**
* Install the **l10n_pe_edi** module.
* Change the company currency to **USD**.
* Configure **Peruvian detraction** settings:
* Set a **withholding percentage** on a product.
* Set a **withholding code** on the same product.
* Create a customer invoice in **USD**.
* Set the **Operation Type** to **[1001] Operation Subject to Detraction**.
* Add the configured product to the invoice.
* Confirm the invoice, **send it to SUNAT** by *process now**, and generate the ZIP which has XML.
**Observed behavior:**
* The generated XML contains: `<cbc:Amount currencyID="PEN">116.82</cbc:Amount>` The `currencyID` is `PEN` but the amount is the raw USD value instead of being converted to PEN using the exchange rate.
* The PDF report also shows the withholding amount with the wrong currency symbol.
**Cause:**
* `_l10n_pe_edi_get_spot()` computed `spot['amount']` as `amount_total_signed * percent`, where `amount_total_signed` is stored in the **company currency**. When the company currency is PEN this works correctly, but when it is changed to USD the value is in USD while the XML node hardcodes `currencyID="PEN"`.
* `spot['currency']` was also set to `company_id.currency_id`, so the PDF rendered the amount with the wrong currency symbol.
* In `_add_invoice_payment_terms_nodes`, `spot_amount` was selected via `spot['amount'] if invoice.currency_id == invoice.company_id.currency_id` which relied on company currency being PEN as a proxy for the invoice being in PEN, breaking when company currency is USD.
**Fix:**
* In `_l10n_pe_edi_get_spot()`, compute `spot['amount']` by explicitly converting `amount_total` from the invoice currency to PEN using `currency_id._convert(..., env.ref('base.PEN'), ...)`, ensuring the detraction amount is always in PEN regardless of the company currency.
* Set `spot['currency']` to `env.ref('base.PEN')` so the PDF report always displays the withholding amount with the correct PEN symbol.
* In `_add_invoice_payment_terms_nodes`, always use `spot['spot_amount']` (invoice currency amount) for the `FormaPago`/`Cuota` arithmetic, since `spot['amount']` is now exclusively the PEN amount for the detraction XML node.
opw-6327571
Forward-Port-Of: odoo/enterprise#125063
Forward-Port-Of: odoo/enterprise#123782Appraisals now select the template assigned to an employee's department instead of keeping a generic default. This ensures department-specific appraisal processes are applied consistently while preserving valid user choices.
Original PR description
**Problem:** When a template is restricted to a department, appraisals for employees of that department do not use it — a generic (global) template is used instead. **Steps to reproduce:** 1. Create…
**Problem:** When a template is restricted to a department, appraisals for employees of that department do not use it — a generic (global) template is used instead. **Steps to reproduce:** 1. Create an appraisal template and restrict it to a department. 2. Make sure at least one global (no department) template also exists. 3. Open a new appraisal and select an employee of that department. 4. Observe the appraisal template stays on the global one. **Current behavior:** The global template that was auto-selected before an employee was chosen is kept, ignoring the department-restricted template. **Expected behavior:** The template restricted to the employee's department is selected. **Cause of the issue:** On a new appraisal the template is computed before an employee (and therefore a department) is set, so a global template is pre-selected. When the employee is then chosen, `_compute_appraisal_template` reruns but the `appraisal.appraisal_template_id or ...` clause short-circuits on the already set global template and never falls through to the department's template. **Fix:** Evaluating the department's template before the existing value ensures a department-restricted template takes precedence over a template that was only auto-selected as a default. The existing value is still preserved when the department has no template of its own, so deliberate choices are untouched. opw-6293936 Forward-Port-Of: odoo/enterprise#121377
This fixes a timing issue where financial reports could briefly show an older selected view while a newer selection was still loading. Users now see report results that match their latest choice, reducing confusion when switching report groupings or filters quickly.
Original PR description
Previously, when a report was loading if a variant was selected, it would display the first one when it loaded and display the second one when it loaded. With this, we wont show the first one as we…
Previously, when a report was loading if a variant was selected, it would display the first one when it loaded and display the second one when it loaded. With this, we wont show the first one as we are waiting for the new one. To reproduce: - load the Demo data on the demo company - Add time.sleep(5) in _get_lines - load the Generic Tax report and wait for it to load - click on the Group by: Account > Tax and wait for 3s - click on the Group by: Tax > Account - Watch the Account > Tax load and still being displayed for 3s while the Tax > Account variant is loading. The loading of the report contains 2 steps: - Loading the options of the report - Loading the content of the report itself The options are used to update the bar at the top of the screen with the filters, such as date or horizontal_split. Since some of those filters dont need to call again for the options, we will store them in a map loadingOptionsFunctions, for them to be called when the options have been fetched. If they are clicked when the data of the report is loading, we can directly updated the options since they dont need to update the value of the lines. Some need to update the lines like rounding_unit, however since the call to format_column_values_from_client is faster than querying again the line, We check if the currency changed while loading the report and if it did, we format the column values. Following those changes, a lot of async/await were rendered useless since we are not waiting for the click of the filter before the user can click on another one. We are able to remove them and when changing the value of an option with _updateOption or updateOption, ... they will return the promise of the reloading of the report which can be awaited when necessary.
This fix prevents pay runs from failing when an employee has multiple contract or employment versions within the same month. It ensures payroll processing can continue reliably through the payslip step for Belgian payroll users.
Original PR description
Step to reproduce: 1. Create Employee with multiple version in 1 month 2. Create New PayRun during that month 3. Run the PayRun until Payslip step 4. Expected error on payslip steps reason: substraction of work100_wds and worked_day generate more than 1 value, if we have multiple version in 1 month task-6296276
A broken automated checkout test for Mexican point-of-sale invoicing was corrected by adding the missing tax selection step. This helps keep invoice and refund flows reliable during future updates, with no expected change for everyday users.
Original PR description
In this commit: =============== - Fix the failing tour `test_mx_pos_invoice_order_and_refund` due to a missing step for Tax Selection. Error-941398
Opening the manufacturing work order planning Gantt view now works reliably. The change restores required planning data so users can view scheduled work orders without encountering an error.
Original PR description
Issue Before This Commit: ============================= The workorder planning Gantt view only declared some fields inside the popover element. As a result, they were not fetched with the Gantt…
Issue Before This Commit: ============================= The workorder planning Gantt view only declared some fields inside the popover element. As a result, they were not fetched with the Gantt records, even though Gantt decorations also used them. Steps to Reproduce: =================== - Install the **mrp_workorder** module. - Enable the **Work Orders** option in **Manufacturing → Configuration → Settings**. - Create and plan a MO with at least one work order. - Open the work order planning view from the **Planning** menu. - Observe the following error: An `EvalError` is raised: `Name 'has_conflicts' is not defined.` Cause of the Issue: ============================= In this [PR](https://github.com/odoo/enterprise/pull/123693), the variables were moved inside the popover, which caused the Gantt view to lose access to them. After This Commit: ============================= These fields have been restored as top-level Gantt fields, ensuring they are loaded with the record data and accessible wherever they are needed.
This fix prevents Point of Sale payment flows from getting stuck when real-time device communication fails or when kiosk payments run without a signed-in user. It improves reliability for card payment confirmation and kiosk checkout scenarios.
Original PR description
A change from odoo/enterprise#116705 made the `iot_http_service` try to access the `self_ordering_mode` on the session`, leading to longpolling failure on non-self order as data would be undefined. Also, a mistake in error catching in the longpolling service made it so that an event request thrown would abort directly with the previous one, instead of only the previous one. If websocket was unavailable at the same time, it would result on the PoS getting stuck waiting for confirmation of the payment. In addition, we fixed Six payments in Kiosk, failing because it tryed to access `user.id` where user was `undefined`. task-6391166 Forward-Port-Of: odoo/enterprise#124782
The WhatsApp identifiers translation file is now properly registered in the translation management configuration. This helps ensure related text can be picked up for translation workflows, reducing the risk of missing translations for users.
Original PR description
pot files must be registered there. See 9966b160972a053e051f2213846acc64d133f2a3 Forward-Port-Of: odoo/enterprise#124076 Forward-Port-Of: odoo/enterprise#124026
The Documents app now keeps the Actions menu working when users select several documents at once. This removes a frustrating blocker where the menu button could appear but not open, while preserving accurate selected-document counts after uploads.
Original PR description
***Issue:*** Since https://github.com/odoo/enterprise/commit/8baaad621a1555680cd77ab514f626137b5b584d the div holding the selection box and the Actions menu carries t-key="targetRecords.length" in…
***Issue:*** Since https://github.com/odoo/enterprise/commit/8baaad621a1555680cd77ab514f626137b5b584d the div holding the selection box and the Actions menu carries t-key="targetRecords.length" in the documents list and kanban views, so every change of the selection count destroys and recreates everything inside it. Ticking a document checkbox updates both the selection and the focused record, and with these two updates the recreation goes wrong: the new ActionMenus component ends up destroyed while its button is still in the page. The dropdown click listeners are removed with the component, so clicking Actions does nothing until the selection changes again. That t-key was added because the selection box shows a stale count after a file upload. The real problem is that SelectionBox caches this.root once in setup. The upload flow reloads the model, which replaces the root record list, and the box keeps counting the selection of the old one. ***Fix:*** Remove the t-key from documents_list_controller.xml and documents_kanban_controller.xml and make DocumentsSelectionBox read the current props.root instead of the value cached by the parent setup. The count follows the new record list after an upload without remounting anything, and the Actions menu is not recreated so its dropdown keeps working. The upload scenario stays covered by the existing multi_view.test.js test, a new test checks that the Actions menu opens with two selected documents. ***Steps to reproduce:*** 1. Open the Documents app 2. Switch to the list view 3. Tick the checkboxes of two documents 4. Click the Actions button next to the "2 selected" box => the Actions dropdown does not open Ticket [link](https://www.odoo.com/odoo/project.task/6365618) opw-6365618 Forward-Port-Of: odoo/enterprise#124238 Forward-Port-Of: odoo/enterprise#123575
Creating Super Accounts in Australian Payroll could fail due to inconsistent internal handling of employee contribution proportions. This fix aligns that handling so payroll administrators can create and view Super Accounts without encountering an error.
Original PR description
Version - 19.0 Steps to reproduce(runbot): - Install `l10n_au_hr_payroll` - Go to Payroll -> Configuration -> Super Accounts - Create a Super Account by filling all the required fields - You will…
Version - 19.0
Steps to reproduce(runbot):
- Install `l10n_au_hr_payroll`
- Go to Payroll -> Configuration -> Super Accounts
- Create a Super Account by filling all the required fields
- You will encounter the issue
**TraceBack:**
```
('hr.menu_hr_employee_payroll', 485, 'Employees > Employees', 756):
Traceback (most recent call last):
File "/tmp/tmpao31_hxw/migrations/base/tests/test_mock_crawl.py", line 346, in crawl_menu
self.mock_action(action_vals)
File "/tmp/tmpao31_hxw/migrations/base/tests/test_mock_crawl.py", line 377, in mock_action
return self.mock_act_window(action)
File "/tmp/tmpao31_hxw/migrations/base/tests/test_mock_crawl.py", line 537, in mock_act_window
mock_method(model, view, fields_list, domain, group_by)
File "/tmp/tmpao31_hxw/migrations/base/tests/test_mock_crawl.py", line 570, in mock_view_form
[data] = record.read(fields_list)
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 3490, in read
return self._read_format(fnames=fields, load=load)
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 3747, in _read_format
vals[name] = convert(record[name], record, use_display_name)
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 6688, in __getitem__
return self._fields[key].__get__(self)
File "/home/odoo/src/odoo/19.0/odoo/orm/fields.py", line 1744, in __get__
self.compute_value(recs)
File "/home/odoo/src/odoo/19.0/odoo/orm/fields.py", line 1915, in compute_value
records._compute_field_value(self)
File "/home/odoo/src/odoo/19.0/addons/mail/models/mail_thread.py", line 484, in _compute_field_value
return super()._compute_field_value(field)
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 4954, in _compute_field_value
determine(field.compute, self)
File "/home/odoo/src/odoo/19.0/odoo/orm/fields.py", line 81, in determine
return needle(*args)
File "/home/odoo/src/enterprise/19.0/l10n_au_hr_payroll/models/hr_employee.py", line 118, in _compute_proportion_warnings
proportions[emp.id] * 100,
KeyError: 4
```
Issue:
During the refactoring from `read_group()` to `_read_group()`, the `proportions` dictionary became keyed by `hr.employee` records instead of employee IDs. While the condition was updated to use `proportions.get(emp)`, the warning message still accessed the dictionary using `proportions[emp.id]`, resulting in a `KeyError`.
Fix:
Use the employee record consistently when accessing the `proportions` dictionary after switching to `_read_group()`.
Task ID - 6390373
Refrence:
https://github.com/odoo/enterprise/pull/72217/changes#diff-8ae3564d54e47eec919ef273d44f6276d8c6df60e21c5c8c8f7292b1c38cf541R460
Forward-Port-Of: odoo/enterprise#124478This fix ensures the subscription commission test correctly clears currency rate records across companies when demo data is present. It prevents false test failures and helps keep commission calculations reliable in multi-company setups.
Original PR description
Steps to reproduce: 1- Initialize a new database with demo data 2- Run the test `test_sub_commission_no_currency_rate` Issue: `AssertionError: 0 != 10 : Regular invoice, 10 percent of 100` Why this happens: The test used to delete all rows in the res_currency_rate table for the current company only. When we load the database with demo data, the query in `_get_subscription_currency_rates` would find entries for the other companies and wouldn't resort to the default. Later when joining, it would find no rates for the current company and the test fails. runbot-243440 Forward-Port-Of: odoo/enterprise#123971 Forward-Port-Of: odoo/enterprise#116197
This fix prevents users from creating or editing WhatsApp templates from the event communication flow, where choosing the wrong template type could make the event message inaccessible after saving. It keeps other communication types unchanged while respecting WhatsApp template approval constraints.
Original PR description
Issue: User goes to Event.event Form -> communication tab -> add line Select whatsapp -> type something -> create and edit -> create new template with any model event.registration -> save ( all the way including the event form) reload page -> whatsapp event.mail displays "User does not have access to this record". Fix: add a patch to the js get m2oProps to deactivate edit, and create on the prop. WA Templates need more limitations for creation and edits due to the approval process from META's external API. We cannot block the full creation and edit in the xml becuase other event.mail types rely on that flow to create their types like social posts. This was a fix implemented due to needing to revert the oginal fix odoo/odoo@f58e9f97192ac0ff8d159377494d6c68f871d20b opw-6334168 Forward-Port-Of: odoo/enterprise#124156 Forward-Port-Of: odoo/enterprise#121852
Email links for appointments now use the website tied to the appointment setup instead of falling back to whichever website last updated the system base URL. This prevents customers in multi-website environments from being sent to the wrong site when managing their appointment.
Original PR description
In Multiwebsite settings, when the public user interactions needs email generation (appointment or event flow), the email links are generated with a base url that does not corresponds to the one from…
In Multiwebsite settings, when the public user interactions needs email generation (appointment or event flow), the email links are generated with a base url that does not corresponds to the one from which the request started. Case 1: - Have website A and website B - Create an appointment page website A - Log in via website B - As public user, make an appointment in Website A - Check the generated email Issue: button links in the email will redirect to the wrong website, so users will encounter an issue when managing the appointment. This occurs because when an user log in, the system parameter 'web.base_url' is updated with the current url. This parameter is then used as fallback when we need to retrieve the base url without an active record Case 2: - Have website A and website B - Create an event and assign it to website B - As public user, access the event and register to it - Check the generated email Issue: button links in the email will redirect to the wrong website, so users will encounter an issue when managing the event. This occurs because the record `event.registration` has no website_id field and the base url is taken from the company default website (website A) Backport with improvements of 15bae202d8f1b5bf70bbc63b2d89025e9237e6cf opw-4146760 opw-4336369 Forward-Port-Of: odoo/enterprise#124714 Forward-Port-Of: odoo/enterprise#122669
Code cleanup and technical improvements
Spreadsheet autofill behavior was adapted to align with recent internal spreadsheet engine changes. This keeps list and pivot autofill working consistently while reducing unnecessary formula processing, with no expected user-facing workflow changes.
Original PR description
The o-spreadsheet commit transformed the autofill plugin into a store. This made the necessary adaptations in odoo. Moslty, the list/pivot autofill plugin were changed into helpers that can be called from the autofill process (they had no state nor command handling). Also changed a bit the autofill helpers so we don't re-compile the cell formula when we already have a compiled formula. Task: 6395361
This update streamlines how several Odoo Enterprise features access shared internal services. It should make the codebase easier to maintain and reduce future development risk, with little to no visible change for end users.
Original PR description
This commit reduces number of `env.services` occurence by replacing them by `useService` or `plugin`. Some handlers/providers are now scoped to be able to call these hooks and `useService` is now also scope dependent instead of component dependent.
The VoIP softphone search bar was updated to use newer internal interface tools, keeping the user experience the same while improving maintainability. Existing checks confirm the search field still focuses correctly when reopening the softphone or switching tabs.
Original PR description
Replaces two `useLayoutEffect` calls in `SearchBar` with OWL3 native APIs (`useEffect`, `computed`, `signal`) The useLayoutEffect refactored in this PR had test coverage — below are some tests that failed when the effect was commented out, and are now passing: - @voip/softphone/softphone/Search bar is focused after reopen the softphone. - @voip/softphone/softphone/Search bar is focused after switching to a tab with search bar. see commented-out runbot build: https://runbot.odoo.com/runbot/batch/2622059/build/116410158