Thursday, September 17, 2026
36 changes · 19.0
Resolved issues and error corrections
This fixes a Safari-specific issue where replacing selected text at the start of an editable note could delete the selection without inserting the typed character. Users can now reliably select text and type over it in the HTML editor, avoiding lost input while editing notes or other rich text content.
Original PR description
When using Safari, if the first character of the editable is selected and a character is pressed, the selection content is removed, but the character is not inserted. It seems that Safari does not trigger the actual `input` event, nor its native behavior, if the initial anchor node is detached from the DOM after `beforeinput`. This commit avoids this issue by preventing Safari from proceeding with the insertion right after the deletion by instead re-triggering the `insertText` command. Steps to reproduce: - Use Safari - Go to a To do note - Select the first word - Press a letter => The first word was deleted but the letter was not inserted. task-6445669 Forward-Port-Of: odoo/odoo#281223
The Time Off Ledger now shows expected hours based on each employee's actual weekday schedule instead of using a weekly average. This gives managers and HR teams more accurate reporting for employees with shorter or longer scheduled days, such as reduced Friday hours.
Original PR description
The Time Off Ledger report shows the same "Expected Hours" value for every working day of an employee, even when their working schedule assigns different hours to different weekdays. ### Steps to…
The Time Off Ledger report shows the same "Expected Hours" value for every working day of an employee, even when their working schedule assigns different hours to different weekdays. ### Steps to reproduce: 1) Install hr_holidays_attendance. 2) Create/assign a working schedule where daily hours vary across the week (e.g. Monday-Thursday 8.5h, Friday 5h). 3) Go to Attendance > Reporting > Time Off Ledger, filter on that employee. ### Observed behavior: "Expected Hours" is identical on every day (the schedule's average hours/day), regardless of which weekday the row is for. ### Expected behavior: "Expected Hours" should match the hours actually scheduled for that specific weekday, so a shorter Friday shows less than a full Monday. ### Root cause: `Time off Ledger` is a SQL view. Its `_select()` builds `expected_hours` from `rc.hours_per_day` at [1], i.e. `resource.calendar.hours_per_day`, a field explicitly labelled **"Average Hour per Day"** and is computed via [_get_hours_per_day] as `hours_per_week / days_per_week`, a single flat value for the whole calendar. The per-weekday hours are available in `resource.calendar.attendance` it stores `dayofweek`, `hour_from`/`hour_to` and a computed `duration_hours` per line, and can legitimately differ per weekday (e.g. Monday 8h vs Friday 4h) at [2]. The report's own [_cte_cal_workday()] CTE already reads this table to decide whether a weekday is a working day (`cal_workday`, joined as `cw` in `_from()`), but discarded the actual hours, keeping only `(calendar_id, dayofweek)`. `_select()` then fell back to the calendar-wide average via `rc.hours_per_day` instead of the per-day value. Example: calendar with Monday 8h and Tuesday 4h -> `hours_per_day` averages to 6h; the report showed 6h on both Monday and Tuesday instead of 8h and 4h respectively. [1]- https://github.com/odoo/odoo/blob/5568f6e472e2e53bc2931e744421015b0f0f3550/addons/hr_holidays_attendance/report/hr_leave_attendance_report.py#L256 [_get_hours_per_day]: https://github.com/odoo/odoo/blob/5568f6e472e2e53bc2931e744421015b0f0f3550/addons/resource/models/resource_calendar.py#L703-L707 [2]- https://github.com/odoo/odoo/blob/5568f6e472e2e53bc2931e744421015b0f0f3550/addons/resource/models/resource_calendar_attendance.py#L15-L31 [_cte_cal_workday()]: https://github.com/odoo/odoo/blob/5568f6e472e2e53bc2931e744421015b0f0f3550/addons/hr_holidays_attendance/report/hr_leave_attendance_report.py#L82-L89 ### Fix: `_cte_cal_workday()` now aggregates `SUM(duration_hours)` per `(calendar_id, dayofweek)` from `resource_calendar_attendance`, instead of just selecting the distinct keys. `_select()` reads this per-weekday value (`cw.hours_per_day`) instead of the calendar-wide `rc.hours_per_day` for both `expected_hours` and `difference_hours`. **opw-6425167**
Point of Sale product search now includes products whose variant has a single attribute value, such as Size M. This helps cashiers find the right item more reliably and avoids missed products during checkout.
Original PR description
In the POS search bar, searching for an attribute value (e.g., "M") returned products with multi-value attributes (e.g., Size: M - L), but omitted products with single-value attributes (e.g., Size: M). This occurred because backend `_compute_display_name` filters out single-value attribute lines from variant display names. As POS `searchString` relied on `display_name`, `name`, `default_code`, and `barcode`, the attribute value name was missing from single-value variant search strings. task-id: 6431718 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix prevents Kenyan eTIMS invoice numbering from moving backwards after certain failed submissions. It reduces the risk of two invoices sharing the same official number and receiving incorrect receipt details, helping maintain accurate tax filings.
Original PR description
Give an eTIMS invoice number back to the sequence only when the failing call is the one that took it, and only when it is still the last one handed out. When sending a customer invoice fails with anything other than a timeout, the number is given back so that it is not consumed for nothing. current issue: - send an invoice, let it time out, so it keeps number N - send other invoices, so the sequence moves past N - send the first one again and let it fail with a non-timeout error - the sequence drops by one and the next invoice sent reuses a number Both documents then sit under the same number. On its next attempt the one that was never accepted finds the other one's filing through selectInvoiceDetails and copies its receipt details. opw-6502563 Forward-Port-Of: odoo/enterprise#131106 Forward-Port-Of: odoo/enterprise#129994
Creating an appointment event from the calendar now uses the exact time slot selected by the user instead of defaulting to the appointment type duration. This prevents unexpected event lengths and helps scheduling reflect the intended booking time.
Original PR description
When creating an event from the calendar view of an appointment type, the duration of the event was set to the duration of the appointment, therefore bypassing the slot we drew. This commit fixes this behavior by giving the priority to the end date of the drawed slot. Task-6412432
This fixes an error that could block users from opening the rental schedule for products whose variants had been deleted. The schedule now opens without trying to preselect a missing product variant, improving reliability for rental product management.
Original PR description
Currently, an error occurs when opening the Rental Order Lines Schedule view. **Steps to Reproduce:** - Install the `sale_stock_renting` module. - Go to `Settings` and enable `Variants`. - Go to…
Currently, an error occurs when opening the Rental Order Lines Schedule view. **Steps to Reproduce:** - Install the `sale_stock_renting` module. - Go to `Settings` and enable `Variants`. - Go to `Rental` > `Products` and create a product. - In the `Attributes & Variants` tab, add an attribute with two values and save. - Delete all variants using the `Variants` smart button or from `Inventory` > `Products` > `Product Variants`. - Return to the `Product` and click the `In Renting smart button`. `IndexError: list index out of range` When a product template is created, a product variant is automatically generated if the product has no attributes. Deleting this variant also deletes the product template [1]. By explicitly creating attribute values, a dynamic product variant is generated. Deleting this variant does not delete the product template because of the dynamic attribute [2]. When the In Renting button is clicked, it it going to open the rental order lines Schedule view and adds default_product_id to the action context. Since no product variants exist anymore, accessing the default product variant raises an error [3]. This commit ensures that default_product_id is not added to the context when the product template has no product variants [1]: https://github.com/odoo/odoo/blob/1f70b81eeebcc62b64c18772915e2f4696e189e7/addons/product/models/product_product.py#L486 [2]: https://github.com/odoo/odoo/blob/1f70b81eeebcc62b64c18772915e2f4696e189e7/addons/product/models/product_product.py#L478-L480 [3]- https://github.com/odoo/enterprise/blob/98ec35f3f17a2b706ad4fcc4d6161d12d05eadfd/sale_renting/models/product_product.py#L69 [4]: https://github.com/odoo/odoo/blob/942d3892c33e7ccdcfec260c3da37095069b699e/addons/stock/models/product.py#L635-L643 [5]: https://github.com/odoo/odoo/blob/942d3892c33e7ccdcfec260c3da37095069b699e/addons/stock/models/product.py#L601-L612 sentry-7637715724
The Colombian Libro Diario report now opens correctly when comparison options are enabled. It also includes journal entries without partners, helping ensure legally required accounting information is not omitted from the report.
Original PR description
## [FIX] l10n_co_reports: fix UNION ALL crash with multiple column groups ### Issue: Any comparison mode (Period, Analytic, etc.) in the Libro Diario raises an Odoo Server Error ### Cause: Each…
## [FIX] l10n_co_reports: fix UNION ALL crash with multiple column groups
### Issue:
Any comparison mode (Period, Analytic, etc.) in the Libro Diario raises an Odoo Server Error
### Cause:
Each sub-query in the `UNION ALL` had its own `ORDER BY` SQL only allows one global `ORDER BY` on a `UNION ALL`, or parentheses around each query — neither was the case
### Steps to reproduce:
- Install `l10n_co_reports` and `accountant`
- Enable Developer Mode in Settings
- Open the Libro Diario report and click the gear icon (top right)
- In the Options tab, enable Period Comparison
- Enable the Comparison for the Previous Period
Before the fix, an error is raised
------------------------------
## [FIX] l10n_co_reports: include partnerless entries in Libro Diario
### Issue:
Journal entries without a partner are excluded from the report but are legally required to appear
### Cause:
`_get_domain` called `super()` which adds `('partner_id', '!=', False)` to the domain, filtering out all partnerless entries
The SQL query also used a `JOIN` instead of `LEFT JOIN` on `res_partner`, excluding lines with no partner at the DB level
### Notes:
`NULL` values for `partner_name` or `line_label` caused the JS to hide the corresponding column headers
`header.js` matches columns to their header by `column_group_index`/`expression_label` and skips `None` values
Fixed by using `COALESCE` to return an empty string instead
### Steps to reproduce:
- Install `l10n_co_reports` and `accountant`
- Create a Journal Entry without a partner or label
- Open the Daily Journal Report
Before the fix, the entry doesn't appear
After the fix, check that PARTNER and LABEL headers are visible
opw-6430728
Forward-Port-Of: odoo/enterprise#129674Submitting a draft Denmark VAT report no longer triggers an error. The report now passes the needed prior settings when calculating VAT lines, helping Danish users complete VAT submissions reliably.
Original PR description
Currently, an error occurs when the user submits the draft Denmark VAT report. ``` TypeError: AccountReport.get_options() missing 1 required positional argument: 'previous_options' ``` When the user submits the draft Denmark VAT report, it gets the calculated lines of the current report by calling get_options method. However, get_options() requires the previous_options argument [1]. Since this argument is not passed [2], it raises the error. This commit ensures that an empty dictionary is passed as the previous_options argument when getting the report lines. [1]- https://github.com/odoo/enterprise/blob/e2aa3073dafa9377654c761e371a660415db1e88/account_reports/models/account_report.py#L2126 [2]- https://github.com/odoo/enterprise/blob/e2aa3073dafa9377654c761e371a660415db1e88/l10n_dk_reports/wizard/tax_report_wizard.py#L216 sentry-7380293202
This fix prevents website visitors from seeing the “Ask a Human” option when the selected live chat channel has no available human agents, avoiding a dead-end support experience. It also moves the related notice into the chat conversation so it remains visible even when the chat window overlaps on-screen notifications.
Original PR description
This commit aims to adapt the tests for this bugfix. How to reproduce: - Create livechat channel with 0 members. - In Rules, add an agent to the channel. - In website configuration, set the created…
This commit aims to adapt the tests for this bugfix. How to reproduce: - Create livechat channel with 0 members. - In Rules, add an agent to the channel. - In website configuration, set the created livechat channel as the default live chat channel. - Go to website, click bottom right bubble to start livechat. - Resize the Window on your screen so that the chat window obscures the top right of the page (where you get notifications) - Press 'Ask a Human' Current behavior: - 'Ask a Human' is available (despite having 0 agants in the channel -> will never reach a human) - Notification is obscured by the chat window (when it overlaps with the top right) Expected: - Remove 'Ask a Human' button, when there are no human (agents) in the livechat channel - Change the notification to a chat message. enterprise PR: https://github.com/odoo/enterprise/pull/125673 task: 6148037 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
The live chat now hides the “Ask a Human” option when no human agents are available, preventing visitors from requesting an unreachable handoff. If a handoff cannot happen, the message appears directly in the chat instead of as a notification that may be hidden behind the chat window.
Original PR description
How to reproduce: - Create livechat channel with 0 members. - In Rules, add an agent to the channel. - In website configuration, set the created livechat channel as the default live chat channel. - Go to website, click bottom right bubble to start livechat. - Resize the Window on your screen so that the chat window obscures the top right of the page (where you get notifications) - Press 'Ask a Human' Current behavior: - 'Ask a Human' is available (despite having 0 agants in the channel -> will never reach a human) - Notification is obscured by the chat window (when it overlaps with the top right) Expected: - Remove 'Ask a Human' button, when there are no human (agents) in the livechat channel - Change the notification to a chat message. task: 6148037 community PR: https://github.com/odoo/odoo/pull/278834
The calendar view now updates properly when users resize their browser or switch between mobile and desktop layouts. This prevents the wrong filter panel from showing on small screens and avoids the calendar disappearing after returning to a wider view.
Original PR description
See commits
Removed an unused line in the maintenance request creation process that had no effect. This keeps the maintenance code cleaner while preserving the same behavior for users, since teams are already assigned automatically when requests are created.
Original PR description
The create method assigned request.maintenance_team_id to itself, a no-op that reads and writes the same value and has no effect. This line originally set the team from the equipment as a fallback when creating a request. PR #196181, while refactoring mail alias handling from equipment category to team, replaced that assignment with a self-assignment, turning it into dead code. maintenance_team_id is a required field and is already a stored compute depending on equipment_id, so it is computed correctly on create without this line. Removing it has no functional impact.
This change reverts a recent update that made vendor bill pages load much more slowly when checking for duplicate receipts. It restores the previous behavior in the stable version to protect day-to-day accounting performance while a fuller solution is handled separately.
Original PR description
This reverts commit 391278237dc4fdbf48039bb269f1377d83bbc54d.
It introduced a performance regression.
Go to Accounting > Vendors > Bill
On odoo.com:
| | Time | Query plan |
|--------|--------|--------|
| Before | ~600ms | https://explain.dalibo.com/plan/3ge1afa2aa632be9|
| Now | 44s | https://explain.dalibo.com/plan/99e847h8d1c38dfd |
The reason is the condition `.move_type in ('in_invoice', 'in_refund')` which was changed to include 'in_receipt':
`.move_type in ('in_invoice', 'in_refund', 'in_receipt')`
Because of that, the query can no longer use the partial index `_duplicate_bills_idx` because it doesn't include 'in_receipt'.
We are reverting the commit in stable. We keep it and adapt the index in master.
task-6577249
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prEmployees can now split an already approved expense even when it has a receipt attached. The fix prevents an erroneous access warning during the split while keeping the normal restriction that blocks adding new receipts to approved expenses.
Original PR description
Steps to reproduce: ---------------------------------------- 1. Install the Expense module. 2. Create an expense with a receipt/attachment and approve it. 3. Try to split the approved expense using…
Steps to reproduce: ---------------------------------------- 1. Install the Expense module. 2. Create an expense with a receipt/attachment and approve it. 3. Try to split the approved expense using the "Split Expense" button. Observation: ---------------------------------------- An Access Error is raised: "You can't add attachments to an expense once it has been approved." Issue: ---------------------------------------- When splitting an approved expense, Odoo duplicates the original expense into multiple split parts. Since the original expense is already in the `approved` state, the newly created duplicate records also have `state == 'approved'`. During the split process, Odoo copies the attachments from the original expense to the newly created split records: https://github.com/odoo/odoo/blob/e696bc516c97bc7157d52c5dba0b42e7cb8bab48/addons/hr_expense/wizard/hr_expense_split_wizard.py#L59-L61 Because `copied_expense.state` is 'approved', the create method of `ir.attachment` triggers an AccessError via the security validation https://github.com/odoo/odoo/blob/e696bc516c97bc7157d52c5dba0b42e7cb8bab48/addons/hr_expense/models/ir_attachment.py#L23-L27 Solution: ---------------------------------------- Bypass the attachment restriction if the creation is initiated from the split wizard. It only bypasses the validation during the split operation, preserving the security constraints for normal user uploads to approved expenses opw-6373579
This change updates an internal automated test for the Events app after a small increase in database activity. It helps keep quality checks accurate without changing how users experience the product.
Original PR description
Description of the issue/feature this PR addresses: Updates the queryCount assertion since the amount of queries increased by one. runbot-944484
Point of Sale order reports now align their totals with the actual order total when taxes are rounded per tax group. This prevents small reporting discrepancies, such as one-cent differences, and improves trust in sales analysis figures.
Original PR description
Steps to reproduce: - Install `Point of Sale` - Settings > Invoicing > Taxes > Rounding Method > choose 'Round Per Tax' - Create 3 products - First one costs 16.73 and has 8% sales tax - Second one…
Steps to reproduce:
- Install `Point of Sale`
- Settings > Invoicing > Taxes > Rounding Method > choose 'Round Per Tax'
- Create 3 products
- First one costs 16.73 and has 8% sales tax
- Second one costs 7 and has no applied sales tax
- Finally, a third one that costs 25.99 and has 8% sales tax too
- Create an order with the following quantities
- 3 of the first product
- 3 of the second product
- 2 of the third one
_Notice that the total is 131.34, but if you sum the individual lines the total 131.35_
- Go to Reporting > Orders Analysis > pivot view
> Notice that the total price is 131.35, resulting in a discrepancy of 0.01.
### Cause of Issue:
The SQL query that shows the report calculates the prices on a "Round per Line" basis, since it retrives each individual line on its own from `pos_order_line` table https://github.com/odoo/odoo/blob/a236f67776616f6facdefb0117a6ffdde9b7c84c/addons/point_of_sale/report/pos_order_report.py#L72 If the order/invoice has products with different taxes, this is bound to cause discrepancies.
### Fix:
Since it is important to match the total value of the order to the total value in the report, while also retriveing the lines one by one, the safest approach was to calculate the tax for each tax group "_round per tax_" and compare it to the tax we get usually by just summing, and if there are any differences we subtract them from the last line of that tax group.
### Drawback:
This might cause some small price differences (in the price's 0.01 places) in different orders, but the display of a correct total value is of more importance.
opw-6141218This fixes an issue where some grid-based views could appear empty after the page refreshed or rebuilt part of the interface. The grid now recalculates immediately when its scroll area changes, so users see the expected rows and columns without needing to scroll or resize the window.
Original PR description
Manual fw-port of https://github.com/odoo/odoo/pull/281721 See commit messages for details. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The French PDP settings screen now uses the correct wording for an option related to e-reporting and sending data to the public portal. This helps users understand the setting accurately and avoid making configuration choices based on misleading text.
Original PR description
When we removed the pilot phase setting from the view, we changed that setting to only mean Enable e-reporting. But that's a mistake. In fact people are also choosing not to send to the PPF, so the previous sentence was still right. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288518
Generic demo leave types no longer carry a country setting, because they are not intended to represent any specific national policy. This prevents demo data from blocking legitimate company country configuration while keeping protections in place for real leave records.
Original PR description
_check_country_change_holidays constraint blocks writes to res.company.country_id whenever hr.leave/hr.leave.allocation records exist whose leave type country differs from the new company country. Unset country_id on the generic holiday_status_* demo leave types they are not meant to represent a specific country's holiday policy, so they should not carry a country at all. With country_id set to False, they no longer participate in the country-change constraint. The constraint keeps protecting real country changes on business data, while no longer blocking legitimate demo installs where we configure the main company with our country but rely on demo data for dev instances. Related to https://github.com/odoo/odoo/pull/277346
Theme updates now correctly remove website-specific copies and related dependent views when a theme view is no longer included. This prevents update failures that could block businesses from applying website theme changes or upgrades.
Original PR description
When a record disappears from a theme's data files, updating that theme deletes its per-website copies. The context built for `copy_ids` in `_process_end_unlink_record` used the literal `'MODULE_UNINSTALL_FLAG'` instead of the constant, and passed it as a positional dict, which replaces the whole context rather than extending it. `ir.ui.view.unlink` therefore did not cascade to the inheriting views and the `inherit_id` foreign key refused the deletion, aborting the theme update. Reported by: https://github.com/odoo/odoo/issues/286171 Forward-Port-Of: odoo/odoo#288548
Products with multiple variants now show the add-to-cart option when at least one variant is available to buy. This prevents shoppers from seeing an item as sold out just because the first variant has no stock, helping avoid missed sales.
Original PR description
Steps to reproduce: =================== 1. Create a storable product with two variants and untick "Continue selling when out-of-stock". 2. Leave the first variant at 0 in stock, put some stock on the…
Steps to reproduce: =================== 1. Create a storable product with two variants and untick "Continue selling when out-of-stock". 2. Leave the first variant at 0 in stock, put some stock on the second one. 3. try the "Add to Cart" option of the shop page. => The product has no add to cart button, as if it were sold out, while its second variant can be bought. Reordering the variants so that the one in stock comes first brings the button back. Root cause: =========== `product.template._is_sold_out()` delegates to `product_variant_id`, which is `product_variant_ids[:1]`, so a whole template is declared sold out on the sole basis of its first variant. `_website_show_quick_add()` then hides the button for the template, whatever the stock of the other variants. The helper has looked at that single variant since it was written: - [1] added it to hide the button of sold out products. Fix: ==== Consider the template sold out only when all of its variants are. The check stops at the first variant in stock, so a product that can be bought still costs a single stock lookup. [1]: https://github.com/odoo/odoo/commit/4ae197cc770cec2058e1f113209c4f93a4a0f4b2 opw-6520052 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286781
Fixed an issue that could prevent Arabic-English GCC invoice PDFs from generating when invoices included section or note lines. This helps users print and preview affected invoices without encountering an internal server error.
Original PR description
## Problem When rendering PDF invoices that contain **section** or **note** lines, the `l10n_gcc_invoice.arabic_english_invoice` QWeb template raises: ``` TypeError: argument of type 'bool' is not…
## Problem When rendering PDF invoices that contain **section** or **note** lines, the `l10n_gcc_invoice.arabic_english_invoice` QWeb template raises: ``` TypeError: argument of type 'bool' is not iterable ``` This happens because `account.move.line` records of type `line_section` or `line_note` have `name = False`. The template evaluates `arabic_name not in line.name` (and the same for `english_name`), which fails because Python cannot apply the `in` operator on a boolean value. ## Fix Add a `line.name and` guard before each `not in` check: ```xml <!-- Before --> <span t-if="arabic_name not in line.name" .../> <span t-if="(english_name != arabic_name) and (english_name not in line.name)" .../> <!-- After --> <span t-if="line.name and arabic_name not in line.name" .../> <span t-if="line.name and (english_name != arabic_name) and (english_name not in line.name)" .../> ``` ## Steps to reproduce 1. Install `l10n_gcc_invoice` on an Odoo 16.0 instance. 2. Create a customer invoice and add a **Section** line. 3. Print/preview the invoice PDF. 4. Observe `Internal Server Error` / `TypeError: argument of type 'bool' is not iterable`. Forward-Port-Of: odoo/odoo#278887 Forward-Port-Of: odoo/odoo#267147
Financial reports now display and scroll correctly on iPhones and iPads, preventing the scrollbar from appearing behind report content. This improves usability for mobile users viewing long accounting reports without changing the desktop experience.
Original PR description
### Issue: On iPhone and iPad (reduced window), the scrollbar is hidden behind the report on long scrollable pages This affects all browsers on iOS and iPadOS — Apple forces the use of WebKit (its…
### Issue: On iPhone and iPad (reduced window), the scrollbar is hidden behind the report on long scrollable pages This affects all browsers on iOS and iPadOS — Apple forces the use of WebKit (its rendering engine) on all of them, including Chrome and Firefox ### Steps to reproduce (on iPhone): - Install `l10n_es` (contains scrollable reports by default) - Switch to the ES company - Open the Tax Report and try to scroll Before the fix, the scrollbar renders behind the report content ### Cause: `o_content` was added unconditionally in commit https://github.com/odoo/enterprise/commit/d60ea6e0f3245e394fd0cee5f22f52aff2a79e2e But its role differs between desktop and mobile, and its presence on mobile triggers a WebKit compositor bug On desktop, `o_content` is required: `.o_action` stays `overflow: hidden`, so only `.o_content` (`overflow: auto`) can scroll the report Per the CSS flexbox spec, a flex item won't shrink below its content size unless its `overflow` is not `visible` Without `o_content`, the div keeps `overflow: visible`, refuses to shrink, overflows `.o_action`, and the excess is silently clipped — content becomes unreachable, not just visually different On mobile, the framework flips scroll responsibility to `.o_action` (`overflow: auto`) and forces `.o_content` back to `overflow: initial` `o_content` is therefore not needed on mobile On WebKit (iOS/iPadOS), keeping `o_content` on mobile is harmful: it sits as a non-scrolling `position: relative` node between the real scroll ancestor (`.o_action`) and descendants that require special compositing — the sticky `thead` and the fixed-position mobile chatter This configuration causes WebKit's compositor to miscalculate the Root layer bounding box This geometry mismatch is the most likely explanation for why the native scroll indicator renders behind the report instead of on top of it Removing `o_content` on mobile avoids this node entirely and restores correct compositor geometry ### Notes: Tested on Android (Blink) with and without `o_content`: no visual difference and identical compositor layer geometry confirmed via Chrome DevTools — no regression introduced The `padding-bottom` on `.o_account_report_scroll_container` is unrelated — it is applied unconditionally and exists for a separate bug (last row clipped on scroll) opw-6191827
Argentinian export invoices now send item quantities with the correct decimal precision instead of always rounding to two decimals. This prevents valid invoices with small fractional quantities from being rejected by ARCA after upgrading to Odoo 19.0.
Original PR description
### Problem `l10n_ar_edi` reports WSFEX / WSBFE line quantities (`Pro_qty`) truncated to 2 decimals in 19.0, no matter what precision the database has configured. `_get_line_details()` reads the…
### Problem
`l10n_ar_edi` reports WSFEX / WSBFE line quantities (`Pro_qty`) truncated to 2 decimals in 19.0, no matter what precision the database has configured.
`_get_line_details()` reads the precision like this:
```python
uom_precision_digits = min(self.env['decimal.precision'].precision_get('Product Unit of Measure'), 2)
```
In 19.0 that `decimal.precision` record was renamed to **`Product Unit`** (`uom/data/uom_data.xml`, `decimal_product_uom`), so the lookup matches no record. `precision_get` does not raise in that case, it falls back to a default of 2 (`base/models/decimal_precision.py`: `return res[0] if res else 2`), so `min(2, 2) = 2`.
The rename was applied everywhere else — `account.move.line.quantity` already declares `digits='Product Unit'`. Only this lookup kept the old name.
### Impact
A quantity of `0.042` is sent as `0.04`. ARCA rejects the invoice with **error 1815** (item math inconsistency: `unit price x quantity - discount` no longer matches the item total), which blocks export invoicing completely.
It only shows up after upgrading to 19.0: on 18.0 the record still had the old name, so the lookup worked and the configured precision was used.
On our hosted fleet we identified **67 Argentinian databases** that use WSFEX or WSBFE and have a unit precision above 2. Eight of them already run 19.0 and are affected today; the rest will hit the same rejection as they upgrade.
### Fix
Use the current record name, and raise the cap to **6**, the maximum `Pro_qty` accepts according to the WSFEX developer manual ([V3.1.1](https://www.afip.gob.ar/ws/documentacion/manuales/WSFEX-Manualparaeldesarrollador_V3.1.1_ARCA.pdf), p. 15).
### Test plan
`l10n_ar_edi/tests/test_fex.py` adds `test_ar_edi_wsfex_pro_qty_decimal_precision`, covering three cases:
| `Product Unit` | quantity | expected `Pro_qty` |
|---|---|---|
| 3 | 0.042 | `0.042` |
| 2 | 0.042 | `0.04` |
| 8 | 0.1234567 | `0.123457` (capped at 6) |
It does not go through the ARCA mock: it calls `_get_rounded_base_and_tax_lines()` and `_get_line_details()` directly.
### Note
Supersedes #130582 by the same author, which carried the same one-line fix without a test. Please review this one instead.
### Left out on purpose
`price_precision_digits` in the same method caps the unit price at 3 digits, which does not come from the specification either. That lookup still uses a record name that exists (`Product Price`), so there is no regression, and we have no rejection reported because of it. Changing it would widen the scope of a fix that has a concrete incident behind it, so it is left untouched here.This update ensures the Turkish Nilvera e-Dispatch module installs reliably by explicitly including a required inventory accounting component. It prevents installation failures in certain test or setup scenarios without changing day-to-day user workflows.
Original PR description
View 'l10n_tr_nilvera_edispatch.view_picking_form_inherit_l10n_tr_nilvera_edispatch' fails to install in single-module test skip auto_install because it depends on field stock.picking:country_code. That field is provided by module 'stock_account' which is not in the dependency path of the module. In normal install the module 'stock_account' is present through auto_install when both 'account' and 'stock' are installed. Adding the direct dependency on 'stock_account' is not a problem because the view crashes without it. 'stock_account' is available through the chain below. [l10n_tr_nilvera_edispatch] ──[depends]──> [stock] ──⚡[AUTOLOAD]──> [stock_account] [l10n_tr_nilvera_edispatch] ──[depends]──> [l10n_tr_nilvera] ──[depends]──> [l10n_tr] ──[depends]──> [account] ──⚡[AUTOLOAD]──> [stock_account] REF Runbot: https://runbot.odoo.com/odoo/error/946186 Forward-Port-Of: odoo/odoo#285633
A formatting mistake in an internal export check has been corrected so the system now reports the intended validation error instead of an unrelated technical failure. This makes export-related diagnostics clearer and helps avoid confusion during troubleshooting.
Original PR description
The format was broken. `'{}:{}' % it` → `TypeError` instead of `AssertionError`.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#288610Attendance approvers can once again adjust Extra Hours directly without changing an employee's check-in or check-out times. This restores the expected manager workflow for correcting overtime while keeping worked time calculations based on the actual attendance timestamps.
Original PR description
Issue: Attendance approvers cannot manually adjust the Extra Hours value anymore. The only available workaround is to change the check-in/check-out timestamps, although Extra Hours should be…
Issue: Attendance approvers cannot manually adjust the Extra Hours value anymore. The only available workaround is to change the check-in/check-out timestamps, although Extra Hours should be adjustable independently from the actually worked time. Steps to reproduce: - Enable overtime validation by manager in Attendances settings. - Configure an employee with the default overtime ruleset and a standard 40h working schedule. - Set the Attendance approver to the user used for the test. - Create an attendance that generates overtime, for example 08:00-20:00 on a regular 8h workday. - Open the attendance as the approver. - Try to edit Extra Hours without changing Check In or Check Out. - Approve the overtime and try to edit Extra Hours again. Cause: `validated_overtime_hours` was a readonly computed value based on linked overtime lines, while https://github.com/odoo/odoo/blob/ba708ca0cf926a5feaf4b1e50c7d0ead3db4391a/addons/hr_attendance/views/hr_attendance_view.xml#L112-L116 kept the form field readonly. The editable value had moved to `hr.attendance.overtime.line.manual_duration`, but the attendance level Extra Hours field no longer wrote back to it. Solution: Make `validated_overtime_hours` editable again and add an inverse function that writes manager edits to non-refused linked overtime lines' `manual_duration`. Keep `overtime_hours`/`duration` computed from the timestamps and rules, and keep the views editable only for managers while the overtime is not refused. Ignore the default `validated_overtime_hours = 0` sent by form creation, because creating an attendance through the UI does not accidentally override generated overtime, thus we needed to make it readonly when it doesn't have an id yet. opw-6271244 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The Norwegian tax report now lists tax code details in a consistent order. This prevents random test failures and helps ensure the generated eVAT report output remains reliable across different database execution paths.
Original PR description
The Norwegian tax report is built from ordered elements, but the summary detail per tax code is appended from a list that is quasi-directly calculated straight from PostgreSQL. The query does not request a specific result order causing indeterminism (it depends on the query plan chosen: hash vs. sort aggregate, parallel workers) when the whole XML tree is compared against a golden copy in tests. An explicit ORDER BY clause is added to the taxes query. The chosen key is the tax_code, because these can be casted for integer natural sort. The produced XML tree can be compared in its entirety without random failures. REF Runbot; https://runbot.odoo.com/odoo/error/939532 Forward-Port-Of: odoo/enterprise#131702 Forward-Port-Of: odoo/enterprise#131307
Uploading a new version of a shared document no longer creates a duplicate behind the scenes. This prevents upload failures for users managing files that are also linked to other records, such as products.
Original PR description
Uploading a new version of a document whose attachment is shared with another record (e.g. a product template) fails with duplicate key value violates unique constraint "documents_document_attachment_unique", key (attachment_id) already exists. `_documents_upload` first writes res_model/res_id of the linked record onto the new attachment, which triggers ir.attachment's auto-create-document logic and spawns a competing document. The version-upload's own write then tries to claim the same attachment, causing the constraint violation. Steps to reproduce: 1. Create a product template and add an attachment. 2. Go to the document->proucts. 3. Click on the attachment, "action" and then "manage version" 4. Upload a new document and you'll see the issue. Fix: pass no_document=True when writing res_model/res_id on the new attachment, so no competing document is created. opw-6360307
Middle-clicking a related field in the API documentation now opens the correct related model page instead of the current base model. This makes navigation more reliable for users consulting technical model information and prevents confusion when opening links in new tabs.
Original PR description
Steps to reproduce: * open api_doc * select a model (eg: project.task) * middle click on a many2one field (eg: stage_id) Observed behavior: The new tab is opened on the base model (project.task) instead of the comodel (project.task.type). This commit fixes the issue by using `t-att-href` to point to the correct model and `preventDefault` to avoid a full reload when clicking on a m2o.
Fixed an accounting issue where some batch payments could remain marked as sent even after the related bill and payment were paid. This keeps payment statuses aligned with the actual payment outcome and reduces manual follow-up for finance teams.
Original PR description
A payment on a journal without outstanding account has no journal entry, so it is matched as soon as it is paid. When the bill it pays is reconciled later on, _compute_state turns it to 'paid', but assigning a field from within a compute doesn't notify the fields depending on it: is_matched stays False and any batch payment holding that payment stays in 'sent' state. Mark is_matched for recomputation explicitly, as 18.0 already does since d8de234. Steps to reproduce: - On the Bank journal, leave the outstanding account empty on the outbound payment method line - Post a vendor bill - Register a payment on it - Put that payment in a batch payment and validate the batch - Create a MISC entry and reconcile the bill with it - Issue: bill and payment are 'Paid' but the batch stays 'Sent'.
This fixes an issue where saved filter settings could fail to load if extra spaces were present around stored text values. The change helps keep user filters working consistently and avoids avoidable errors when opening filtered views.
Original PR description
task-6578010 Forward-Port-Of: odoo/odoo#288528
The shop now blocks combo products from being added to the cart unless every required choice has been completed correctly. This prevents customers from checking out with incomplete combo selections, reducing incorrect orders and fulfillment issues.
Original PR description
Steps to reproduce: =================== 1. Publish a combo product whose choices each offer more than one item, so the combo configurator is shown. 2. On the shop, open the product and click "Add to…
Steps to reproduce: =================== 1. Publish a combo product whose choices each offer more than one item, so the combo configurator is shown. 2. On the shop, open the product and click "Add to Cart". 3. Select an item for one combo choice only. The dialog's "Add to cart" button stays disabled. 4. Remove the `disabled` attribute from that button with the browser developer tools and click it. => The combo is added to the cart with one of its choices unanswered, and the order can be paid in that state. Root cause: =========== The incomplete selection is only prevented on the client side, by disabling the button until every choice has been answered. Server side, `/website_sale/combo_configurator/update_cart` only rejects a completely empty selection, never that the selected items cover every choice. Fix: ==== Reject the request unless the selected combo items cover exactly the combo choices of the product. Comparing the set of choices rather than counting the items also rejects a selection answering the same choice twice while leaving another one unanswered. opw-6478837 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284704 Forward-Port-Of: odoo/odoo#283465
EU OSS sales are now reported correctly in French e-invoicing flows by treating them as non-French VAT while preserving the destination VAT on invoices and accounting records. This avoids compliance rejections caused by French reporting rules that only allow French VAT rates in this flow.
Original PR description
OSS sales are taxed in the customer's Member State but are not subject to French VAT. They must therefore be reported under TNT1, while the Flow 10 Schematron only accepts French VAT rates. Identify taxes generated for the EU OSS scheme through their OSS tag. Keep the destination VAT on the invoice and in accounting, but report the taxable base under TNT1 with a zero tax rate and amount. Continue rejecting unsupported rates for regular taxes and invalid OSS rates. no task id Forward-Port-Of: odoo/odoo#288013
The mail plugin now allows administrators to adjust how long authentication tokens remain valid, with a default duration of seven days. This reduces the need for users to sign in every day while keeping the tokens limited to Outlook-related access.
Original PR description
Purpose ======= Allow customizing the expiration time of tokens, so users don't need to login everyday in the plugin. This is customized with a system parameter, with a default of 7 days. Those tokens are limited to endpoints `auth="outlook"`. Task-6466253 Forward-Port-Of: odoo/odoo#287767
This fixes an issue where help text shown from spreadsheet actions could lose its intended formatting and appear as escaped text. Business users will see trusted help content rendered correctly, improving clarity when opening linked actions from spreadsheets.
Original PR description
Current behavior before PR: - `navigateTo` cleans up the action description using `JSON.parse(JSON.stringify(...))`. - This removes Owl's `markup()` wrapper from the `help` field. - As a result, the trusted HTML is converted to a plain string and gets escaped instead of being rendered. Desired behavior after PR is merged: - Pass the action description directly to doAction. - `_preprocessAction` already provides defensive fallbacks for individual fields, such as `action.domain || [] and action.display_name || action.name || "".` - Therefore, undefined fields or missing keys are handled safely without the need for the JSON round-trip. - This preserves the trusted markup in the help field and renders the HTML correctly. Task: [6428217](https://www.odoo.com/odoo/project/2328/tasks/6428217) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286191