Daily updates from Odoo
Friday, July 31, 2026
55 changes · saas-19.2
Enhancements to existing features
The bank reconciliation setup now handles long payment references much more efficiently when creating automatic reconciliation rules. This prevents memory errors and reduces delays for customers processing bank statement lines with lengthy transaction descriptions.
Original PR description
When setting an account to an account.bank.statement there is a step to automatically create a reconciliation rule/model if one does not exist already. To do so, we retrieve 5…
When setting an account to an account.bank.statement there is a step to automatically create a reconciliation rule/model if one does not exist already. To do so, we retrieve 5 account.bank.statement.lines and use them to define the reconciliation model config. A matching is done on the payment_ref of the account.bank.statement.lines by finding the longest common substring in the reference. ### Current Implementation The current algorithm does so by first generating all the possible substrings for all the payment_ref before doing the intersection between these sets and returning the max if `len(substring) >=10`. This is reasonable when the payment_ref follows either a SEPA communication national standard like the Belgian one or the Creditor Reference standard (ISO 11649). For transactions with large, unstructured communication with more than 100 chars, the method `_get_common_substrings` quickly overfill the memory, sometimes raising a MemoryErorr, and takes a significant amount of time. That's because the nested function `_generate_all_substrings` generates n*(n+1)/2 substrings, with n being the lenght of a payment_ref, called `label` in `generate_all_substrings`. ### Proposed Fix This commit introduces another algorithm to find the largest common substring. It starts by taking the two smallest labels to find their substrings intersection. We know that for an arbitrary collection of labels, the intersection of their substrings sets A ∩ B ∩...∩ Z is included in the intersection of any two substrings sets. The underlying assumption of the first step is that for an arbitrary collection of labels the intersection of the substrings sets of the two smallest labels will be the smallest intersection of any given pair of substrings sets. This won't hold true everytime and using a metric such as label similarity instead of shortest string might be better. But on average this should be good enough and it's easier to implement + it removes the need of preprocessing the labels to compute the similarity. The point of the new nested function `common_substrings` is to discard common substrings as we build them. Using the current `generate_all_substsrings` on either the smallest label or both smallest labels would still generate and store a lot of substrings, especially for large labels. By yielding the common substrings as we find them, the memory footprint is vastly reduced. Lastly, the next substring in the common_substrings iterable is only checked against the remaining labels if it's longer than the current match. This speeds up the whole process ### speedup In a customer database with some account.bank.statement.line with payment_ref > 500 chars, setting a specific account (code 4970) on transactions goes from MemoryError to < 1Mb memory consumption. Because of the memory consumption it was not possible to gather timing value on the current version. Testing the new algorithm in a shell and using as labels the 5 longest payment_ref in the customer database (831, 831, 1117, 1178, 1300 chars), averaging to 2000 chars once normalised, the average time to execute `_get_common_substrings` is 900 ms ± 10.3 ms. Forward-Port-Of: odoo/enterprise#118824
If a list item has a selection blocker, the user can be stuck and forced to move to the previous or next block instead of being able to continue to edit the list item. To get around this, this commit allows selection placeholders in list items. task-6394918 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278323
Original PR description
If a list item has a selection blocker, the user can be stuck and forced to move to the previous or next block instead of being able to continue to edit the list item. To get around this, this commit allows selection placeholders in list items. task-6394918 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278323
Previously, questions in the event template could not be reordered via drag-and-drop on the Questions page. Instead of adding the handle to the inline list embedded in the event.type form view, we add it to the dedicated event.question list view (event_question_view_list). This will allow the users to reorder from the event questions and the sequence will be synced everywhere those questions appear. Steps to reproduce: 1.Go to event, configurations, and event questions. 2.We can't drag an
Original PR description
Previously, questions in the event template could not be reordered via drag-and-drop on the Questions page. Instead of adding the handle to the inline list embedded in the event.type form view, we add it to the dedicated event.question list view (event_question_view_list). This will allow the users to reorder from the event questions and the sequence will be synced everywhere those questions appear. Steps to reproduce: 1.Go to event, configurations, and event questions. 2.We can't drag and drop questions Original PR (18.0): odoo/odoo#270576 opw-6260478 Forward-Port-Of: odoo/odoo#277071
Resolved issues and error corrections
The accounting reports now handle cases where a tax that was previously configured as a group is later changed to another tax type. This prevents the Journal Report from failing and helps users continue reviewing audit reports without interruption.
Original PR description
**Steps to reproduce:** - Install account_reports - Create a tax * Tax Computation: Group of Taxes * Definition: [Add a tax] - Create an invoice with that tax - Confirm the invoice - Edit the tax by changing "Tax Computation" to "Percentage" - Go to "Accounting / Reporting / Audit Reports / Journal Report" **Issue:** A KeyError is raised. **Cause:** While generating the data, a group of taxes is found in the journal items. When trying to retrieve its info from the dict listing the groups of taxes, its ID is not found but the system assumes that it's present. opw-6377465 Forward-Port-Of: odoo/enterprise#125291
Australian payroll no longer shows an error if an employee's Tax Treatment Category is temporarily removed. The system now safely leaves the related tax treatment code empty until the required category is set again, helping payroll users continue editing employee records without interruption.
Original PR description
Currently, an error occurs when a user removes the Tax Treatment Category of an Australian employee. Steps to Reproduce: - Install the `l10n_au_hr_payroll` module with demo data. - Switch to an…
Currently, an error occurs when a user removes the Tax Treatment Category of an Australian employee. Steps to Reproduce: - Install the `l10n_au_hr_payroll` module with demo data. - Switch to an `Australian company`. - Open any `Employee` > `Payroll` > remove the `Tax Treatment Category` value. `UnboundLocalError: cannot access local variable 'code' where it is not associated with a value` After the [change] in selection field behavior, users can clear the value of the field. When the user removes the Tax Treatment Category value, the system computes the tax treatment code [1]. During this process, if no condition matches, the code variable is not initialized. Converting this uninitialized variable to a string [2] raises an error. This commit ensures that when the tax treatment category is not set, the tax treatment code is set to False with an early return. Since the tax treatment category is required field and compute the correct tax treatment code, once the category is set. [change]: https://github.com/odoo/odoo/pull/214422/changes/8d2a42ac419fdf7943a0c11beb8c5de6c6f85bef [1]- https://github.com/odoo/enterprise/blob/017743cbe97b629e9d9f1895b655014d4c3abbcb/l10n_au_hr_payroll/models/hr_version.py#L450-L451 [2]- https://github.com/odoo/enterprise/blob/017743cbe97b629e9d9f1895b655014d4c3abbcb/l10n_au_hr_payroll/models/hr_version.py#L515 No task ID Forward-Port-Of: odoo/enterprise#124067
Brazilian electronic invoices now send the required trade unit conversion factor to Avalara. This helps ensure fiscal reform tax calculations use the correct quantity basis and reduces the risk of invoice processing errors.
Original PR description
This commit adds the comexTaxUnitFactor to the json sent to Avalara when sending an invoice. comexTaxUnitFactor is a factor that convert sales quantity to comexTaxUnit, its value should be the same as cbsIbsUnitFactor. opw-6396462 Forward-Port-Of: odoo/enterprise#125695
This fixes how Mexican electronic payment documents calculate related invoice balances when foreign currency exchange differences are involved. Payments and credit notes are now applied in the right order, helping ensure XML amounts match fully settled invoices and reducing compliance or reconciliation confusion.
Original PR description
### Issue: Attributes of DoctoRelacionado node in the XML display an incorrect value because the exchange rate difference entry distorts the calculation, even though the invoice was fully settled.…
### Issue: Attributes of DoctoRelacionado node in the XML display an incorrect value because the exchange rate difference entry distorts the calculation, even though the invoice was fully settled. https://drive.google.com/file/d/1ntQny0o8bkkfYtY5ZNBK0Yq7Rq0I-yfz/view ### Fix: Sorting partials by "not exchange_move_id" first broke the chronological order whenever the invoice/payment partial itself carried an exchange difference (e.g. a foreign currency payment settled at another rate). This made the residual-chain algorithm consume the credit note's "other_residual" on the wrong payment, so ImpSaldoAnt/ImpPagado/ ImpSaldoInsoluto in the payment CFDI's DoctoRelacionado stayed wrong even though the invoice was fully paid. Populate the exchange move mapping in a separate first pass and sort the partials purely by date/id, so credit notes are always deducted from the correct payment. task-id:[6363092](https://www.odoo.com/odoo/project/49/tasks/6363092) Forward-Port-Of: odoo/enterprise#125475 Forward-Port-Of: odoo/enterprise#124882
The timesheet assistant now keeps its suggestions aligned with the date the user most recently selected, even when they navigate dates quickly. It also avoids showing project or task mapping for away-from-keyboard events, keeping those entries informational as intended.
Original PR description
Before this commit, when the user hits multiple times the arrow button to change the date displayed in timesheet assistant, the suggestions displayed could be the suggestions from another day because a rpc is made each time the user changes the date and amoung all rpcs call, the one which takes more time then the one will be taken but it is not necessary the date shown in the view. This commit uses `KeepLast` class to avoid the concurrency issue with those rpcs to be able to always take the last rpc call to get the data.
Polish JPK tax exports now use the supplier bill reference for purchase document numbers when it is available, instead of always using the internal bill number. This helps companies produce tax files that better match vendor documents and official reporting guidance.
Original PR description
**Steps to reproduce:** - Install the `l10n_pl_reports` module and switch to a `PL Company`. - Create and confirm a vendor bill with a `Bill Reference` and `Taxes`. - Navigate to Accounting >…
**Steps to reproduce:** - Install the `l10n_pl_reports` module and switch to a `PL Company`. - Create and confirm a vendor bill with a `Bill Reference` and `Taxes`. - Navigate to Accounting > Reporting > Tax Report and select `This Month`. - From the dropdown, click `JPK` > `Export XML`. - Open the generated XML file and observe the `DowodZakupu` field. **Observation:** `DowodZakupu` contains the vendor `Bill Number` even when a `Bill Reference` is set. **Root Cause:** At [1], `DowodZakupu` is populated only with the vendor `Bill number`(`move_name`) instead of using the `Bill reference`(`ref`) when available. **Fix:** This commit ensures `DowodZakupu` contains the `Bill Reference` when it is available in JPK exports. **Reference:** https://www.podatki.gov.pl/media/eqrn3dey/broszura-jpk_vat-z-deklaracj%C4%85-od-1-lutego-2026-r-en.pdf (page 41) [1]: https://github.com/odoo/enterprise/blob/4b0404058b280136f6865090562f95e18d4d7e0b/l10n_pl_reports/data/jpk_export_templates.xml#L208 opw-6299827 Forward-Port-Of: odoo/enterprise#126113 Forward-Port-Of: odoo/enterprise#121117
Payroll decimal precision settings are now protected from being reset during module upgrades. This prevents company-specific payroll rounding or rate precision choices from being silently overwritten, while still creating the default settings for new installations.
Original PR description
decimal.precision records are user-configurable settings that may be adjusted per company needs. With noupdate="0", every module upgrade resets the 'Payroll' and 'Payroll Rate' precision values back to their defaults, silently discarding any customization made by the user. This is inconsistent with the standard pattern used across Odoo modules. For example, the 'quality' module correctly loads its decimal.precision records with noupdate="1". The same convention is followed in core addons such as 'product' and 'account'. The forcecreate="True" attribute already ensures the records are created on fresh installations, so noupdate="1" only prevents overwriting existing values on upgrade — which is the expected behavior for configuration data. Forward-Port-Of: odoo/enterprise#120509
This fix ensures active German POS transactions can be cancelled even when their transaction details are missing required receipt information. It avoids rejection by the certification service by supplying a minimal cancellation receipt only when needed, while keeping existing transaction data unchanged.
Original PR description
When cancelling active transactions, the schema was forwarded as-is from the listed transaction. ACTIVE transactions can have an empty schema, and Fiskaly rejects the cancellation PUT with:
{
"code": "E_TX_NO_TYPE_DEFINED",
"message": "`schema.raw.process_type` must be defined for
updating or finishing a transaction",
"status_code": 409,
"error": "Conflict"
}
Fall back to a minimal CANCELLATION receipt schema when the transaction has no schema, while preserving any schema that is already present.
opw-6345005
Forward-Port-Of: odoo/enterprise#122130Fixed an issue where the Timesheets overtime indicator could show remaining time in hours instead of days after changing the user's language. This keeps time balances consistent and easier to understand for multilingual teams.
Original PR description
Steps to reproduce: ------------------- 1. Install Timesheets. 2. Create a new employee with a fully fixed working schedule (40h/week). 3. Open Timesheets > All Timesheets and create a new timesheet…
Steps to reproduce: ------------------- 1. Install Timesheets. 2. Create a new employee with a fully fixed working schedule (40h/week). 3. Open Timesheets > All Timesheets and create a new timesheet in the past week for this employee (e.g. 8 hours). 4. Observe the overtime indicator for the employee (it shows 32 hrs) (Click the left arrow to display it). 5. Change the timesheet encoding unit to "Days / Half-Days". 6. Return to Timesheets and observe the overtime indicator (it now correctly shows 4 days). 7. Install a language other than English (e.g. French). 8. Return to Timesheets and observe the overtime indicator again. Issue: -------- The remaining time value changes unexpectedly and displays 32 hours instead of 4 days. Cause: --------- In `get_timesheet_and_working_hours_for_employees`, the code determines whether the timesheet UoM is expressed in days by comparing the UoM name with the string `"days"`. Since UoM names are translatable, this comparison becomes invalid when the user language changes (e.g. `"jours"` in French), causing the logic to skip the day conversion and return values in hours instead. https://github.com/odoo/enterprise/blob/c48290e90fdeadf4f9ca8c44b035e601e7ed380a/timesheet_grid/models/hr_employee.py#L165-L169 Solution: ----------- Compare the timesheet UoM record with the day UoM record directly instead of relying on translated string values. see commit: https://github.com/odoo/enterprise/commit/5fbf194c5056566453a124812fd2614edfe19a82 opw-6279133 Forward-Port-Of: odoo/enterprise#126114 Forward-Port-Of: odoo/enterprise#120595
Fixed an issue that could crash the Employees list when document counts were shown across multiple companies. This keeps HR users able to view all employees reliably in multi-company environments, including when document settings differ by company.
Original PR description
Steps to reproduce: ------------------- 1. Install `documents_hr` and `web_studio` with demo data. 2. Add `document_count` to the Employees list view via Studio. 3. Create a second company with an…
Steps to reproduce:
-------------------
1. Install `documents_hr` and `web_studio` with demo data.
2. Add `document_count` to the Employees list view via Studio.
3. Create a second company with an employee, enable multi-company.
4. Open Employees list, click **All** in the search panel.
Issues:
------
Issue 1:
```python
File "/home/odoo/odoo/enterprise/documents_hr/models/hr_employee.py", line 25, in _compute_document_count
if not self.company_id.documents_hr_settings:
File "/home/odoo/odoo/community/odoo/orm/fields.py", line 1429, in __get__
record.ensure_one()
File "/home/odoo/odoo/community/odoo/orm/models.py", line 5640, in ensure_one
raise ValueError("Expected singleton: %s" % self)
ValueError: Expected singleton: res.company(1, 2)
```
Issue 2:
```python
File "/home/odoo/odoo/enterprise/documents_hr/models/hr_employee.py", line 31, in _compute_document_count
('partner_id', '=', self.work_contact_id.id)
File "/home/odoo/odoo/community/odoo/orm/fields_misc.py", line 117, in __get__
raise ValueError("Expected singleton: %s" % record)
ValueError: Expected singleton: res.partner(9, 8, 7)
```
Cause:
---------
https://github.com/odoo/enterprise/blob/38674448e387159d28f98c1678856fcaf5f7f52e/documents_hr/models/hr_employee.py#L23-L48
1. The document count computation assumes all employees belong to the same company by directly accessing `self.company_id.documents_hr_settings`. In a multi-company environment, `self` may contain employees from different companies, making self.company_id a multi-recordset and triggering a singleton error.
2. Similarly, when `documents_hr_settings` is disabled, the fallback computation accesses `self.work_contact_id` on a multi-recordset, causing another singleton error.
Solution:
-----------
Split the employees based on whether `documents_hr_settings` is enabled and compute each group separately.
Additionally, use the current employee's `work_contact_id` in the fallback computation to avoid singleton error.
**NOTE:**
This has been resolved from saas-19.4 onward with this improvement [commit](https://github.com/odoo/enterprise/commit/d018d8205300b434728129e199ae048cefbaa296).
opw-6351141
Forward-Port-Of: odoo/enterprise#126099
Forward-Port-Of: odoo/enterprise#124826The POS preparation display badge now counts the same active orders shown on the preparation screen, including orders left open overnight. It also stops counting orders that were removed from the screen after a reset, reducing confusion for restaurant and retail staff.
Original PR description
Steps to reproduce: - Configure a preparation display on a POS config with a product category - Place an order and leave it in a non-final stage - Keep the session open past midnight Issue: The…
Steps to reproduce: - Configure a preparation display on a POS config with a product category - Place an order and leave it in a non-final stage - Keep the session open past midnight Issue: The kanban order-count badge drops the order once its create_date falls behind "today", while the preparation screen still lists it. The same divergence makes the badge keep counting an order that a "Reset" already removed from the screen. _compute_order_count() scoped its search on pos_config_id and create_date >= today, whereas the screen is built by get_preparation_display_order() from _get_open_orders_in_display() and _get_stageless_orders_in_display(), which have no date filter and instead bound the set by the order stage `done` flag and the session state. An order open across midnight is therefore in the screen set but not in the badge set. Conversely reset() marks the current stage done, which drops the order from the screen set, but the badge only skipped orders whose latest stage is the final stage, so an order reset while still in the first stage stayed counted. opw-6414302 Forward-Port-Of: odoo/enterprise#126139 Forward-Port-Of: odoo/enterprise#125984
This update reverses a previous change that was only needed for an earlier version and should not have been carried forward. It helps avoid unnecessary errors when using quick create in bank reconciliation on this version.
Original PR description
This commit https://github.com/odoo/enterprise/commit/e559d9f5acbd176792db0dedc4e1f0cad7271457 fixed a problem only happening in 19.0. The commit shouldn't have been forward ported. no task id Forward-Port-Of: odoo/enterprise#126039
Accrual list reports now remember the user's chosen "As of" date when they open a report line and return using the breadcrumb. This prevents the report from unexpectedly reverting to today's date and helps users continue their review with the same reporting period and accurate grouped results.
Original PR description
Issue: In accrual list reports (Billed Not Received / Invoiced Not Delivered), selecting an "As of" date, opening a line, and returning with the breadcrumb resets the date filter to the default value…
Issue: In accrual list reports (Billed Not Received / Invoiced Not Delivered), selecting an "As of" date, opening a line, and returning with the breadcrumb resets the date filter to the default value (today's date) Steps to reproduce: 1) Open an accrual list report ( Accounting > Audit > Purchases > Bill to receive / Billed Not Received OR Invoices to be issues / invoiced Not delivered) 2) Pick any "As of" date 3) Open any row 4) Click breadcrumb to return to the accrual list 5) Observe the "As of" date has been reset to today's date To generate some data you could: create a PO, then upload the bill, validate the receipt, then you'll find it in bills received Cause: `AccrualListController.setup()` always initialized state.date with a fresh default date and did not re-put the previously saved `accrual_entry_date` from restored context https://github.com/odoo/enterprise/blob/899f0d45b2ae1dc4e5e06a2e0acb3d006d12d563/account_reports/static/src/views/accrual_list_controller.js#L10-L16 Although `setDate()` stored the selected date in context, `setup()` overwrote the UI state on controller recreation https://github.com/odoo/enterprise/blob/899f0d45b2ae1dc4e5e06a2e0acb3d006d12d563/account_reports/static/src/views/accrual_list_controller.js#L61-L65 Solution: - Persist `accrual_entry_date` in `AccrualListSearchModel` via `exportState()` / `_importState()`, so the date is restored in search context before the list model loads on breadcrumb navigation. - Initialize the date picker through `setDate()` in `onWillStart()` instead of hardcoding `DateTime.now()` in `setup()`, so restoration and user changes share the same code path. - In `setDate()`, reset grouped list caches (`currentGroups` and `groups`) before `root.load()`, because those caches are not keyed on `accrual_entry_date` and would otherwise show stale vendor groups after a date change or breadcrumb restore. opw-6232263 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/enterprise#118669
Sending the Partner Ledger report by email no longer crashes when multiple companies use different currencies. This ensures the email wizard opens reliably for accounting teams working in multi-company, multi-currency environments.
Original PR description
### Description of the issue/feature this PR addresses Sending the **Partner Ledger** report by email in a multi-currency setup (several companies using different currencies) crashes the send wizard…
### Description of the issue/feature this PR addresses Sending the **Partner Ledger** report by email in a multi-currency setup (several companies using different currencies) crashes the send wizard on opening with: ``` psycopg2.errors.UndefinedTable: relation "account_currency_table" does not exist ``` ### Current behavior before PR To compute the recipients, `AccountPartnerLedgerReportHandler._get_report_send_recipients` runs `_get_query_sums`, whose SQL joins the currency table. In a multi-currency setup that table is a **temporary** table that must be created beforehand by `AccountReport._init_currency_table`. Every regular rendering entry point calls `_init_currency_table` before running currency-table queries, but the report-sending path does not, so the query fails on a missing `account_currency_table` relation. ### Desired behavior after PR is merged `_init_currency_table(options)` is called before running the query, so the temporary table exists. It is a no-op in mono-currency setups (early return in `_init_currency_table`), so mono-currency behavior is unchanged. ### Steps to reproduce 1. Have several companies using different currencies. 2. Select more than one of them in the company switcher. 3. Open **Accounting > Reporting > Partner Ledger**. 4. Click **Send by email** → the wizard crashes on opening. Video: https://drive.google.com/file/d/1skpg7YDtxcY1PCtURyzk5PdFPi7ZPreG/view A regression test covering the multi-currency send-recipients path is included in `test_partner_ledger_report.py`. I've created the task #6362131 for this issue Forward-Port-Of: odoo/enterprise#124900 Forward-Port-Of: odoo/enterprise#122897
Basic document users can now open the spreadsheet creation window even when they do not have access to spreadsheet templates. This removes an inconsistency that blocked them from creating blank spreadsheets from the Documents kanban view, while keeping template access restricted.
Original PR description
A basic user can access the document app and create all types of documents from the kanban view except for the spreadsheets because it requires an access to the templates. While the user cannot interact with the templates, they should have the possibility to create an empty spreadsheet. Note that it can already be done coming from the view of a spreadsheet! This revision ensures that the user can indeed access the spreadsheet creation modal even if they don't have access to the spreadsheet templates. Task-6364964 Forward-Port-Of: odoo/enterprise#126231 Forward-Port-Of: odoo/enterprise#123003
# How to reproduce - Go to a website page - Add atleast one image to the page - Go to Site > Optimize SEO - Add a description to an image - Save # The issue The description is not applied to the image. It is not present in the image's alt in the page and if we re-open the SEO modal, it is not there either. # Cause This [PR] changed the way the image's id are generated in `get_alt_image()`, but did not change the id generation in the check done in `update_alt_images()`, so this check
Original PR description
# How to reproduce - Go to a website page - Add atleast one image to the page - Go to Site > Optimize SEO - Add a description to an image - Save # The issue The description is not applied to the image. It is not present in the image's alt in the page and if we re-open the SEO modal, it is not there either. # Cause This [PR] changed the way the image's id are generated in `get_alt_image()`, but did not change the id generation in the check done in `update_alt_images()`, so this check always fails: https://github.com/odoo/odoo/blob/36eee85dbaed927ff76b2d44a44ba476e9588c72/addons/website/controllers/main.py#L894 [PR]: https://github.com/odoo/odoo/commit/1f1980a1d9835567597ca239d404f47d4677dc47 # Proposed solution Added an helper function for the id generation. This is not necessary but might prevent this from happening again, up to the reviewer to keep it or not opw-6389523 Forward-Port-Of: odoo/odoo#278675 Forward-Port-Of: odoo/odoo#277588
While preloading chart template data, if an account already exists in the db but has `active=False`, it is not found because archived records are excluded from the [`_search`]. As a result, the load attempts to create a new account. During creation, the Python constraint [`_ensure_code_is_unique`] searches with `active_test=False`, finds the archived account with the same code, and raises a ValidationError: ```py File "/home/odoo/src/odoo/saas-19.2/addons/account/models/chart_template.py"
Original PR description
While preloading chart template data, if an account already exists in the db but has `active=False`, it is not found because archived records are excluded from the [`_search`]. As a result, the load…
While preloading chart template data, if an account already exists in the db but has `active=False`, it is not found because archived records are excluded from the [`_search`].
As a result, the load attempts to create a new account. During creation, the Python constraint [`_ensure_code_is_unique`] searches with `active_test=False`, finds the archived account with the same code, and raises a ValidationError:
```py
File "/home/odoo/src/odoo/saas-19.2/addons/account/models/chart_template.py", line 735, in _load_data
'noupdate': True,
^
File "/home/odoo/src/odoo/saas-19.2/odoo/orm/models.py", line 4519, in _load_records
records = self._load_records_create([data['values'] for data in to_create])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/odoo/orm/models.py", line 4426, in _load_records_create
records = self.create(vals_list)
^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/odoo/orm/decorators.py", line 363, in create
return method(self, vals_list)
^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/addons/account/models/account_account.py", line 1057, in create
records._ensure_code_is_unique()
File "/home/odoo/src/odoo/saas-19.2/addons/account/models/account_account.py", line 1135, in _ensure_code_is_unique
raise ValidationError(
odoo.exceptions.ValidationError: Account codes must be unique. You can't create accounts with these duplicate codes: 1.1.1.02.003, 1.1.1.02.004
```
Steps to reproduce:
- In a `v19.0` database, install `l10n_ar` with demo data.
- The accounts with codes `1.1.1.02.003` and `1.1.1.02.004` are created by [`_create_outstanding_accounts`].
- Archive those accounts and upgrade to `v19.2`.
- During the upgrade, [`_get_latam_check_outstanding_account_account`] attempts to create new accounts with the same codes, triggering the duplicate code validation.
tbg-2840
[`_search`]: https://github.com/odoo/odoo/blob/619d8ce9f31349ed06b11ead4988a4a1d9ca6bdd/addons/account/models/chart_template.py#L438
[`_ensure_code_is_unique`]: https://github.com/odoo/odoo/blob/19.0/addons/account/models/account_account.py#L1133
[`_create_outstanding_accounts`]: https://github.com/odoo/odoo/blob/06a46b083fde5688d71c5602553316c9e7160b6a/addons/account/models/chart_template.py#L925C9-L949
[`_get_latam_check_outstanding_account_account`]: https://github.com/odoo/odoo/blob/saas-19.2/addons/l10n_latam_check/models/account_chart_template.py#L60-L76
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#279313
Forward-Port-Of: odoo/odoo#279092**Problem:** When creating a new task, the Customer does not follow the project the user selects: once a first project has filled it, selecting another project keeps the previous project's customer. **Steps to reproduce:** 1. Create a new task and select a project that has a customer. 2. The Customer is set to that project's customer. 3. Select another project configured with a different customer. 4. Observe the Customer keeps the first project's customer. **Current behavior:** The C
Original PR description
**Problem:** When creating a new task, the Customer does not follow the project the user selects: once a first project has filled it, selecting another project keeps the previous project's customer.…
**Problem:** When creating a new task, the Customer does not follow the project the user selects: once a first project has filled it, selecting another project keeps the previous project's customer. **Steps to reproduce:** 1. Create a new task and select a project that has a customer. 2. The Customer is set to that project's customer. 3. Select another project configured with a different customer. 4. Observe the Customer keeps the first project's customer. **Current behavior:** The Customer keeps the first selected project's customer. **Expected behavior:** The Customer follows the selected project and shows its customer. **Cause of the issue:** partner_id is filled by _compute_partner_id, which only assigns a partner while the field is empty. Once a project has filled it, selecting another project no longer refreshes the now non-empty Customer. **Fix:** Refresh the Customer from the project on project_id change, but only while the task is new (no _origin). An existing task's customer is left untouched, since it may already carry sale order lines, timesheets, materials or worksheets that must not be reset when the project changes. opw-6315902 Forward-Port-Of: odoo/odoo#278526 Forward-Port-Of: odoo/odoo#276211
**Steps to reproduce:** - Install website_forum - Create a new post on the forum with the admin - Subscribe to the post notifications using the bell button - Create a new portal user and give him 5 karma (to give him enough rights to answer and comment) - Connect with the portal user and go to the post - Create an answer - Try to comment on your own answer - AccessError is raised **Issue:** Since [1] we check comodel access (in this case `res.partner`) when adding records. Here
Original PR description
**Steps to reproduce:** - Install website_forum - Create a new post on the forum with the admin - Subscribe to the post notifications using the bell button - Create a new portal user and give him 5…
**Steps to reproduce:**
- Install website_forum
- Create a new post on the forum with the admin
- Subscribe to the post notifications using the bell button
- Create a new portal user and give him 5 karma
(to give him enough rights to answer and comment)
- Connect with the portal user and go to the post
- Create an answer
- Try to comment on your own answer
- AccessError is raised
**Issue:**
Since [1] we check comodel access (in this case `res.partner`) when adding records. Here during the `message_post` the `question_followers` are added manually as `partner_ids` before sending (the logic only relies on the original post subscribers, not on the added comment/reply).
```py
question_followers = self.env['mail.followers'].sudo().search([
('res_model', '=', self._name),
('res_id', '=', self.parent_id.id),
('partner_id', '!=', False),
]).filtered(lambda fol: comment_subtype in fol.subtype_ids).mapped('partner_id')
partner_ids += question_followers.ids
```
As the portal user has no `read` access to the subscribers the message creation fails with a traceback.
**Fix:**
Add `sudo` to the `message_post` call of `post_comment`.
[1] https://github.com/odoo/odoo/commit/aae732957c3c3b3590f5686cfccc0ab264d0b5c9
opw-5318757
Forward-Port-Of: odoo/odoo#278840
Forward-Port-Of: odoo/odoo#274751When displaying lots or serial numbers on invoices, the quantity and UoM were rendered without any separator in the SN/LN table, e.g. `1.00Units`. - Add a non-breaking space between the quantity and UoM so the table renders as `1.00 Units`. - Also align the SN/LN value cell consistently with its right-aligned column header. - Update the `sale_stock` and `sale_mrp` report tests to expect whitespace between the quantity and UoM in the rendered invoice output. Task [link](https://www.odoo.
Original PR description
When displaying lots or serial numbers on invoices, the quantity and UoM were rendered without any separator in the SN/LN table, e.g. `1.00Units`. - Add a non-breaking space between the quantity and UoM so the table renders as `1.00 Units`. - Also align the SN/LN value cell consistently with its right-aligned column header. - Update the `sale_stock` and `sale_mrp` report tests to expect whitespace between the quantity and UoM in the rendered invoice output. Task [link](https://www.odoo.com/odoo/project/967/tasks/6365178) task-6365178 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279214 Forward-Port-Of: odoo/odoo#274660
### Issue: When importing EDI documents such as Peppol files, Swiss VAT numbers may be formatted or unformatted When `base_vat` is installed, VAT is stored formatted in Odoo (e.g. `CHE-530.781.296 TVA`) If the format differs, the matching fails and a new partner is created at each import ### Cause: `_retrieve_partner` lacked Swiss-specific VAT normalization logic in `_get_country_specific_vat_variants`, causing it to miss formatted variants with language suffixes (`TVA`, `MWST`, `IVA`)
Original PR description
### Issue: When importing EDI documents such as Peppol files, Swiss VAT numbers may be formatted or unformatted When `base_vat` is installed, VAT is stored formatted in Odoo (e.g. `CHE-530.781.296…
### Issue: When importing EDI documents such as Peppol files, Swiss VAT numbers may be formatted or unformatted When `base_vat` is installed, VAT is stored formatted in Odoo (e.g. `CHE-530.781.296 TVA`) If the format differs, the matching fails and a new partner is created at each import ### Cause: `_retrieve_partner` lacked Swiss-specific VAT normalization logic in `_get_country_specific_vat_variants`, causing it to miss formatted variants with language suffixes (`TVA`, `MWST`, `IVA`) Even when the match succeeded, `_import_ubl_create_missing_customer` still compared VAT strings without stripping `-` and lang suffixes for CH partners, causing a false mismatch and triggering partner creation anyway ### Notes: The partner match improvement is backported from 18.3: https://github.com/odoo/odoo/commit/f1a5a3d72a26ee471d2ef1d8034136208b2bcebc The original fix was incomplete — it added `_get_country_specific_vat_variants` but missed the VAT comparison fix in `_import_ubl_create_missing_customer`, allowing the issue to persist after a successful match `base_vat` is required to get the fix fully working ### Steps to reproduce: - Install `account` and `base_vat` - Create a Vendor (Name: Test CH Vendor, Country: Switzerland, Tax ID: CHE-530.781.296 TVA) - Import a [Peppol Bill](https://github.com/user-attachments/files/27202997/CH_bill_to_import.xml) with VAT `CHE530781296TVA` Before the fix, a new partner is created instead of matching the existing one opw-6353387 Forward-Port-Of: odoo/odoo#279184 Forward-Port-Of: odoo/odoo#274398
With pos_hr enabled, the cash in/out popup uses the cashier's `work_contact_id` as partner for the `account.bank.statement.line`. When that partner is archived or not part of the limited partner loading, the relation cannot be resolved in the frontend and the statement line is created with `partner_id = False`. Deleting such a cash move then crashes in `delete_cash_in_out`: File ".../point_of_sale/models/pos_session.py", line 1877, in delete_cash_in_out action = cashier_name +
Original PR description
With pos_hr enabled, the cash in/out popup uses the cashier's `work_contact_id` as partner for the `account.bank.statement.line`. When that partner is archived or not part of the limited partner…
With pos_hr enabled, the cash in/out popup uses the cashier's `work_contact_id` as partner for the `account.bank.statement.line`. When that partner is archived or not part of the limited partner loading, the relation cannot be resolved in the frontend and the statement line is created with `partner_id = False`.
Deleting such a cash move then crashes in `delete_cash_in_out`:
File ".../point_of_sale/models/pos_session.py", line 1877, in delete_cash_in_out
action = cashier_name + ': ' + str(amount)
TypeError: unsupported operand type(s) for +: 'bool' and 'str'
Steps to reproduce:
- Enable "Multi Employees per Session" (pos_hr) on a PoS config
- Archive the work contact of an employee, or make sure it is not included in the limited partner loading
- Open a session, log in as that employee and register a cash in/out
- As a manager, delete the cash move from the cash move list => Traceback, the cash move cannot be deleted
opw-6389830
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#278462
Forward-Port-Of: odoo/odoo#276598Before this commit, pressing Enter or Ctrl+Enter after editing the custom favorite filter name could save the previous value instead of the latest one. This happened because `t-model.trim` synchronizes the model on the `change` event. Since the save action is triggered on `keydown`, the latest input value had not yet been propagated to the component state. This commit removes `.trim` from `t-model` and trims the description only during the save operation, ensuring the latest value is used
Original PR description
Before this commit, pressing Enter or Ctrl+Enter after editing the custom favorite filter name could save the previous value instead of the latest one. This happened because `t-model.trim` synchronizes the model on the `change` event. Since the save action is triggered on `keydown`, the latest input value had not yet been propagated to the component state. This commit removes `.trim` from `t-model` and trims the description only during the save operation, ensuring the latest value is used while preserving the existing validation against empty or whitespace-only names. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279365 Forward-Port-Of: odoo/odoo#273593
[FIX] stock: fix user permission to edit lots Issue: An user with the Role/User that is Inventory/Administrator is unable to edit the custom Lot/Serial Steps to reproduce: 1. Take a user with Role/User 2. Assign them Inventory/Administrator 3. Log in as this user and create a product tracked by lots 4. Go to the Inventory tab and try editing the field Custom Lot/Serial Cause: Only Role/Administrator can create Ir.Sequence, therefore even if a Role/User is Inventory/Administrator tri
Original PR description
[FIX] stock: fix user permission to edit lots Issue: An user with the Role/User that is Inventory/Administrator is unable to edit the custom Lot/Serial Steps to reproduce: 1. Take a user with Role/User 2. Assign them Inventory/Administrator 3. Log in as this user and create a product tracked by lots 4. Go to the Inventory tab and try editing the field Custom Lot/Serial Cause: Only Role/Administrator can create Ir.Sequence, therefore even if a Role/User is Inventory/Administrator tries to create a new Ir.Sequence (ex: editing a custom Lot/Serial) it will still throw him an error as he is not Role/Administrator Fix: To avoid the permissions issue at the time of creation we use sudo to bypass the Role/User lack of permissions. opw-6269415 Forward-Port-Of: odoo/odoo#278453 Forward-Port-Of: odoo/odoo#268799
The AutoComplete component wraps a text input that already renders its own suggestion dropdown, so the browser's native suggestions must stay out of the way. In 12/2024 in this commit [1] the input's autocomplete was switched from `off` to `new-password` on the premise that `off` is not respected by browsers. In 02/2025, in this commit [2] then made it the component default so many2x pickers stay clean. Chrome runs two independent suggestion mechanisms, and each attribute silences only
Original PR description
The AutoComplete component wraps a text input that already renders its own suggestion dropdown, so the browser's native suggestions must stay out of the way. In 12/2024 in this commit [1] the input's…
The AutoComplete component wraps a text input that already renders its own suggestion dropdown, so the browser's native suggestions must stay out of the way. In 12/2024 in this commit [1] the input's autocomplete was switched from `off` to `new-password` on the premise that `off` is not respected by browsers. In 02/2025, in this commit [2] then made it the component default so many2x pickers stay clean. Chrome runs two independent suggestion mechanisms, and each attribute silences only one of them: - `off` is ignored when the input has a name and Chrome holds autofill (address) data for it. - `new-password` is ignored when Chrome offers "frequently used values", i.e. the form-history dropdown. [3] So `new-password` does not always work. It only stays hidden while the field is ineligible for those suggestions. A field becomes eligible as soon as it gains an `id` and an associated `<label>`. On `saas-19.3` this is provable on commit [4] where adding a prefix icon gave the link popover URL input an `id` + `<label for>`, and the native dropdown resurfaced over the custom URL/page suggestions. This branch targets `saas-18.4`, where the regression is NOT reproducible because that URL input has no `id`/`label` yet. The fix is applied here anyway to avoid the gap that any later `label`/`id` addition re-exposes (as commit [4] proved), and the fix can be forward ported to `saas-19.3`. The website editor's URL field implements autocomplete without the `AutoComplete` component, so the default does not reach it. so `autocomplete="off"` is set directly on that input. [1]: https://github.com/odoo/odoo/commit/9c0f5612 [2]: https://github.com/odoo/odoo/commit/2980694b (opw-4551051) [3]: https://issues.chromium.org/issues/41163264#comment30 [4]: https://github.com/odoo/odoo/commit/2b4d04d1 (task-6034288) task-6370552 Forward-Port-Of: odoo/odoo#279203 Forward-Port-Of: odoo/odoo#277501
Stripe recommends connecting to a reader returned by the most recent discovery call. However, the POS Stripe interface discovered readers as soon as the Stripe Terminal object was created, during POS loading. This means a POS reload performed long before the first payment could populate `pos.discoveredReaders` with stale reader objects. If the first Stripe Terminal payment happens much later, the SDK may try to connect using outdated reader/credential state and fail with an expired Connection
Original PR description
Stripe recommends connecting to a reader returned by the most recent discovery call. However, the POS Stripe interface discovered readers as soon as the Stripe Terminal object was created, during POS loading. This means a POS reload performed long before the first payment could populate `pos.discoveredReaders` with stale reader objects. If the first Stripe Terminal payment happens much later, the SDK may try to connect using outdated reader/credential state and fail with an expired ConnectionToken. Move reader discovery to `connectReader()` so Odoo connects using fresh discovery results, and stop discovering readers eagerly when creating the Stripe Terminal instance. opw-6311626 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#277765 Forward-Port-Of: odoo/odoo#276875
Steps to reproduce --- 1. Confirm a purchase order and receive it a few days later. 2. Open Purchase, Reporting, Purchase and add the "Effective Days To Arrival" measure. Observed: the value is counted from the line's scheduled date instead of the order date, and can even be negative when the scheduled date precedes the confirmation date. Issue --- The metric is meant to be the effective lead time, the number of days between the order confirmation and the actual receipt, falling back
Original PR description
Steps to reproduce --- 1. Confirm a purchase order and receive it a few days later. 2. Open Purchase, Reporting, Purchase and add the "Effective Days To Arrival" measure. Observed: the value is…
Steps to reproduce --- 1. Confirm a purchase order and receive it a few days later. 2. Open Purchase, Reporting, Purchase and add the "Effective Days To Arrival" measure. Observed: the value is counted from the line's scheduled date instead of the order date, and can even be negative when the scheduled date precedes the confirmation date. Issue --- The metric is meant to be the effective lead time, the number of days between the order confirmation and the actual receipt, falling back to the planned "days to receive" when nothing has been received yet according to [task](https://www.odoo.com/odoo/project/809/tasks/3691573). The query instead computes age(date_planned, COALESCE(date_done, date_order)), so once a receipt exists it returns date_planned - date_done (the gap between the scheduled date and the receipt) rather than date_done - date_order. https://github.com/odoo/odoo/blob/c06be48ce7277a667719fd756e0a1f63e91cda27/addons/purchase_stock/report/purchase_report.py#L20-L28 opw-6226523 Forward-Port-Of: odoo/odoo#279267 Forward-Port-Of: odoo/odoo#268843
Taxes with SAF-T code 21 and 22 are not correct and needed modification where they should be set to 0% because: - The foreign supplier invoices without Norwegian VAT, so the invoice total shouldn't increase - You must still self-assess 25% VAT and report it - The 25% is booked as both output and input VAT simultaneously → net cash effect = 0 - Only the basis amount is reported in the VAT return (import boxes) task-6254727 Forward-Port-Of: odoo/odoo#278896 Forward-Port-Of: odoo/odoo#26751
Original PR description
Taxes with SAF-T code 21 and 22 are not correct and needed modification where they should be set to 0% because: - The foreign supplier invoices without Norwegian VAT, so the invoice total shouldn't increase - You must still self-assess 25% VAT and report it - The 25% is booked as both output and input VAT simultaneously → net cash effect = 0 - Only the basis amount is reported in the VAT return (import boxes) task-6254727 Forward-Port-Of: odoo/odoo#278896 Forward-Port-Of: odoo/odoo#267510
The mail.message/delete bus handler accessed selfMember?.seen_message_id.id with optional chaining only on selfMember, not on seen_message_id. When a member has never seen any message in a channel (seen_message_id is False), accessing .id threw 'TypeError: can't access property id, selfMember.seen_message_id is undefined', breaking message deletion for that user (e.g. deleting a message in a channel the user never opened. Add optional chaining on seen_message_id so the unread counter is simpl
Original PR description
The mail.message/delete bus handler accessed selfMember?.seen_message_id.id with optional chaining only on selfMember, not on seen_message_id. When a member has never seen any message in a channel (seen_message_id is False), accessing .id threw 'TypeError: can't access property id, selfMember.seen_message_id is undefined', breaking message deletion for that user (e.g. deleting a message in a channel the user never opened. Add optional chaining on seen_message_id so the unread counter is simply not decremented when no message has been seen yet. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279279 Forward-Port-Of: odoo/odoo#277417
Test `load dashboard that doesn't exist` sometimes fails because the test doesn't wait for the rpc to complete before checking the error message. This commit adds a wait for the next animation frame to ensure the rpc has completed before checking the error message. Runbot-940389 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/su
Original PR description
Test `load dashboard that doesn't exist` sometimes fails because the test doesn't wait for the rpc to complete before checking the error message. This commit adds a wait for the next animation frame to ensure the rpc has completed before checking the error message. Runbot-940389 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#279418
The navigator class, which adds event handlers, is instanciated on the setup of components using the useNavigation hook. However, before this commit, it was destroyed when the components were unmounted. It may happen that components are instanciated (setup is executed) but never mounted in the DOM, when the current rendering is cancelled. When this happened, the navigator cleanup wasn't executed, resulting in a small memory leak. This commit fixes the issue by using onWillDestroy instead.
Original PR description
The navigator class, which adds event handlers, is instanciated on the setup of components using the useNavigation hook. However, before this commit, it was destroyed when the components were unmounted. It may happen that components are instanciated (setup is executed) but never mounted in the DOM, when the current rendering is cancelled. When this happened, the navigator cleanup wasn't executed, resulting in a small memory leak. This commit fixes the issue by using onWillDestroy instead. 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#279322
Problem: When adding a caption to an image, typing text, then undoing and redoing does not restore the caption content. Cause: The caption used a native `<input>` element whose content is managed by the browser, not the editor's history stack. While redo could restore the input element itself, its content was lost since the editor never tracked it. Solution: Replace the `<input>` with a `contenteditable` `<span>` so the editor manages its content as part of the DOM history, enabling ful
Original PR description
Problem: When adding a caption to an image, typing text, then undoing and redoing does not restore the caption content. Cause: The caption used a native `<input>` element whose content is managed by the browser, not the editor's history stack. While redo could restore the input element itself, its content was lost since the editor never tracked it. Solution: Replace the `<input>` with a `contenteditable` `<span>` so the editor manages its content as part of the DOM history, enabling full undo/redo. Changes: - Use a contenteditable `<span>` instead of `<input>` for caption editing - Prevent pasting HTML inside the span (plain text only) - Limit caption content to 100 characters - Disable power box, toolbar, and paragraph insertion inside the span task-6219868 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278901 Forward-Port-Of: odoo/odoo#267211
Issue: --- Adding a rating with a message on website is causing TB as the portal user doesn't have the access to send rating message. opw-6316142 Forward-Port-Of: odoo/odoo#279171 Forward-Port-Of: odoo/odoo#271833
Original PR description
Issue: --- Adding a rating with a message on website is causing TB as the portal user doesn't have the access to send rating message. opw-6316142 Forward-Port-Of: odoo/odoo#279171 Forward-Port-Of: odoo/odoo#271833
Before this commit, the composer suggestion list could re-open right after the user closed it with Escape, and the next press of Escape would then close the list again instead of being handled by the composer (e.g. discarding a reply). This happened because NavigableList was re-opened on every patch: the useEffect opening the list had `[this.props]` as dependency, and props are a new object on every render. Any unrelated re-render of the composer (e.g. triggered by a late store update) would
Original PR description
Before this commit, the composer suggestion list could re-open right after the user closed it with Escape, and the next press of Escape would then close the list again instead of being handled by the composer (e.g. discarding a reply). This happened because NavigableList was re-opened on every patch: the useEffect opening the list had `[this.props]` as dependency, and props are a new object on every render. Any unrelated re-render of the composer (e.g. triggered by a late store update) would therefore re-open the list, which would then steal the next Escape from the composer. Fix by narrowing the dependency to the content of the options, so the list only opens on mount and when a new set of options arrives. https://runbot.odoo.com/odoo/error/944571 Forward-Port-Of: odoo/odoo#279275 Forward-Port-Of: odoo/odoo#278929
In the Italian localization, a bill should not be reset to draft once it has been sent to SDI. The exception is when the document was rejected or imported (0440cbab7a9cc183c7836fe58196799afdc00513). Steps to reproduce: - Create a vendor bill with l10n_it_edi enabled. - Send it to SDI. - The "Reset to Draft" button is still visible. opw-6350928 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#273616
Original PR description
In the Italian localization, a bill should not be reset to draft once it has been sent to SDI. The exception is when the document was rejected or imported (0440cbab7a9cc183c7836fe58196799afdc00513). Steps to reproduce: - Create a vendor bill with l10n_it_edi enabled. - Send it to SDI. - The "Reset to Draft" button is still visible. opw-6350928 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#273616
Clicking a Unit Cost in the stock report opens the Unit Cost History of that product. The action filters on `active_id`, which the cost cell fills with the id of the row that was clicked. That value was overwritten: the cell context was merged first and the context of the running action last, so the latter won. When the list comes from "Inventory at Date", that context carries the id of the `stock.quantity.history` record the wizard just created, which is not a product id at all. The history
Original PR description
Clicking a Unit Cost in the stock report opens the Unit Cost History of that product. The action filters on `active_id`, which the cost cell fills with the id of the row that was clicked. That value was overwritten: the cell context was merged first and the context of the running action last, so the latter won. When the list comes from "Inventory at Date", that context carries the id of the `stock.quantity.history` record the wizard just created, which is not a product id at all. The history then opens on whichever product happens to carry that id, and stays empty when none does. Steps to reproduce: - Inventory > Reporting > Stock, click a Unit Cost -> the history of that product opens, as expected - go back, click "Inventory at Date" and confirm - click that same Unit Cost -> another product's history opens, or an empty list opw-6391778 Forward-Port-Of: odoo/odoo#279116
With the Shared Customer Account setting enabled, a user created in c1 cannot access the shop in the website of c2 Steps to reproduce: 1. Install eCommerce and Contacts 2. Go to Settings > Users & Companies > Companies and create two companies c1 and c2 3. Go to Website > Configuration > Websites and create two websites w1 with company c1 and w2 with company c2 4. Change the order of the websites so that w1 is at the top 5. Go to Website > eCommerce > Pricelists and create a pricelist pl
Original PR description
With the Shared Customer Account setting enabled, a user created in c1 cannot access the shop in the website of c2 Steps to reproduce: 1. Install eCommerce and Contacts 2. Go to Settings > Users &…
With the Shared Customer Account setting enabled, a user created in c1 cannot access the shop in the website of c2 Steps to reproduce: 1. Install eCommerce and Contacts 2. Go to Settings > Users & Companies > Companies and create two companies c1 and c2 3. Go to Website > Configuration > Websites and create two websites w1 with company c1 and w2 with company c2 4. Change the order of the websites so that w1 is at the top 5. Go to Website > eCommerce > Pricelists and create a pricelist pl1 in c1 assigned to w1 and pl2 in c2 assigned to w2 6. In an incognito tab, go to w1 and create a new account 7. As admin, go to Website > Configuration > Websites and change the order of the websites so that w2 is at the top 8. In an incognito tab, connect with the previously created account and go to the shop 9. An error is thrown (This error only happens when geoip works, i.e. when `_get_geoip_country_code` returns something) Issue: When geoip returns a country code, we search through all the pricelists available for that country code but some of them can be restricted to a company which raises an access error. We need to be able to access them in order to filter the ones that are not available on the current website Solution: Access all pricelists with sudo, they will be filtered out with `_is_available_on_website` opw-3574089 Forward-Port-Of: odoo/odoo#268863
**Issue:** `test_mail_template_dynamic_placeholder_tour` tour is failing sometimes with the following error: `Tour mail_template_dynamic_field_tour → Step Click on contact (trigger: div[name="model_id"] .ui-autocomplete). TypeError: Cannot read properties of undefined (reading 'click')` **Cause:** It happens that the element is not loaded yet after the delay and clicking on an undefined element triggers the error. **Solution:** Make sure to only click on the element when it's ready.
Original PR description
**Issue:** `test_mail_template_dynamic_placeholder_tour` tour is failing sometimes with the following error: `Tour mail_template_dynamic_field_tour → Step Click on contact (trigger: div[name="model_id"] .ui-autocomplete). TypeError: Cannot read properties of undefined (reading 'click')` **Cause:** It happens that the element is not loaded yet after the delay and clicking on an undefined element triggers the error. **Solution:** Make sure to only click on the element when it's ready. runbot-223306 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278983 Forward-Port-Of: odoo/odoo#277954
When an image is start-aligned, if a list is defined after it, its bullets/numbers/checkboxes are rendered on top of the image. This commit makes the bullets rendered after the image. Steps to reproduce: - Edit a website page - Drop a text page - Insert an image with `/image` - Align image to the left - Insert a bullet list or a numbered list or a checkbox list with some indented entries => Some bullets were rendered on top of the image Additionally, the start-align is neutral
Original PR description
When an image is start-aligned, if a list is defined after it, its bullets/numbers/checkboxes are rendered on top of the image. This commit makes the bullets rendered after the image. Steps to reproduce: - Edit a website page - Drop a text page - Insert an image with `/image` - Align image to the left - Insert a bullet list or a numbered list or a checkbox list with some indented entries => Some bullets were rendered on top of the image Additionally, the start-align is neutralized inside list lines because other approaches do not provide a satisfactory layout - and break further situations. task-6116437 Forward-Port-Of: odoo/odoo#260325
Routes inherited from product category are not available when on replenish. Steps to reproduce: ------------------- * Create a new route and allow it on product category * Create a product category and add the route * Add the category to a product * use the repenish button -> the category route is not available on the replenishment Observation: ------------- When opening replenishment, it will open the view_product_replenish, in that view, it will use allowed_route_ids to allow us
Original PR description
Routes inherited from product category are not available when on replenish. Steps to reproduce: ------------------- * Create a new route and allow it on product category * Create a product category…
Routes inherited from product category are not available when on replenish. Steps to reproduce: ------------------- * Create a new route and allow it on product category * Create a product category and add the route * Add the category to a product * use the repenish button -> the category route is not available on the replenishment Observation: ------------- When opening replenishment, it will open the view_product_replenish, in that view, it will use allowed_route_ids to allow us to choose a route for this replenishment: https://github.com/odoo/odoo/blob/2f25b2a70eca5b4f3c2e1d6c7003b1760afc0ba0/addons/stock/wizard/product_replenish_views.xml#L37 allowed_route_ids is compute in the mixin with the following domain: https://github.com/odoo/odoo/blob/2f25b2a70eca5b4f3c2e1d6c7003b1760afc0ba0/addons/stock/models/stock_replenish_mixin.py#L18-L21 https://github.com/odoo/odoo/blob/2f25b2a70eca5b4f3c2e1d6c7003b1760afc0ba0/addons/stock/models/stock_replenish_mixin.py#L25-L31 this only take into account route from the product and not the ones from the product category. https://github.com/odoo/odoo/blob/deeecf7cd02e7383b591835b0c6495e3ddead0ff/addons/stock/models/stock_location.py#L511 opw-6297308 Forward-Port-Of: odoo/odoo#271535
Before this commit, the pointerup event when doing a drag'n'drop in kanban view was containing the kanban card placeholder. Now, this placeholder isn't considered anymore to prevent unwanted target in the event. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278324
Original PR description
Before this commit, the pointerup event when doing a drag'n'drop in kanban view was containing the kanban card placeholder. Now, this placeholder isn't considered anymore to prevent unwanted target in the event. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278324
The state_id field was not cleared when editing an address and switching to a country without states — the state selector reset visually, but the stale state_id was still saved to the backend. Steps to reproduce: 1. Add a product to the cart. 2. Go to checkout and edit the address, selecting a country that has states. 3. Edit the address again, now selecting a country without states. 4. Save and check the contact in the backend: state_id still holds the state from the previo
Original PR description
The state_id field was not cleared when editing an address and switching to a country without states — the state selector reset visually, but the stale state_id was still saved to the backend. Steps to reproduce: 1. Add a product to the cart. 2. Go to checkout and edit the address, selecting a country that has states. 3. Edit the address again, now selecting a country without states. 4. Save and check the contact in the backend: state_id still holds the state from the previous country. Solution: reset the state_id select options for the new country. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278660 Forward-Port-Of: odoo/odoo#278125
Steps to reproduce: - Edit a page. - Drop a carousel snippet like s_quotes_carousel - Click "Add Slide" with the browser console open(for concrete race condition) => Traceback: TypeError: Cannot read properties of null (reading 'classList') Cause: `slide()` used the editor window's `Carousel` instead of the iframe's. This created a second Carousel instance for the same element. Both instances updated the indicators at the same time, causing one to remove the active indicator before the
Original PR description
Steps to reproduce: - Edit a page. - Drop a carousel snippet like s_quotes_carousel - Click "Add Slide" with the browser console open(for concrete race condition) => Traceback: TypeError: Cannot read properties of null (reading 'classList') Cause: `slide()` used the editor window's `Carousel` instead of the iframe's. This created a second Carousel instance for the same element. Both instances updated the indicators at the same time, causing one to remove the active indicator before the other tried to use it, leading to the traceback. Fix: Use `this.window.Carousel` so the iframe's existing Carousel instance is reused instead of creating a second one. task-6084484 Forward-Port-Of: odoo/odoo#279117 Forward-Port-Of: odoo/odoo#275903
### Issue before this commit: 1. When generating a FatturaPA XML for a self-invoice (reverse charge / autofattura, e.g. TD17-TD19) with the "Reference" (ref) field filled in, the supplier's invoice number was placed under <DatiOrdineAcquisto> instead of <DatiFattureCollegate>. 2. When a credit note was generated from a vendor bill, the <IdDocumento> in <DatiFattureCollegate> contained Odoo's internal document number (e.g. BILL/2026/07/0002) instead of the actual reference of the invoice rece
Original PR description
### Issue before this commit: 1. When generating a FatturaPA XML for a self-invoice (reverse charge / autofattura, e.g. TD17-TD19) with the "Reference" (ref) field filled in, the supplier's invoice…
### Issue before this commit: 1. When generating a FatturaPA XML for a self-invoice (reverse charge / autofattura, e.g. TD17-TD19) with the "Reference" (ref) field filled in, the supplier's invoice number was placed under <DatiOrdineAcquisto> instead of <DatiFattureCollegate>. 2. When a credit note was generated from a vendor bill, the <IdDocumento> in <DatiFattureCollegate> contained Odoo's internal document number (e.g. BILL/2026/07/0002) instead of the actual reference of the invoice received from the supplier (ref). ### Steps to reproduce the issue: ISSUE 1: 1. Download Accounting and l10n_it 2. Go to Vendor -> Bills 3. Create a bill with: 1. Italian company as vendor 2. Product with tax 22% S RC 3. Bill reference filled (ex. FT00001) 4. Send it to SDI, open the XML and see that the tag <IdDocumento> is inside the tag <DatiOrdineAcquisto> while it sohuld be inside <Datifatturecollegate> ISSUE 2: 1. From a bill created click Credit Note 2. Send to SDI again, open the XML and see that the tag <IdDocumento> contains the bill reference created in Odoo while it should take the reference of the original invoice SENT by the vendor ### Cause of the issue: 1. The template's t-elif chain did not distinguish between self-invoices and regular documents, so any value in record.ref was routed to DatiOrdineAcquisto regardless of context. 2. Separately, the linked_moves loop always used linked_move.name to populate <IdDocumento>, which for vendor bills/refunds is Odoo's own sequential number, not the supplier's original invoice number. ### Reason to introduce the fix: 1. For the official FatturaPA Technical Specifications, DatiOrdineAcquisto must only reference a purchase order, while DatiFattureCollegate must reference a related invoice — which is the correct category for the supplier document being integrated in a self-invoice. This is confirmed by the Agenzia delle Entrate documentation: https://www.agenziaentrate.gov.it/portale/documents/d/guest/allegato-a-specifiche-tecniche-vers-1-9 (p.107, chapter Compilazione del documento XML con codice TD17) 2. For credit/debit notes, <IdDocumento> inside <DatiFattureCollegate> must contain the number of the original invoice being referenced/varied, not an internally generated document number, as clarified here: https://www.pa.sm/ticket/kb/faq.php?id=46 opw-6117968 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276523
Portal subscribe task can create tb because the task was archived before unsubscribing. opw-6397850 Forward-Port-Of: odoo/odoo#279071 Forward-Port-Of: odoo/odoo#278667
Original PR description
Portal subscribe task can create tb because the task was archived before unsubscribing. opw-6397850 Forward-Port-Of: odoo/odoo#279071 Forward-Port-Of: odoo/odoo#278667
Description of the issue this commit addresses: The settlement tour expects an invoice named with the year 2026. On time-shifted test instances, invoices use a later year, so the tour cannot find the invoice and fails at the settlement selection step. --- Desired behavior after this commit is merged: This commit matches settlement invoices using the stable journal prefix, so the tour works regardless of the year in which it runs. --- runbot-[242206](https://runbot.odoo.com/odoo
Original PR description
Description of the issue this commit addresses: The settlement tour expects an invoice named with the year 2026. On time-shifted test instances, invoices use a later year, so the tour cannot find the invoice and fails at the settlement selection step. --- Desired behavior after this commit is merged: This commit matches settlement invoices using the stable journal prefix, so the tour works regardless of the year in which it runs. --- runbot-[242206](https://runbot.odoo.com/odoo/error/242206) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278573
A return coming back from the customer must always decrease the delivered quantity of a sale order line, even when it is not linked to its original delivery (no origin_returned_move_id and its picking has no return_id) These unlinked returns are created through a negative procurement, example: when the ordered quantity is reduced below the delivered one. Steps to reproduce: - Confirm a sale order of 10 and deliver the 10 - Cancel the order and reset it to draft - Reduce the line quantit
Original PR description
A return coming back from the customer must always decrease the delivered quantity of a sale order line, even when it is not linked to its original delivery (no origin_returned_move_id and its picking has no return_id) These unlinked returns are created through a negative procurement, example: when the ordered quantity is reduced below the delivered one. Steps to reproduce: - Confirm a sale order of 10 and deliver the 10 - Cancel the order and reset it to draft - Reduce the line quantity from 10 to 4 - Re-confirm: the pull rule generates a return of 6 with to_refund set but no origin_returned_move_id nor picking.return_id - Validate that return Before: delivered stays at 10. After: delivered is 4. opw-6345628 Forward-Port-Of: odoo/odoo#274399
Documentation and clarification updates
Description of the issue/feature this PR addresses: Add the Odoo Corporate Contributor License Agreement for bobco GmbH and list the employees authorized to contribute on the company's behalf. Current behavior before PR: bobco GmbH does not yet have a corporate CLA in the Odoo repository. Desired behavior after PR is merged: The corporate CLA covers the listed bobco GmbH contributors. Approval and signature status: @philipbr explicitly approved the declaration as an authorized representat
Original PR description
Description of the issue/feature this PR addresses: Add the Odoo Corporate Contributor License Agreement for bobco GmbH and list the employees authorized to contribute on the company's behalf. Current behavior before PR: bobco GmbH does not yet have a corporate CLA in the Odoo repository. Desired behavior after PR is merged: The corporate CLA covers the listed bobco GmbH contributors. Approval and signature status: @philipbr explicitly approved the declaration as an authorized representative of bobco GmbH. The signatory line has been finalized as: `Philip Braun philip@braun.tf https://github.com/philipbr` --- The corporate CLA has been signed by Philip Braun, and the PR guidelines at www.odoo.com/submit-pr have been read. Forward-Port-Of: odoo/odoo#279348
Miscellaneous changes
Optimize search_paid_order_ids to reduce ORM overhead and resolve a database bottleneck during POS synchronization. - Move currency validation from a Python post-filter into the SQL domain via config_id.currency_id. - Split the costly OR query on pos.order.line into two targeted index scans combined via UNION, dropping execution time from ~627ms to ~16ms (~40x speedup) and reducing shared read blocks from 650k+ to zero. - Align totalCount with the exact same domain and simplify state exclus
Original PR description
Optimize search_paid_order_ids to reduce ORM overhead and resolve a database bottleneck during POS synchronization. - Move currency validation from a Python post-filter into the SQL domain via config_id.currency_id. - Split the costly OR query on pos.order.line into two targeted index scans combined via UNION, dropping execution time from ~627ms to ~16ms (~40x speedup) and reducing shared read blocks from 650k+ to zero. - Align totalCount with the exact same domain and simplify state exclusions from multiple != operators to a single not in. Explain Before: https://explain.dalibo.com/plan/84095dcgfdc158a2 Explain After: https://explain.dalibo.com/plan/ag2abcdf4ge5d5e5 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278739
The government has held off the `shipToGSTIN` changes until further notice. https://services.gst.gov.in/services/advisoryandreleases/read/668 Hence, this PR reverts those changes. task-6431622 ref - https://github.com/odoo/odoo/pull/266388 Forward-Port-Of: odoo/odoo#279421
Original PR description
The government has held off the `shipToGSTIN` changes until further notice. https://services.gst.gov.in/services/advisoryandreleases/read/668 Hence, this PR reverts those changes. task-6431622 ref - https://github.com/odoo/odoo/pull/266388 Forward-Port-Of: odoo/odoo#279421
Steps to reproduce: ------------------------- 1. Install hr_holidays. 2. Create a time off type with "Custom Hours" as the duration type and "Considered as" set to "Working Time". 3. Create a multi-day time off request for an employee with a standard working schedule. 4. Check the computed duration. Issue: -------- The duration is 32 hours instead of the expected 16 hours. Cause: ---------- https://github.com/odoo/odoo/blob/c0d737142f3a0a385163eb8d075ca71d63913b68/addons/hr_holid
Original PR description
Steps to reproduce: ------------------------- 1. Install hr_holidays. 2. Create a time off type with "Custom Hours" as the duration type and "Considered as" set to "Working Time". 3. Create a…
Steps to reproduce: ------------------------- 1. Install hr_holidays. 2. Create a time off type with "Custom Hours" as the duration type and "Considered as" set to "Working Time". 3. Create a multi-day time off request for an employee with a standard working schedule. 4. Check the computed duration. Issue: -------- The duration is 32 hours instead of the expected 16 hours. Cause: ---------- https://github.com/odoo/odoo/blob/c0d737142f3a0a385163eb8d075ca71d63913b68/addons/hr_holidays/models/hr_leave.py#L621-L623 For hourly time off types considered as working time, the duration was computed from the raw datetime difference between `date_from` and `date_to`. This bypassed the existing working schedule computation and included hours outside the employee's working schedule. Solution: ------------ Remove this logic and let the duration fall back to the working schedule-based computation. **NOTE:** This issue has been resolved in saas-19.4 with this [commit](https://github.com/odoo/odoo/commit/53672fdca597644a2b675ce887042ef640073d4f) opw-6385683 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Backport of [1]. Builder image tests were flaky in full-suite runs because earlier tests left slow requests pending. Bogus snippet thumbnails, obsolete modify_image mock data, and made-up attachment URLs triggered expensive website 404 rendering and starved the browser connection pool. Avoid rendering missing thumbnails, use data URIs or existing static images in fixtures, return the current modify_image response shape, and give the CORS test image explicit dimensions. [1]: https://gith
Original PR description
Backport of [1]. Builder image tests were flaky in full-suite runs because earlier tests left slow requests pending. Bogus snippet thumbnails, obsolete modify_image mock data, and made-up attachment URLs triggered expensive website 404 rendering and starved the browser connection pool. Avoid rendering missing thumbnails, use data URIs or existing static images in fixtures, return the current modify_image response shape, and give the CORS test image explicit dimensions. [1]: https://github.com/odoo/odoo/pull/277424 Forward-Port-Of: odoo/odoo#279584 Forward-Port-Of: odoo/odoo#279314