Thursday, August 27, 2026
20 changes · 18.0
Enhancements to existing features
The mail test suite now re-checks certain pending conditions more frequently instead of waiting up to 10 seconds. This reduces unnecessary waiting in automated testing, helping developers get feedback faster without changing user-facing behavior.
Original PR description
Before this commit, a contains that does not match right away runs again only when its MutationObserver fires, and once more at the 10 seconds timeout. The problem is that the observer reports neither a text node updated in place nor an input value or checked property, so a check waiting for one of those sleeps 10 seconds and then passes: "Delete starred message decrements starred counter once" spends 10.2s of the 183s @mail suite waiting for a counter to go from "Starred3" to "Starred2". This commit turns that single timeout into a 500ms tick up to the same deadline, so that such a check costs 500ms. The tick uses the unmocked timer to stay out of the timer graph a test drives with runAllTimers, and only 32 of the suite's 4239 contains calls stay pending long enough to tick once.
The web test runner now avoids unnecessary memory cleanup pauses unless memory use is actually high. This reduces wasted time during automated testing while keeping the existing safety behavior for browsers that cannot report memory usage.
Original PR description
Before this commit, the unit test runner pauses for a major garbage collection after every test file, since __gcAndLogMemory collects whenever window.gc exists and browser_js always passes --js-flags=--expose-gc. The problem is that the heap it protects stays at 183MB of a 4192MB limit, so those collections reclaim nothing: the 84 of an @mail run cost 8.8s of its 183s, and the whole suite has 1268 test files. This commit turns the unconditional collection into one that runs only past half the limit, so that a run pays for it when the heap really grows. A browser without performance.memory keeps collecting after every file.
Lead assignment now checks whether records still exist in batches instead of repeating the same database check on every loop. This reduces unnecessary database work and helps CRM lead allocation run more reliably under heavier volumes.
Original PR description
Every call to `.exists` inside `_allocate_leads` runs a select query for every iteration of the `while population:` loop. We can reduce the query count by using the already built block to check occasionally. https://github.com/odoo/odoo/blob/d6c01a69b4314b869510be8e48ccaa004e1c918a/addons/crm/models/crm_team.py#L496-L500 This is the major contributing factor to why the tests `test_assign_perf_duplicates` `test_assign_perf_no_duplicates`, and `test_assign_perf_populated` are failing https://github.com/odoo/odoo/blob/d6c01a69b4314b869510be8e48ccaa004e1c918a/addons/crm/tests/test_performances.py#L180-L182 https://github.com/odoo/odoo/blob/d6c01a69b4314b869510be8e48ccaa004e1c918a/addons/crm/tests/test_performances.py#L97-L99 https://github.com/odoo/odoo/blob/d6c01a69b4314b869510be8e48ccaa004e1c918a/addons/crm/tests/test_performances.py#L51-L54 runbot-238398
Odoo now applies the same permission checks to related record changes passed through the context as it does to normal field updates. This prevents unauthorized changes from being silently ignored and makes access behavior more consistent and predictable.
Original PR description
Some commands may perform a change on related records by using Commands. When passed through the context, unallowed actions are ignored instead of rising access errors. This check aligns the behaviour with normal writes of fields. A test in `project` module shows this behaviour. Backport of odoo/odoo#258845 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The live chat settings now explain that automatic chat popups only open on larger screens. This helps teams understand why mobile visitors see the chat button instead of an automatically opened chat window during testing or rollout.
Original PR description
The 'Open automatically' action only triggers the auto popup on larger screens (`ui.isSmall` is checked in `AutopopupService. allowAutoPopup`). On mobile/small viewports, only the chat button is shown and the visitor must tap it manually. The existing help text does not mention this, which could lead to confusion when the auto popup does not trigger during testing on mobile. Update the field's help text to explicitly state that automatic opening is limited to larger screens. opw-6459279 Forward-Port-Of: odoo/odoo#284785
Italian electronic invoices can now be imported without relying on a specific file name format. This reduces rejected or incomplete imports when files are renamed, making invoice processing smoother for users.
Original PR description
With this commit: - We remove the regex check on the imported file name. - The behavior before was checking the filename to fill data into the invoice. - This should not be constrained so that we can ignore the filename Task [link](https://www.odoo.com/odoo/project.task/6152773) task-6152773
Resolved issues and error corrections
This fixes an error that could stop users from generating PDF quotes when Quote Builder documents include dynamic fields. The change preserves required PDF form settings and adds the missing structure expected by the PDF library, making quote printing more reliable.
Original PR description
Issue: --- Due to this issue, generating PDF Quote using Quote Builder with dynamic fields leads to a traceback. This was partially fixed by: e16edc7b0d9f56cb7769068ecf7d64ff8b0f6359 Steps: --- 1-…
Issue: --- Due to this issue, generating PDF Quote using Quote Builder with dynamic fields leads to a traceback. This was partially fixed by: e16edc7b0d9f56cb7769068ecf7d64ff8b0f6359 Steps: --- 1- Using a python 3.13 env, install requirements.txt. (You could instead uninstall pypdf2 and install pypdf==5.4.0) 2- Enable Quote Builder. 3- Create a SO and in quote builder tab, select a document. This document should have dynamic fields. e.g. you could use`Office Furnitures Header` document. 4- Print -> PDF Quote. Cause: --- In previous fix, we fixed the traceback when no dynamic field is set. However, if you have a dynamic field, then inside `PdfWriter._update_field_annotation()`, the font is get from `DR` dict inside acroform: https://github.com/py-pdf/pypdf/blob/f20954f2241640feb484800e191373f8fbdfa44b/pypdf/_writer.py#L917-L929 Even if we are not setting a font, we need to have an empty `DR` dict inside acroform, in order to avoid calling `get` on a none object, which is leading to the traceback. Also in previous fix we were losing `NeedAppearances` inside `AcroForm` by creating new dict which wasn't right. opw-6392001
Documentation and clarification updates
Shinnosuke Morita has been added to Quartile's corporate contributor license agreement list. This is a legal/administrative update that helps confirm contribution authorization and has no impact on product functionality.
Original PR description
Adding myself to the Quartile corporate CLA contributors list. Related: https://github.com/odoo/enterprise/pull/129459
This fix ensures the Urban Piper point-of-sale integration is removed and restored together with the related enterprise point-of-sale component. It prevents reinstall errors that could interrupt system maintenance or module updates.
Original PR description
Problem: Uninstalling and reinstalling `web_enterprise` causes a ParseError on the `res.config.settings` view when `mail_mobile` (or another module) is loaded. Cause: `pos_urban_piper` inherits `res.config.settings` and targets the setting block `urban_piper_setting`, which is defined in `pos_enterprise`. However, `pos_urban_piper` only declared a dependency on `pos_preparation_display`. When `web_enterprise` is uninstalled, `pos_enterprise` is uninstalled as a cascade, but `pos_urban_piper` stays installed. This leaves an orphaned inherited view targeting an anchor that no longer exists. On the reinstall pass, loading views revalidates the inheritance tree before `pos_enterprise` is reloaded, causing a ParseError. Fix: Add `pos_enterprise` to `pos_urban_piper`'s manifest dependencies so `pos_urban_piper` is uninstalled and reinstalled together with the module owning its view anchor. runbot-234911
The Shop Floor view now disables the gear menu while opening a Manufacturing Order, preventing users from launching another dialog during the transition. This avoids a disruptive error on slower connections and makes navigation from work orders more reliable.
Original PR description
**Steps to reproduce:** * Install the **Manufacturing** module with **Work Orders** enabled. * Create and confirm a Manufacturing Order with at least one Work Order. * Open the **Shop Floor** view. *…
**Steps to reproduce:** * Install the **Manufacturing** module with **Work Orders** enabled. * Create and confirm a Manufacturing Order with at least one Work Order. * Open the **Shop Floor** view. * On a work order card, click the **gear** icon to open the menu dialog. * Click **Open Manufacturing Order** on a slow network connection. * Before the MO form view finishes loading, quickly click the **gear** icon again and open another dialog (e.g. Log Note). * The MO form view loads, destroying the shop floor component. * Close the Log Note dialog. **Observed behavior:** * An `UncaughtPromiseError: Component is destroyed` error is thrown because the dialog tries to interact with the shop floor component that has already been destroyed by the navigation to the MO form view. **Cause:** * When the user clicks "Open Manufacturing Order", `doAction` is called to navigate to the MO form view, and `props.close()` immediately closes the menu dialog. However, the shop floor component is still visible while the new view is loading. * During this gap, the gear button remains clickable. If the user opens another dialog (e.g. Log Note), that dialog holds a reference to the shop floor component. When the MO form view finishes mounting, the shop floor is destroyed, and closing the stale dialog triggers operations on the destroyed component. **Fix:** * Add an `actionPending` state flag to `MrpDisplayRecord`. When the user selects "Open Manufacturing Order" from the menu dialog, an `onSelect` callback sets `actionPending` to `true`, which disables the gear button and prevents any new dialog from being opened. * The flag is only set for `openMO` (which navigates away and destroys the component), not for other menu actions like Scrap, Add Component, or Log Note which open wizard dialogs and return to the shop floor. opw-6107579
This fix restores a required tax total entry on Saudi electronic invoices, including invoices issued in SAR. It prevents ZATCA validation errors that could block invoice submission or compliance workflows.
Original PR description
This reverts commit 77280860a3a3557619ac13c853c37a66e9d19f2e. The reverted commit removed the second cac:TaxTotal node (in company currency, without tax subtotals) whenever the invoice currency…
This reverts commit 77280860a3a3557619ac13c853c37a66e9d19f2e. The reverted commit removed the second cac:TaxTotal node (in company currency, without tax subtotals) whenever the invoice currency matched the company currency (SAR), based on the assumption that it was an optional node only needed for foreign-currency invoices. This is wrong. Business rule BR-KSA-EN16931-09 is currency-agnostic: it requires exactly one TaxTotal without subtotals whenever cbc:TaxCurrencyCode is present in the document, regardless of whether the document and tax currencies match. Since l10n_sa_edi always sets TaxCurrencyCode, the second TaxTotal node is always required, even when both TaxTotal amounts end up identical (which is normal and expected for a same-currency invoice). ZATCA's own official "Standard Invoice" sample confirms this: a plain SAR invoice (DocumentCurrencyCode = TaxCurrencyCode = SAR) contains two TaxTotal nodes with the same TaxAmount. After the reverted commit was deployed, ZATCA started returning: [202] BR-KSA-EN16931-09 : Only one tax total (BG-22) without tax subtotals (BG-23) must be provided when tax currency code is provided. opw-6409881 Forward-Port-Of: odoo/odoo#284676
The French PDP registration wizard no longer shows “(Production)” when the system is already in production mode. This avoids redundant wording and makes the registration screen clearer for users.
Original PR description
It makes no sense to mention (Production) on pdp registration wizard when you are in prod mode Forward-Port-Of: odoo/odoo#280360
Content typed with a phone or other virtual keyboard in HTML note fields is now properly saved when users submit a form immediately. This prevents notes such as scheduled activity details from being lost when the field still has focus.
Original PR description
On a virtual keyboard the editor handles typing as a composition and only commits its history step on a later interaction. The html field marks itself dirty from that step, so right after typing the…
On a virtual keyboard the editor handles typing as a composition and only commits its history step on a later interaction. The html field marks itself dirty from that step, so right after typing the field is not dirty. On save the form calls commitChanges, but it only writes the value when the field is dirty, or on an urgent or inline-style save. None of those hold for a plain sanitized html field, so the typed content is not written to the record. Finalize the pending composition in commitChanges by calling the editor _compositionStep() before that check. commitChanges is the single point every save path goes through, so committing the pending step there marks the field dirty and the content is saved. It does nothing on a physical keyboard, where the composition flag is cleared after each keystroke. Steps to reproduce: 1. Open the Sales app and a quotation. 2. In the chatter, click Activities then Schedule Activity. 3. On a phone, or a browser with a virtual keyboard, fill Summary, tap the note field and type a note. 4. Tap Schedule while the note field still has focus. 5. Open the created activity. => the note is empty Ticket [link](https://www.odoo.com/odoo/project.task/6331719) opw-6331719 Forward-Port-Of: odoo/odoo#274465
When time off accrual allocations are created for a company, department, or employee category with a start date in the past, employees now receive the days they should have already accrued. This prevents missing leave balances and makes bulk allocations behave consistently with individual employee allocations.
Original PR description
**Problem:** Creating an accrual allocation for a whole company, a department or an employee category, with a plan that started in the past, does not grant the employees the days they should already…
**Problem:** Creating an accrual allocation for a whole company, a department or an employee category, with a plan that started in the past, does not grant the employees the days they should already have accrued. Doing the same allocation employee by employee works. **Steps to reproduce:** 1. Create a monthly accrual plan (e.g. 2 days, gained at the end of the period) 2. Create an accrual allocation for a whole company/department with a start date a few months in the past, and validate it 3. Observe each employee only gets the current period **Current behavior:** The child allocations only get the current period; the backlog between the start date and today is lost. **Expected behavior:** Each employee is back-filled for every elapsed period, exactly like a single-employee allocation. **Cause of the issue:** The single-employee flow runs `_onchange_date_from`, which resets `lastcall` to the allocation start date so `_process_accrual_plans` back-fills every elapsed period. That onchange returns early when there is no `employee_id`, which is always the case for company/department/category allocations, so the parent's `number_of_days` preview stays 0. The per-employee children are then created through `create`, where `_add_lastcalls` sets `lastcall` to the current period; the subsequent accrual run therefore only grants the current period and drops the whole backlog. **Fix:** Restart the child accrual from the allocation's start date before processing it, the same way the single-employee onchange does, so the plan is evaluated period by period for every employee. This is scoped to company/department/ category modes because employee-mode multi allocations already carry the value computed on the parent and must keep it. opw-6308064 Forward-Port-Of: odoo/odoo#272152
Checkout now detects carts that become zero-priced because of country-based pricing rules while zero-priced product sales are blocked. Instead of trying to complete the order and timing out, customers are sent back to the cart with a clear warning so they can resolve the issue.
Original PR description
When 'Prevent Sale of Zero Priced Product' is enabled and a country-group pricelist prices a product at 0 once the customer's country becomes known during checkout, the cart total becomes 0. The 'free order' branch then validated the order at the payment step, which could hang and time out for a cart that is not actually sellable. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The inventory report now keeps its table layout aligned when warehouse locations are grouped. This prevents missing gridlines and broken borders in printed PDF reports, making inventory counts easier to read and share.
Original PR description
When new columns were added to the stock inventory report, the location grouping row was not updated. This results in mismatched column counts, causing missing gridlines and broken borders in the PDF output Fixed by ensuring the location row's column count matches the header <img width="603" height="200" alt="image" src="https://github.com/user-attachments/assets/86872bee-f315-4bdd-b3f2-a525e3bb5fe0" /> ### Steps to reproduce: - Ensure warehouses are activated in the settings - Go to Barcode -> Count Inventory - Add a Product - Select the gear Icon then "Print Inventory" - You will notice that the location row has missing gridlines opw-6307728
Uruguay electronic invoicing now uses the exchange rate saved on the original invoice instead of recalculating it later. This prevents mismatches on credit notes or references when exchange rates are updated after an invoice is posted.
Original PR description
18.0 introduced an `invoice_currency_rate` field, storing the currency rate used for each invoice. Currently `_l10n_uy_edi_get_used_rate` uses the `currency.convert()` method using the date and company of the invoice. This is vulnerable to inaccuracy if the currency rates are changed after the invoice posting. For example, if an invoiceis posted and the currency rates are updated, the invoice will use the old rate, while `_l10n_uy_edi_get_used_rate` will return the new one. In the case of the customer in the related ticket, this caused their credit note reference currency rate to mismatch with the one on the invoice. This PR changes the `currency.convert()` call to fetching and inverting the `invoice_currency_rate` field. This ensures the returned value will match the one used for the invoice. opw-6456867
The Guatemala localization now applies the updated tax treatment for regular gasoline containing 10% alcohol. This ensures new customers receive the correct tax setup where only 90% of gallons are taxable under the new government rule.
Original PR description
Starting August 22nd Regular Gasoline is changing to a version that contains 10% alcohol. Because of this, the government is only taxing 90% of the gallons since the 10% alcohol portion is exempt. This will update COA for new customers, any current dbs who need to use the new formula can manually update theirs to include the * 0.9 portion. task-6483303
This fixes a configuration typo that prevented two important accounting report templates from being properly protected against deletion. The change helps keep standard vendor bill and invoice reports available and reduces the risk of accidental removal.
Original PR description
On `ir.actions.report` we want to block the unlinking of specific reports in odoo. However, when the list was created a comma was missed between `action_account_original_vendor_bill` and `account_invoice_without_payment` which means we were actually protecting against people unlinking `action_account_original_vendor_billaccount_invoice_without_payment`. Adding in that comma will allow these two records to be properly protected. task-none Forward-Port-Of: odoo/odoo#283323
Fixes an issue where automated activity creation could cause new time off requests to show 0 days or 0 hours, even when valid dates were entered. The system now recalculates the duration after the request is created, helping managers and employees see accurate leave balances and approvals.
Original PR description
Current behavior: -- With an active automation rule (base_automation) whose action creates an activity (e.g. a "Time Off Approval" activity) on hr.leave creation, every newly created time off request…
Current behavior: -- With an active automation rule (base_automation) whose action creates an activity (e.g. a "Time Off Approval" activity) on hr.leave creation, every newly created time off request has a duration of 0 days / 0 hours, regardless of the requested dates. Expected behavior: -- The leave duration is computed from the requested dates, unaffected by the presence of an activity-creating automation rule. Steps to reproduce: -- - Create an automation rule on hr.leave, trigger "On Creation & Update". - Add an action that creates an activity (type "Time Off Approval"). - Create any time off request for an employee with a working schedule. - The request shows a duration of "0 days" (or "0 hours"). Cause of the issue: -- When an automation with a "Create Activity" action runs on leave creation, base_automation schedules the activity after the record is created. Creating the activity reads the leave record, forcing an early flush of its pending computes. At that point date_from/date_to are not yet settled, so the duration compute (number_of_days/number_of_hours) reads empty dates and stores (0, 0). As these are stored fields, they are marked done and never recompute. Fix: -- In create(), after the record is created and its dates are settled, recompute the duration explicitly so any zero stored by an early flush is overwritten with the correct value. opw-6235902 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr