Tuesday, September 1, 2026
75 changes · master
Resolved issues and error corrections
The Guatemala electronic invoicing localization now uses the updated tax calculation for regular gasoline that contains 10% alcohol. This matters because, from August 22, only 90% of the gallons are taxable under the new government rule for new customer chart of accounts setup.
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 Forward-Port-Of: odoo/enterprise#129483 Forward-Port-Of: odoo/enterprise#129412
Fixed an issue where timesheets could lose their connection to a replacement invoice after an invoice was reversed and recreated through a credit note. This helps ensure corrected invoices still show the right timesheet details for the related sales order, reducing billing confusion and manual cleanup.
Original PR description
### Description of the issue/feature this PR addresses: Fixes an issue where timesheets lose their invoice reference when reversing and re-creating an invoice via a credit note. ### Current behavior…
### Description of the issue/feature this PR addresses: Fixes an issue where timesheets lose their invoice reference when reversing and re-creating an invoice via a credit note. ### Current behavior before PR: When reversing an invoice tied to timesheets and creating a replacement via a credit note, the timesheets linked to the original invoice have their timesheet_invoice_id cleared. Because the modify_moves function builds the replacement invoice directly via copy_data()/create(), it bypasses the normal sale order invoicing flow. As a result, the unbilled timesheets are left permanently unlinked from the newly created invoice, leaving the new invoice with no reference to the timesheets linked to the original sale order. ### Desired behavior after PR is merged: When a replacement invoice is created, each timesheet is properly relinked to the corresponding line on the new invoice. This linkage matches on the sale order line (so_line) rather than line position, ensuring accuracy since line order and count are not guaranteed to be preserved between the original and modified invoices. opw-6449995 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285232 Forward-Port-Of: odoo/odoo#283104
Mobile invoice line cards now display the line description when no product is selected. This prevents blank cards and makes invoices easier to review on mobile devices.
Original PR description
Invoice lines can be created without a product. However, the mobile kanban view does not handle such lines properly and displays an empty card without a name or image. This commit adapts mobile kanban view to show invoice line's name when `product_id` is not set. Before | After -- | -- <img width="469" height="277" alt="image" src="https://github.com/user-attachments/assets/3b367581-b73d-4b12-a487-1a54c3b88470" /> | <img width="406" height="300" alt="image" src="https://github.com/user-attachments/assets/107d7294-90e8-49c6-826a-9be75c6b799f" /> --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285608 Forward-Port-Of: odoo/odoo#284755
The QFPay point-of-sale refund flow now waits for the order process to complete before starting a refund, preventing incorrect refund links and related test failures. This helps keep payment refund checks stable and avoids errors in automated validation environments.
Original PR description
Due to the tour not waiting to click next order on the feedback screen, the refund was initiated too early causing the refunded line to not link properly. This resulted in multiple runbot errors depending on the installed modules. This commit fixes both the traceback by adding a conditional access, and the tour logic by adding a `clickNextOrder` step. runbot-939616, runbot-237992 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285887
The web test system now stops immediately when required test dependencies cannot be fetched, instead of waiting until a long timeout. This makes build failures clearer and faster to diagnose, reducing wasted CI time and improving reliability for development teams.
Original PR description
Before this commit, a unit test suite could go silent between two test files, and `browser_js` then waited out its whole timeout before failing with: [ERROR] TypeError: Failed to fetch at orm…
Before this commit, a unit test suite could go silent between two test files, and `browser_js` then waited out its whole timeout before failing with:
[ERROR] TypeError: Failed to fetch
at orm (web.assets_unit_tests.min.js)
FAIL: WebSuite.test_unit_desktop
AssertionError: Script timeout exceeded
This happens because `fetchDependencies` gives every addon a `Deferred` that only the success handler of the batched `all_dependencies` call resolves, and a loaded runbot host answers that call with `RuntimeError: can't start new thread`. Nothing then settles those deferreds, so the `Promise.all` waiting on them never returns, `stop()` is never called, and the browser holds the build until the timeout.
This commit rejects the deferreds of a failed batch and reports an error escaping `runTests` on the console, so the build fails on the fetch error instead of running out its timeout.
https://runbot.odoo.com/odoo/error/243504
Forward-Port-Of: odoo/odoo#285834
Forward-Port-Of: odoo/odoo#284980This fix prevents installation errors when the Ecuador electronic invoicing module is installed without optional automatic dependencies. It also keeps withholding pages displaying correctly by hiding an irrelevant paid badge only when that badge is present.
Original PR description
When installing l10n_ec_edi with --skip-auto-install we receive an error on Runbot. Anchored on the sidebar title (always present on account.portal_invoice_page) rather than div[name='invoice_paid_badge'], which only exists when account_payment (not a dependency of this module) is installed and inherits this view to add it. Hides the account_payment "Paid" badge, if present, without requiring it to exist. runbot-237864 Forward-Port-Of: odoo/enterprise#129291 Forward-Port-Of: odoo/enterprise#126918
The self-order kiosk confirmation page now correctly shows the company logo after an order is placed. This improves brand visibility and makes the final order confirmation screen look complete and professional for customers.
Original PR description
Steps to reproduce: -- - Open the kiosk. - Place an order and go to the confirmation page. Issue: -- - The company logo is not displayed on the confirmation page. Fix: -- - Use the logo content to correctly set the company logo image source. task-6511327 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The point of sale invoice search test now waits for order syncing to finish before closing the session. This reduces occasional automated test failures and helps keep development validation more reliable without changing customer-facing behavior.
Original PR description
In the tour `TestUi.test_order_invoice_search`, the POS was closed without waiting for the order syncing to finish, causing an occasional failure. This commit fixes the issue by explicitly waiting for orders to sync before closing the POS. runbot-946625 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fixes how a stock-related field is defined so Odoo can create its supporting database table consistently. It helps prevent upgrade failures for customers whose databases already contain a table with the conflicting name.
Original PR description
The `move_line_ids` field has been a computed Many2many field since its introduction. Starting from V19.0, it was changed to a regular Many2many field…
The `move_line_ids` field has been a computed Many2many field since its introduction. Starting from V19.0, it was changed to a regular Many2many field [Here](https://github.com/odoo/odoo/pull/171743).
The current field definition incorrectly passes the second positional argument as the [relation](https://github.com/odoo/odoo/blob/19.0/odoo/orm/fields_relational.py#L1255) table name. Since the relation name starts with an uppercase character, as the table store as "Products" In addition, the relation table is mistkanley defined (not sure) instead of letting the ORM generate the relation table.
Update the field definition to use the correct field parameters and let the ORM handle the relation table creation.
Issue:
- The standard convention for automatically generated Many2many relation tables is to use a `_rel` suffix.
- Some databases already have a custom relation table with the same table name Products as the one explicitly defined by this field. When upgrading such databases, the ORM attempts to create the conflicting relation table, which can cause the migration to fail.
This change avoids the conflict and makes the field definition consistent with the expected ORM behavior.
current behaviour
```
labo_19_2=# \d "Products"
Table "public.Products"
Column | Type | Collation | Nullable | Default
------------------------------+---------+-----------+----------+---------
stock_package_destination_id | integer | | not null |
stock_move_line_id | integer | | not null |
Indexes:
"Products_pkey" PRIMARY KEY, btree (stock_package_destination_id, stock_move_line_id)
"Products_stock_move_line_id_stock_package_destination_id_idx" btree (stock_move_line_id, stock_package_destination_id)
Foreign-key constraints:
"Products_stock_move_line_id_fkey" FOREIGN KEY (stock_move_line_id) REFERENCES stock_move_line(id) ON DELETE CASCADE
"Products_stock_package_destination_id_fkey" FOREIGN KEY (stock_package_destination_id) REFERENCES stock_package_destination(id) ON DELETE CASCADE
```
After fix
```
labo_19_2=# \d stock_move_line_stock_package_destination_rel
Table "public.stock_move_line_stock_package_destination_rel"
Column | Type | Collation | Nullable | Default
------------------------------+---------+-----------+----------+---------
stock_package_destination_id | integer | | not null |
stock_move_line_id | integer | | not null |
Indexes:
"stock_move_line_stock_package_destination_rel_pkey" PRIMARY KEY, btree (stock_package_destination_id, stock_move_line_id)
"stock_move_line_stock_package_stock_move_line_id_stock_pack_idx" btree (stock_move_line_id, stock_package_destination_id)
Foreign-key constraints:
"stock_move_line_stock_package_destinati_stock_move_line_id_fkey" FOREIGN KEY (stock_move_line_id) REFERENCES stock_move_line(id) ON DELETE CASCADE
"stock_move_line_stock_package_stock_package_destination_id_fkey" FOREIGN KEY (stock_package_destination_id) REFERENCES stock_package_destination(id) ON DELETE CASCADE
```
Ticket: 6485470
Upgrade pr :-https://github.com/odoo/upgrade/pull/11189
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-prThis fix updates spreadsheet and dashboard interface elements to match the newer Frost visual style. It also corrects a search bar display issue so the bar keeps a properly rounded end when no search menu is available, improving visual consistency for users creating spreadsheets from Documents.
Original PR description
- removed the rounded borders of the spreadsheet component - adapted the icons to the `<div class="o-icon><i class="oi"/></div>` structure to match o-spreadsheet expected rules Task-6511787 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
Odoo now treats common placeholder VAT values such as '/', 'NA', and 'na' as missing VAT numbers instead of valid entries. This improves accuracy in accounting, e-invoicing, and localization workflows, while also optimizing VAT lookup performance for large databases.
Odoo now treats common placeholder VAT entries such as '/', 'NA', and 'na' as missing VAT values, rather than accepting them as valid. This helps reports, tax filings, payroll, and electronic document flows make more accurate decisions when customer or company VAT information is incomplete.
Users can now generate lot or serial numbers from the Barcode app on receipts without the screen failing. This restores the expected workflow for receiving tracked products and prevents an error that blocked warehouse operations.
Original PR description
Issue: ------------------------------------------ Clicking the `+` button on a receipt in Barcode for a lot/serial tracked product gives traceback instead of opening the lot/serial generation dialog.…
Issue: ------------------------------------------ Clicking the `+` button on a receipt in Barcode for a lot/serial tracked product gives traceback instead of opening the lot/serial generation dialog. Steps to reproduce: ------------------------------------------ 1. Install `stock_barcode`. 2. Create product P1 with serial tracking. 3. Create receipt for P1. 4. Go to barcode and click on `+` button to generate serial number. 5. Traceback: `Cannot read properties of undefined (reading 'getRecord')` Cause of the issue: ------------------------------------------ `LineComponent.openDialog()` was trying to access `this.cache.getRecord(...)`, but `LineComponent` does not define a cache property. This issue was introduced by PR https://github.com/odoo/enterprise/pull/129521, which fixed a similar traceback when generating serial/lot numbers from Barcode for manufacturing orders. Solution: ------------------------------------------ Use `this.env.model.cache.getRecord(...)` when resolving the move before opening the dialog. This avoids the undefined access and allows the serial/lot generation dialog to open normally.
Spreadsheet dialogs and selectors now display with more consistent rounded borders, cleaner search bars, and top navigation styling aligned with the rest of the interface. This fixes visual layout issues after recent design updates, making spreadsheet creation and selection feel more polished and easier to use.
Original PR description
- merge the topbar menu style with the navbar - fix spreadsheet dialogs searchbars - align spreadsheet selector design with kanban standards (rounded borders) Task-6511787
Fixed an accounting issue where bank reconciliation could hide a vendor bill reference when exchange rate differences were involved. This helps users verify reconciled foreign-currency payments more clearly and reduces confusion during review.
Original PR description
In the bank reconciliation widget, a reconciled transaction matched with a bill and a loss exchange entry no longer displays the bill reference next to the reconciled line. Steps to reproduce: - Have…
In the bank reconciliation widget, a reconciled transaction matched with a bill and a loss exchange entry no longer displays the bill reference next to the reconciled line. Steps to reproduce: - Have company currency USD and foreign currency EUR - Create a vendor bill in foreign currency (100 EUR, rate 1 USD = 1 EUR) - Create a bank transaction in the foreign currency for less than the bill total, at a rate making the amount higher in company currency (90 EUR, 108 USD at 1 EUR = 1.2 USD) - Open the bank reconciliation widget, reconcile transaction and bill - Unfold the reconciled transaction Issue: A bill reference is missing Analysis: The exchange difference line is reconciled with the transaction line. Being reconciled with two lines (bill and exch), the widget relies on `*_reconciled_lines_excluding_exchange_diff` to show the fields. However, `count_reconciled_lines_excluding_exchange_diff` has been defined as boolean instead of int, faulting the comparison. opw-6479015 Forward-Port-Of: odoo/odoo#283855
The Point of Sale variant popup now shows the truly available free quantity, excluding stock already reserved by confirmed sales orders. This prevents staff from seeing misleading stock levels when choosing product variants, reducing overselling risk and inventory confusion.
Original PR description
## Steps to reproduce: - Create another warehouse - Create a product with a variant, like Color, values black and white - Track the product, add a qty on hand of 50 on the black product - Go to the…
## Steps to reproduce: - Create another warehouse - Create a product with a variant, like Color, values black and white - Track the product, add a qty on hand of 50 on the black product - Go to the sales app, make a quotation of 50 for the black product - Confirm the quotation - Go to the PoS, click on the product, check the available qty in the popup - It is still 50, even though the forecasted is correct at 0 ## Why the fix: Having the actual free qty was added in this commit 682bc82 to be able to check the qty that was really free instead of the available qty. This means that we subtract the reserved_qty from the qty_available to get the free_qty. The variant popup was forgotten in this commit, so it was still displaying the qty_available. This is why there was a difference in the qty if we press the product normally or if we long press it, because the variant popup was forgotten in said commit. opw-6382845 Forward-Port-Of: odoo/odoo#284321 Forward-Port-Of: odoo/odoo#280330
Uploading a product image now refreshes the preview right away instead of continuing to show the previous image until the record is saved. This helps users confirm they selected the correct image without an extra save step.
Original PR description
Bug === 1. Open the product form view 2. Upload an image -> You need to save to see the preview of the new image Technical ========= Before 23a7d8cc5b1d2f38eaee35a4f6729fe128c1b62b, it used `this.props.record.data[this.props.name]` in `getUrl` (so `image_1920`) and not `this.props.record.data[<preview_image>]` (`image_128`). When we upload a new image, we write on `image_1920`, but `image_128.content` is missing, and so it fallback to the old image. Task-6509975
Fixed monthly salaries in Belgian payroll are now prorated using the employee's expected hours for the full pay period, rather than a single week's hours. This prevents incorrect deductions and ensures employees are paid accurately when they work more or less than half of the period.
Original PR description
### Problem - Fixed salary payslips were not being prorated correctly. The 50% rule threshold was compared against `hours_per_week` (single week) instead of 50% of the theoretical hours for the…
### Problem
- Fixed salary payslips were not being prorated correctly. The 50% rule threshold was compared against
`hours_per_week` (single week) instead of 50% of the theoretical hours for the payslip period.
This led to incorrect salary deductions in all cases and the wrong computation path being taken when less than
50% of the month was worked.
### Solution
- Fix `_l10n_be_has_enough_paid_hours` to compare paid hours against
50% of theoretical hours instead of `hours_per_week` (which are calculated for the employee's `working schedule`)
### How it works
- For fixed salary (`wage_type = 'monthly'`), the quarterly hourly rule
is applied as follows:
- **Hourly rate** = `fixed_salary × 3 / 13 / theoretical_hours`
- **50% rule**:
- If more than 50% of theoretical hours were worked → deduct absences
from fixed wage
- If less than 50% of theoretical hours were worked → pay only the
hours worked
For variable salary (`wage_type = 'hourly'`), the employee is simply
paid for the number of hours worked during the period.
Task-6260378
Forward-Port-Of: odoo/enterprise#126600This change corrects an internal reference used when uninstalling the fleet stock integration. It prevents a build or uninstall error caused by an outdated module name, helping keep system maintenance reliable.
Original PR description
A reference to the `stock_picking_batch_action` was not updated in [^1] and still referred to `stock_picking_batch` instead of `stock`. runbot-build-error: [946649](https://runbot.odoo.com/odoo/runbot.build.error/946649) [^1]: https://github.com/odoo/enterprise/pull/128175
The data cleaning tool now correctly recognizes the database index details it relies on when checking linked records. This prevents missed references during merge or cleanup operations after a related platform change.
Original PR description
`ir.model.fields.index` now stores the exact index type instead of a boolean. Match B-tree index types when looking for indexed references. task-6507120 Related: https://github.com/odoo/odoo/pull/284668
The Point of Sale dashboard now keeps sales graphs aligned at the bottom of each card, even when cards show different amounts of information. This makes the dashboard easier to scan and gives users a cleaner, more consistent view.
Original PR description
In the POS config dashboard kanban view, cards could display a variable number of data lines (e.g. session dates, opening, sold, ongoing amounts, or closing date). Because the graph was placed right below the text content without vertical pushing, graphs across cards in the same row ended up vertically misaligned. Fix this by adding `d-flex flex-column` to the card template and `mt-auto` to the graph container wrapper so that graphs are always pushed to the bottom of the card, ensuring consistent alignment regardless of the number of displayed lines. task-id: 6424698 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279040
The HTML editor now correctly removes formatting, such as underline, from headings inside list items. It also prevents formatting applied to a parent list item from unintentionally affecting nested list items, making document editing more predictable for users.
Original PR description
**Current behavior before PR:**
1. Create a list with h1, have some text inside.
2. Select whole heading and apply underline style.
3. Click on remove format button.
You will notice that underline style is not removed from list.
**Desired behavior after PR is merged:**
Now, format is removed correctly from the list.
task-6348753
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#273678Asset disposals with no depreciation no longer create unnecessary zero-value accounting lines. This keeps accounting entries cleaner and improves the accuracy of depreciation values by calculating them directly instead of relying on line order.
Original PR description
Purpose: When we try to dispose of an asset with 0 depreciation, we get a move line with 0 balance, this line is supposed to debit depreciated amount till date on asset depreciation account which in our case is 0. And line with 0 balance seems like a noise in accounting. Fix:- - When creating disposal move, create lines with either +ve or -ve balance and skip lines with 0 balance. - Computation of `depreciation_value` on `account.move` assumed that `move.line_ids[1]` will be always depreciated amount. Which is guess work and break if at that index there is some other line. So instead of guessing calculate here depreciation amount with the same logic as disposal move.
The shared time selection control has been moved into the main HR module so it is available even when Time Off is not installed. This prevents warning messages in HR Attendance tests and improves reliability across HR applications.
Original PR description
Using widget="float_time_selection" in hr_attendance triggered a missing widget warning during view tests when hr_holidays was not installed, as the widget was defined in hr_holidays. This move the float_time_selection component to the hr module so the widget is available across all HR applications. Task-6523568 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update fixes automated tests for the HTML editor so they no longer depend on visual design settings that can vary between editions or themes. It helps keep quality checks stable while preserving the intended color contrast behavior for users.
Original PR description
The ContrastPlugin adjusts the colors of the content against the background the editable is drawn on, which it reads from a CSS variable whose value is a design decision: it differs between community and enterprise, as well as between light and dark mode. The tests asserting the exact adjusted colors pin that variable so as not to assert on the design of the assets they happen to run with, but they were still pinning its former name, which the follow-up below renamed to `--o-html-editor-background-contrast`. Pin the current name, on the white background the expected colors were computed against. This is the follow-up of: https://github.com/odoo/odoo/pull/285000 https://github.com/odoo/odoo/pull/285285 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
Rental product pages now show the original price before a discount more reliably. This helps shoppers better understand savings when rental pricing uses multiple rules across the rental period.
Original PR description
As multiple rules can be applied for rental products, the default computaion of the product price before discount is not computed correctly. This commit overrides `_get_price_before_discount` for rental products to fetch the product's price without pricelist. task-6279525 See also: - https://github.com/odoo/odoo/pull/276566
A Point of Sale automated test was corrected so it no longer tries to install the Restaurant feature unnecessarily. This prevents avoidable test failures and helps keep development validation reliable without changing customer-facing behavior.
Original PR description
`TestPosOrderReceipt.test_split_per_product_preserves_combo_children`: This test sets `module_pos_restaurant` to `True`, which causes the `pos_restaurant` module to start installing, leading to this error: ``` RuntimeError: Module operations inside tests are not transactional and thus forbidden. If you really need to perform module operations to test a specific behavior, it is best to write it as a standalone script, and ask the runbot/metastorm team for help. ``` The test does not in fact rely on any `pos_restaurant` behaviour, so the line can simply be removed. runbot-946164 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Responses to invoices received through France's PDP are now sent through only the PDP channel instead of both PDP and classic Peppol. This avoids duplicate messages and keeps invoice communication threads cleaner for users.
Original PR description
When responding to an invoice received through PDP, 2 responses were actually sent: one through PDP and the other through the 'classic' Peppol. This was done to allow more reliability, in case one channel is down when trying to respond, but this mainly creates cluttah' in the chattah' (call me busta rhymes yo) task-6522652 Forward-Port-Of: odoo/odoo#285596
Fixed an issue where reprinting a preparation receipt from the preparation display could fail with an error. This ensures staff can reliably reprint kitchen or preparation receipts when needed, avoiding interruptions during service.
Original PR description
Reprinting a preparation receipt from the preparation display was showing a traceback because the preparation template wasn't correctly loaded. task-6522158
The website now sends a visitor's saved cookie consent choice to Google before loading analytics settings on each page. This prevents tracking gaps between page views while continuing to respect visitors who declined cookies.
Original PR description
With Google Consent Mode V2, we have to set a default consent state, then update it according to the user's choice. While our setup worked fine with new users interacting with the cookies bar, on subsequent pages it would update the consents after pinging Google first. This meant that, from Google's point of view, there was a gap for a single user when switching pages. By updating it before setting the config, we make sure that Google handles the analytics as expected, without any hole, while keeping it safe for users who refuse the cookies. task-6471290
This fix prevents slow mobile-style taps in Odoo's web test tools from being incorrectly treated as long presses. It reduces random test failures, helping teams get more dependable build results and avoid false alarms during quality checks.
Original PR description
Before this commit, MobileWebSuite.test_unit_mobile failed at random on the mail test "HtmlMail add icon and save inline html", which taps the save button of an html_mail field and waits for the…
Before this commit, MobileWebSuite.test_unit_mobile failed at random on the mail test "HtmlMail add icon and save inline html", which taps the save button of an html_mail field and waits for the write:
[verifySteps] expected the following steps
> Expected: [
"web_save",
]
> Received: []
This happens because a tap held longer than `LONG_TAP_DELAY` counts as a long press, and `_pointerUp` then dispatches no click at all. That delay spans the synthetic pointerdown and pointerup of a single `click()`, so the work a listener does during the press counts towards that delay: tapping save blurs the html field, which converts the whole editor content to inline styles, and under the load of a runbot build that conversion alone takes longer than the delay.
One solution could have been to raise `LONG_TAP_DELAY`, but a listener slower than the new value suppresses the click again.
This commit counts a tap as long only when the test releases the pointer itself, with `pointerUp`, so the work a listener does during a `click()` no longer suppresses the click.
https://runbot.odoo.com/odoo/error/946581
Forward-Port-Of: odoo/odoo#285640
Forward-Port-Of: odoo/odoo#284971Users can now click links in read-only website content to open the link popover and inspect where the link goes. This fixes a case where non-editable article links appeared inactive, improving clarity without changing the content itself.
Original PR description
Problem: Clicking a non-editable link does nothing, making it impossible to open or inspect the link. Solution: Allow the link popover to open in read-only mode for non-editable links. Steps to reproduce: - Run `/article`. - Click on the inserted article link. - Observe that nothing happens. opw-6442026 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285445 Forward-Port-Of: odoo/odoo#280224
This fix prevents the HTML editor from accidentally deleting visible items that do not contain text, such as icons or images. It helps ensure saved website or document content keeps its intended visual elements.
Original PR description
#### Description of the issue this PR addresses: - Empty inline elements were detected using only their text content, causing visible non-text content (e.g. icons, images) to be removed. #### Desired behavior after PR is merged: - Remove the empty inline attribute when an inline element contains visible content by using `isVisible`. - Use `isVisible` to detect visible content instead of relying on text content. task-6391496 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276587
Belgian payroll test demo employee records have been cleaned up so they no longer create unnecessary empty versions. Employee identifiers, schedules, tags, wages, and worker codes now align with the stated demo records, making payroll test data more reliable.
Original PR description
Every employee record was also creating an empty version dated 2023-01-01, because hr.employee delegates to hr.version. Employees now carry their own first version, so those extra ones are gone. Fields the ORM was throwing away are removed, the NISS numbers are rebuilt from each birthday and sex, and the schedules, tags, wages and worker codes now match what the records claim. task-6519088
This fix prevents an error when users close the email composer after selecting more than 500 CRM leads. The composer now uses the selected records from the context when the usual stored list is intentionally unavailable, avoiding a disruptive crash during bulk email workflows.
Original PR description
Steps to reproduce: 1. Install `crm` 2. Create leads more than 500. 3. Select all and try to send email 4. Not close the wizard by "X" Issue: - Traceback occurs: `Uncaught Promise > Unexpected end of JSON input` Cause: - res_ids is not set on the composer when more than 500 records are selected. This is expected, as the compute method `_compute_res_ids()` does not write `res_ids` when the number of `active_ids` exceeds 500 (to avoid storing large payloads on the field). Because of this, the code trying to JSON.parse(res_ids) fails while dismissing the wizard at `onCloseWizardModal` Solution: - Fallback to context.active_ids when res_ids is not available opw-5891862 Forward-Port-Of: odoo/odoo#252670 Forward-Port-Of: odoo/odoo#248406
Audio messages in VoIP forms now open without crashing after a recent platform change to how files are stored. This keeps users able to access and update recorded audio content normally.
Original PR description
Since odoo/odoo#266082, binary fields use an object containing their content, filename and size. The `isBinarySize` helper was also removed.
The VoIP audio binary field still imported this helper and treated binary values as raw base64 strings. Opening an audio message form therefore crashed with:
TypeError: isBinarySize is not a function
Use the new binary value structure when reading and updating audio content.Users will now see a newly selected image right away when editing image fields with previews. This removes confusion where the old image stayed visible until the record was saved.
Original PR description
**Description of the issue/feature this PR addresses:** The uploaded image is not displayed immediately when using the `preview_image` option. **Current behavior before this PR:** The widget checks the preview field (e.g. `image_128`), which remains stale until the record is saved. As a result, the newly uploaded image is only displayed after saving. **Desired behavior after this PR is merged:** The widget checks the edited field itself instead of the preview field, allowing the newly uploaded image to be displayed immediately. **Related PR**: https://github.com/odoo/odoo/pull/266082
This fixes an issue where search filters and groupings could stop appearing correctly in the search menu after users switched views, even though the choices were still applied. The search interface now keeps its state properly updated, making filtering behavior clearer and more reliable.
Original PR description
Importing a state, as done when switching view, replaced the search model's query and searchItems by the raw values read from that state, dropping the reactivity of both containers. The search bar menu, whose content is rendered in a popover that the parent's render does not reach, then stopped reflecting the filters and group bys toggled in it, even though they were still applied. The fix populates the existing containers in place to preserve reactivity. task-6524460
The search bar filter labels now display their icons at a consistent height and with cleaner rounded styling. This small visual fix makes the interface look more polished and easier to scan without changing functionality.
Original PR description
Before this commit, the facet label never centered its children, so each icon compensated on its own (`align-middle`, `align-content-around`, `ms-1 mt-1`) and the three landed at different heights. The swap_vert overlay also had no radius, drawing a square over the rounded label. This commit centers each icon from its own container and rounds the swap_vert overlay. It also drops the pencil overlay's transparent border and `pe-2`, which only pushed it down and off-center, and an unreachable `!facet.icon` branch on the swap_vert span. requires: https://github.com/odoo/enterprise/pull/129724 task-6518046 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix stops Point of Sale restaurant orders from accidentally deleting order lines when the same order is handled across multiple devices. It helps ensure paid orders keep the correct items, totals, and audit trail after device synchronization.
Original PR description
Steps to reproduce: - Restaurant config with two POS devices on the same session - Device A: open a table, add two products, press Order (the order is synced as a draft, its lines get server ids) -…
Steps to reproduce:
- Restaurant config with two POS devices on the same session
- Device A: open a table, add two products, press Order (the order is synced as a draft, its lines get server ids)
- Device A: remove both lines, without syncing
- Device B: touch the same order, so device A re-reads it from the server through the synchronisation websocket
- Device A: pay and validate the order
Issue:
The order is saved as paid, with its total and its payment, but without any orderline. The lines are deleted on the server: odoo.models.unlink: deleted pos.order.line records with IDs: [...]
Cause:
Removing an orderline queues an unlink command in
models.commands['pos.order'].unlink['lines_<order id>'] (delete_ in related_models.js). That command is only discarded by clearCommands(), which syncAllOrders() calls after a successful sync, so it stays pending in between.
Any read of the order in that window puts the line back: a deleted record counts as missing in missingRecursive(), so it is fetched again and loadData() re-creates it with its server id.
serialize() then emits both the update of the live line and the still pending unlink, and sync_from_ui writes
lines: [[1, id, {...}], [3, id]]. The ORM applies commands in order, and pos.order.line.order_id is ondelete='cascade', so Command.UNLINK deletes the line right after writing it.
Fix:
Skip a removal command when the record is still linked to the parent at serialization time. A record cannot be both linked and removed in the same payload, so the pending command is stale and dropping it keeps the local state. Genuine removals, where the record is no longer linked, are still sent.
opw-6401146
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#285427
Forward-Port-Of: odoo/odoo#284695The Indian localization now hides the generic Self Billing option in journal settings. This prevents users from enabling conflicting invoicing options and helps keep the Indian Self Invoice process compliant with GST requirements.
Original PR description
With this commit:- The generic Self Billing option is hidden from the journal configuration for Indian localization, preventing users from enabling both Self Invoice and Self Billing, and ensuring the Indian Self Invoice flow and output remain compliant with the GST requirements. Related [Self Invoice PR](https://github.com/odoo/odoo/pull/269434) task-6505237
This fixes an automated mail test so messages containing emoji shortcuts are posted reliably. The change prevents the emoji suggestion popup from blocking the test action, reducing false failures in quality checks without changing user-facing behavior.
Original PR description
Before this commit, "[text composer] Posting message should transform relevant data to emoji." posted no message on runbot:
Failed to find 1 of ".o-mail-Message-body:text('test ...')"
(Timeout of 10 seconds). Found 0 instead.
This happens because "test :P :laughing:" leaves the emoji suggestion of the shortcode it just typed pending, and Composer.onKeydown returns without posting when the NavigableList takes the Enter. The fetch behind that suggestion is debounced by 250ms, so the test posts only while the debounce has not fired.
This comes from "[IMP] web,*: emoji loader": the search reads the emoji data the mail test helpers preload, so the list has an entry to offer where it had none.
This commit types a trailing space, which closes the suggestion and leaves the Enter to post.
https://runbot.odoo.com/odoo/error/946650
Forward-Port-Of: odoo/odoo#285616Event agenda pages with many items now scroll horizontally more smoothly in Firefox and Safari. This reduces shaking or stuttering when visitors browse busy event schedules, improving the attendee browsing experience.
Original PR description
On Firefox and Safari, horizontal scrolling on an agenda with many items stuttered. This was likely caused by triggering too many scroll events, without throttling them. Steps to reproduce: - On Firefox or Safari, run with demo data - Go to the "Design Fair Los Angeles" agenda (click on the event > Talks > Agenda. Alternatively, enter the URL directly: `/event/ID/agenda`, with the right event ID) - Resize the page (or open the dev tools) so that there is a horizontal scrollbar. - Scroll horizontally with a trackpad or with shift + mouse wheel. => The agenda stutters/shakes: as you go to the right, it sometimes slightly goes back left, and vice-versa. You might have to scroll from left to right and right to left a few times to see the issue. Forward-Port-Of: odoo/odoo#285237
This fixes an inventory issue where selecting a contact on a delivery transfer could incorrectly reset the destination from a custom customer stock location back to the generic Customers location. Businesses using tailored delivery operation types can now rely on their configured destination locations staying in place, reducing manual corrections and shipment errors.
Original PR description
### Steps to reproduce: - In the settings: Enable Storage Locations - Create a customer location "Customer stock" with "Customers" as its parent location - Create a delivery operation type "Deliver…
### Steps to reproduce: - In the settings: Enable Storage Locations - Create a customer location "Customer stock" with "Customers" as its parent location - Create a delivery operation type "Deliver Super Customer" and set its default destination location to "Customer stock" - Go to Inventory > Overview > Deliver Super Customer > New - Set a contact on the transfer #### > The destination location switches from "Customer stock" to "Customers" ### Cause of the issue: The `location_dest_id` of `stock.picking` depends on its `partner_id`. So that changing the partner recomputes the locations of the transfer. However, as soon as the destination of the operation type has a `customer` usage, the `property_stock_customer` of the contact replaces it unconditionally: https://github.com/odoo/odoo/blob/04f3a7bca99d0144a4ea871be9625db368b196ca/addons/stock/models/stock_picking.py#L949-L963 However, the `property_stock_customer` falls back to an `ir.default` pointing at the default `Customers` location when nothing is set on the contact: https://github.com/odoo/odoo/blob/1c40fab04b71def8f3645c4c4bb0c1441057f307/addons/stock/data/stock_data.xml#L71-L72vs The override comes from 8a0775aa1dd9, which replaced an `elif` fallback on the contact by an "unconditional" substitution as this fallback had become unreachable once `default_location_src_id` and `default_location_dest_id` were made required: https://github.com/odoo/odoo/blob/04f3a7bca99d0144a4ea871be9625db368b196ca/addons/stock/models/stock_picking.py#L34-L41 opw-6421090 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284873 Forward-Port-Of: odoo/odoo#280016
Customers who place self-order takeout or delivery orders now receive an email that actually provides access to their receipt. This fixes a mismatch where the email promised a receipt but did not include one, reducing confusion and support follow-up.
Original PR description
Currently, when takeout and delivery mails are sent out to clients the mention "Attached you will find you receipt" can be seen but no receipt is sent. Steps to reproduce: ------------------- * Modify restaurent config * Enable QR + self ordering * Add Online payment method * Open the self order * Make an order for delivery or takout * Pay the order * Check the emails sent > No attachment provided Why the fix: ------------ Since this commit https://github.com/odoo/odoo/commit/a0b567508ffeb572a3c36bf28ae085d766d95f18 we now send the email only from the backend but the receipt couldn't be rendered from the backend at that time. In this version it is now possible so we cans attach the receipts with the mail. opw-6197985 Forward-Port-Of: odoo/odoo#285241 Forward-Port-Of: odoo/odoo#266007
Polish e-invoices now correctly treat K_12 taxes, such as 0% EU U, as reverse charge transactions. This helps ensure KSeF XML submissions contain the right reverse charge indicator and tax base, reducing compliance and reporting errors.
Original PR description
**PROBLEM** K_12 taxes needs to be reported as reverse charge tax. **STEP TO REPRODUCE** 1. Create an invoice with the tax 0% EU U. 2. Send the invoice the ksef. 3. Open the xml, an notice P_18 value is 2 (meaning no reverse charge), and there is no tag P_13_10. expected behavior: P_18 = 1, P_13_10 = base for the tax. opw-6460338 Forward-Port-Of: odoo/odoo#281934
This fix gives localizations the option to refuse future confirmed leave requests instead of deleting them when an employee departure is recorded. It preserves relevant leave history where required while keeping the standard behavior unchanged for other localizations.
Original PR description
Before: * Future leaves were deleted when an employee departure was applied. * There was no way for localizations to preserve these leaves when they should remain in the employee's history. After: * Add `refuse_future_leaves` to `_cleanup_employee_departure_leaves()`. * Approved leaves are still cancelled when they extend beyond the departure date. * Future confirmed leaves can now be refused instead of deleted when requested by a localization. * Keep the existing deletion behavior by default for other localizations. Impact: * Allows localizations to preserve future leave records while keeping the existing generic departure behavior unchanged. Task: 6453718 Forward-Port-Of: odoo/odoo#284398 Forward-Port-Of: odoo/odoo#281493
This fixes resizing issues in the HTML editor when tables or columns are nested inside each other. Users can now resize or reset table columns without inner content overflowing or blocking further adjustments, making page editing more reliable.
Original PR description
### Steps to reproduce **Issue 1:** * Create a table inside another table (e.g. `/table`). * Resize the last cell of the inner table beyond the outer `td` boundary. * Then try to resize the outer…
### Steps to reproduce **Issue 1:** * Create a table inside another table (e.g. `/table`). * Resize the last cell of the inner table beyond the outer `td` boundary. * Then try to resize the outer table `td` containing the inner table. * The outer table `td` can no longer be resized. **Issue 2:** - Create a table in the editor - Inside any cell, create a nested table - Resize the nested table by dragging its last column to the left to give it a fixed width - Resize the outer column that contains the nested table to the left - Observe that the nested table overflows its parent cell **Issue 3:** - Create a table in the editor - Inside any cell, create a nested table - Resize the nested table columns to give it a fixed width - Double-click the outer column border to reset its width - Observe that the nested table overflows its parent cell ### Purpose of this PR Prevent resizable elements from growing beyond the bounds of their nearest resizable ancestor. This fixes resize behavior for nested resizable structures such as: * tables inside tables * text columns inside tables * tables inside text columns The resize logic now clamps width expansion once the nearest resizable ancestor boundary is reached. - When resizing an outer table column that contains a fixed-width nested table, the outer column could be shrunk below the nested table's width, causing the nested table to overflow its parent cell. - The fix computes an effective minimum size per column by inspecting any fixed-width nested tables in that column's cells before the resize begins. This effective minimum size is passed through the resize_target_processors resource so ResizePlugin remains generic. - When resetting an outer column width, any fixed-width nested table inside the affected cells is also reset so it adapts naturally to the new outer column width instead of overflowing its parent cell. task-6215674 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#264633
Employee transport reimbursement details are now preserved when a company car is assigned. This prevents public transport, private car, and bicycle reimbursement information from being accidentally removed, supporting employees who use multiple commuting options.
Original PR description
When entering transport reimbursement values on an employee record, `_onchange_transport_mode` automatically resets public transport kilometers (bus, tram, metro), private car kilometers, and bicycle settings to zero whenever a company car is assigned. This commit removes the code that wipes alternative transport values when `transport_mode_car` or `car_id` is set, allowing employees to combine a company car with other transport reimbursement modes. task-6514557
Manufacturing bills of materials now prevent configurable combo products from being selected as components. This avoids using sales bundle items where physical manufacturing parts are expected, reducing setup errors in production planning.
Original PR description
Version: --------- - 19.0+ Steps to reproduce: ------------------- - Install `mrp` and `sale_management` - Create a product of type `combo` - Go to Manufacturing > Products > Bills of Materials - Create or edit a BoM - Add a component line - Select the combo product Issue: ------ Combo products can be selected as BoM components. Since combo products represent configurable sales bundles rather than physical products, they are not meaningful manufacturing components. Before Commit: ------------------- - It was possible to select a product of type 'combo' as a component in a Bill of Materials. After Commit: ----------------- - Combo products are filtered out of the component picker and cannot be added as BoM components --- opw-6425024 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278861
The AI assistant now avoids unnecessary follow-up questions in Website Builder and presents choices as valid answers instead of more questions. Users can still type free-text answers by default, see cleaner prompts without selected-count clutter, and receive auto-approval notices right away.
Original PR description
Prevent unnecessary follow-up questions in Website Builder and ensure choices are valid answers rather than questions. Keep free-text answers enabled by default, remove the selected-count display, and post the auto-approval notice immediately.
Belgian payroll now calculates home-work transport tax exemptions from the amounts actually taxed on payslips, rather than estimates from contract fields. This prevents employees from being overtaxed on some transport benefits or receiving unintended double benefits, improving legal compliance and payroll accuracy.
Original PR description
The withholding tax exemption for the employer intervention in home-work transportation costs (500 EUR/year - 41.70 EUR/month for income year 2026) was estimated from contract fields (car_atn,…
The withholding tax exemption for the employer intervention in home-work transportation costs (500 EUR/year - 41.70 EUR/month for income year 2026) was estimated from contract fields (car_atn, private_car_employee_kilometer) instead of the amounts actually taxed on the payslip. As a consequence: - The fuel card for daily commute was fully taxed: the employee never received the exemption they are legally entitled to. - The private car reimbursement was paid net, never added to the taxable base, yet it still granted the exemption on the yearly taxable: a double benefit. - A bike allowance paid above the exempt rate per km (0.37 EUR/km in 2026) was never taxed. The exemption is now computed from a new TRANSPORTATION_BASE salary rule category accumulating the taxable transportation amounts of the month: - ATN.CAR and FUEL_CARD_COMMUTE (already taxed) are tagged with the category. - New hidden rules CAR.PRIV.TAX and CYCLE.TAX fictively add the private car reimbursement and the above-rate bike excess to the withholding base, after ONSS and before the gross totals, while the reimbursements themselves remain paid net. - TRANSPORT_TAX_DED becomes -min(yearly taxable, yearly threshold, 12 x monthly transportation base), including the transport lines of already validated payslips of the same month so that non-periodic (regularisation) payslips preserve the monthly totals. The termination fees variant uses the category without x12, its base being annual. The obsolete helper _get_be_withholding_taxes_transport_deduction is removed, and the affected payslip validation expectations are updated to the corrected amounts. task-6388103 Forward-Port-Of: odoo/enterprise#124644
Belgian employee departures now avoid unintended archiving when no archive date is set, preserve company car assignments, and refuse future leave instead of deleting it. This protects important HR records and reduces accidental loss of employee-related information during offboarding.
Original PR description
Before: * Belgian employees without an "Archive Employee On" date were automatically archived after their departure. * Future leaves were deleted when applying an employee departure. * The employee's company car was automatically unassigned on departure. After: * Belgian employees without an "Archive Employee On" date are no longer automatically archived. * Future leaves are refused instead of deleted. * Company car assignments are kept when applying a Belgian employee departure. * Display "Don't archive" when no archive date is set. Impact: * Prevents unintended archiving and preserves future leave and company car information for Belgian employees. Task: 6453718 Forward-Port-Of: odoo/enterprise#129127 Forward-Port-Of: odoo/enterprise#127414
Secondary badges are now easier to read in both light and dark display modes. This improves clarity for users by ensuring badge labels stand out consistently across the interface.
Original PR description
Before this PR:- ========================== Secondary badges are barely visible in light and dark modes. <img width="500" height="200" alt="image" src="https://github.com/user-attachments/assets/552dd924-4ab4-4fd5-b0fe-b103697a0bb9" /> <img width="500" height="200" alt="image" src="https://github.com/user-attachments/assets/a199ad73-bd94-42dd-aa46-1fc7fb2f9436" /> After this PR:- ========================== Make the badge text and background clearly visible in both modes. <img width="1232" height="672" alt="image" src="https://github.com/user-attachments/assets/cbff2756-f2e4-42fa-97c6-669409fd4efb" /> Enterprise PR:- odoo/enterprise#129792
A mail testing helper now stops retrying after a timeout instead of continuing to report the same failure across later tests. This makes automated test results clearer and helps developers identify the real issue faster without misleading follow-on failures.
Original PR description
Before this commit, one test contains() reaching its 10 seconds budget made the 129 tests that ran after it fail with its own assertion, over 14 suites:
[HOOT] Test "@mail/message/link_preview/Delete all link previews at
once" failed:
Failed assertion:
3. [toBe] expected values to be strictly equal (Failed to find 1 of ".o-mail-Message-body:text(...)" (Timeout of 87 seconds). Found 0 instead.)
This happens because the tick re-schedules itself on the result of runOnce(), which is undefined on a failure, the crashing one included. The check keeps selecting every 500ms and logs its assertion against whichever test is running, until a tick lands between two suites and getFixture() throws.
This commit re-schedules a tick only while the check is not done, so the crashing one is the last.
https://runbot.odoo.com/odoo/error/946650
Forward-Port-Of: odoo/odoo#285585Mexican DIOT reports now show the remaining column headers in Spanish for Latin America instead of mixing Spanish and English. This makes the report easier and more consistent for Spanish-speaking users to read.
Original PR description
## Merge method squash --- ## Problem The DIOT report (`account_report_diot.xml`) uses inline `name@es_419` translations for Spanish (Latin America) column headers. However, the translations were…
## Merge method squash --- ## Problem The DIOT report (`account_report_diot.xml`) uses inline `name@es_419` translations for Spanish (Latin America) column headers. However, the translations were inconsistently applied: columns at 16% had them, while the equivalent 8% columns and several others were left without Spanish translations. **Columns missing `name@es_419` (before this fix):** | `id` | English name | Added es_419 | |---|---|---| | `diot_report_paid_8_n_wnc` | Paid 8 % Northern | Pagado al 8% Norte | | `diot_report_paid_8_n_r` | Refunds 8 % Northern | Devoluciones 8% Norte | | `diot_report_paid_8_s_wnc` | Paid 8 % Southern | Pagado al 8% Sur | | `diot_report_paid_8_s_r` | Refunds 8 % Southern | Devoluciones 8% Sur | | `diot_report_paid_16_r` | Refunds 16% | Devoluciones 16% | | `diot_report_paid_16_imp_r` | Refunds Importation 16% | Devoluciones Importación 16% | | `diot_report_paid_16_imp_int_wnc` | Intangible Imports 16% | Importaciones Intangibles 16% | | `diot_report_paid_16_imp_int_r` | Refunds Intangible Imports 16% | Devoluciones Importaciones Intangibles 16% | | `diot_report_paid_8_n` | Paid 8 % N. - Creditable | Pagado al 8% Norte - Acreditable | | `diot_report_paid_8_s` | Paid 8 % S. - Creditable | Pagado al 8% Sur - Acreditable | | `diot_report_paid_16` | Paid 16% - Creditable | Pagado al 16% - Acreditable | | `diot_report_paid_16_imp` | Importation 16% - Creditable | Importación 16% - Acreditable | | `diot_report_paid_16_imp_int` | Intangible Imports 16% - Creditable | Importaciones Intangibles 16% - Acreditable | | `diot_report_paid_8_n_nc` | Paid 8 % N. - Non-Creditable | Pagado al 8% Norte - No acreditable | | `diot_report_paid_8_s_nc` | Paid 8 % S. - Non-Creditable | Pagado al 8% Sur - No acreditable | | `diot_report_paid_16_imp_nc` | Importation 16% - Non-Creditable | Importación 16% - No acreditable | | `diot_report_paid_16_imp_int_nc` | Intangible Imports 16% - Non-Creditable | Importaciones Intangibles 16% - No acreditable | | `diot_report_exempt_imp` | Exempt Imports | Importaciones Exentas | | `diot_report_no_obj` | No Tax Object | No Objeto de Impuesto | ## Why this matters Mexican Odoo instances run with `es_419` locale. Without these inline translations, column headers in the DIOT report display in English for Mexican accountants, even though the equivalent 16% columns already had Spanish translations. This creates an inconsistent bilingual UI on the same report. ## Fix Adds `<field name="name@es_419">` entries to all column records that were missing them, following the same pattern already used by `diot_report_paid_16_wnc`, `diot_report_paid_16_nc`, `diot_report_withheld`, `diot_report_exempt`, and `diot_report_paid_0`. ## Files changed - `addons/l10n_mx/data/account_report_diot.xml`
Fixed an issue where adding all suggested products from the purchase catalog ignored the vendor-specific unit of measure. Purchase orders now use the correct vendor unit and matching suggested quantity, reducing ordering mistakes and manual corrections.
Original PR description
Issue ----- When adding all of the catalog's suggestions at once, the uom specified on the product's vendor lines is not respected. Steps to reproduce ----- - Create a product - Add a vendor line in…
Issue ----- When adding all of the catalog's suggestions at once, the uom specified on the product's vendor lines is not respected. Steps to reproduce ----- - Create a product - Add a vendor line in a different uom - Create a past outgoing shipment of 100 of the product - Create a PO (same vendor as vendor line) - Open the Catalog - Click "Add All" in the suggestion section on the left - Go back to the PO > The product is in units instead of the different uom Cause ----- Clicking the button calls `action_purchase_order_suggest` in Python directly https://github.com/odoo/odoo/blob/3aec1c317aad547b1ad0c85a21cc53f735c5cbfd/addons/purchase_stock/models/purchase_order.py#L131-L134 whereas clicking on a product will go through `addProduct` in JS https://github.com/odoo/odoo/blob/3aec1c317aad547b1ad0c85a21cc53f735c5cbfd/addons/purchase_stock/static/src/product_catalog/record/kanban_record.js#L20-L28 Which leads to `_update_order_line_info` where the purchase line is created https://github.com/odoo/odoo/blob/f59783d43f891fea14d03f67d7c017d7ffbbe5a7/addons/purchase/models/purchase_order.py#L1322-L1328 The uom is then retrieved in the create call through `_suggest_quantity` https://github.com/odoo/odoo/blob/f59783d43f891fea14d03f67d7c017d7ffbbe5a7/addons/purchase/models/purchase_order_line.py#L547-L549 Other issue ----- The quantity of the product also doesn't match the suggestion since the product `suggested_qty` is in the product's uom and not the vendor ones. ----- Ticket: opw-6417665 Forward-Port-Of: odoo/odoo#284841 Forward-Port-Of: odoo/odoo#280934
Website popups are now fully hidden before a page is saved, preventing popup display settings from being saved incorrectly. This makes page editing more reliable when popups are present.
Original PR description
The method `hide` of Bootstrap's `Modal` internally calls `_hideModal` asynchronously. When saving the page, the builder must hide the popups of the page. Calling `hide` does not work well, as the changes done in `_hideModal` are done after the ave is finished. This commit replaces the call to `hide` in the "clean for save" implementation of popup visibility plugin with the internals of `hide` that inpact the content of the saved element (without the delay): - removing the class `show` - calling `_hideModal`
This change ensures that when a business sets Monday as the first day of the week, reports and scheduling calculations consistently group all seven days into the correct week. It prevents weekly hours or limits from being split incorrectly in locales that normally start the week on Sunday.
Original PR description
**Description of the issue/feature this PR addresses:** `weeknumber` takes a `first_week_day` override, documented as `(0 = Monday, ..., 6 = Sunday). If None, derived from the locale.`, but tests it…
**Description of the issue/feature this PR addresses:** `weeknumber` takes a `first_week_day` override, documented as `(0 = Monday, ..., 6 = Sunday). If None, derived from the locale.`, but tests it with `if not first_week_day`, so Monday (`0`) is discarded and the locale's own day is used. `resource` passes `int(get_lang(self.env).week_start) - 1`, which is exactly `0` for Monday, at three call sites. #259600 added the parameter for the opposite case, a Monday locale with a Sunday-start user, and that direction works; this one never has. **Current behavior before PR:** With `en_US` (first day Sunday) and week start set to Monday, `resource_resource.py:299` builds `week_start_date` from Monday while `:303` buckets by Sunday. Monday 2026-08-24 to Sunday 2026-08-30 comes back as W35 for six days and W36 for the Sunday, so one week's hours are split across two buckets and the `hours_per_week` cap lands on the wrong days. **Desired behavior after PR is merged:** The override is honoured and those seven days are all W35. I checked this with a partition property: every day of a `first_week_day`-aligned week must share one `(year, week)`. Over 9 locales x 7 values of `first_week_day` x 12 years, 275940 combinations, 5 locales failed and every failure was in the `first_week_day=0` bucket. After the change there are none. I also diffed every output before and after, 315360 combinations of locale, `first_week_day` and date: 4460 rows change, all of them `first_week_day=0` on a non-Monday locale. `None` and values 1 to 6 are byte identical, so the change is contained to the broken case. Targeting 19.0 because that is the oldest branch carrying the parameter; 17.0 and 18.0 do not have it. AI-assisted: I used Claude to sweep the parameter space and write the tests. Forward-Port-Of: odoo/odoo#285706 Forward-Port-Of: odoo/odoo#285508
This fixes a pricing display issue where product variants could show fewer decimal places than the main product price in reports. Businesses using reports or Studio fields will now see consistent price formatting that respects the configured minimum precision.
Original PR description
Issue: --- Record's `min_display_digits` is not inherited in case of a model inheritance. e.g. `list_price` is inherited to `product.product` from `product.template`, however it renders with fewer decimals than `product.template.list_price` in reports. Steps to reproduce: 1- Set a product's Sales Price to 19.5. 2- Add `product.product.list_price` to a report using studio. The variant's field renders with 1 decimal while `decimal.precision` is 2. Cause: --- This is missed in ee32a17495a10af18f0b5de0a295283c5059d3c2 which introduces minimum precision. opw-6465159 Forward-Port-Of: odoo/odoo#285336 Forward-Port-Of: odoo/odoo#285064
Gantt charts using a weekly view now place tasks in the correct week based on the user's locale, such as weeks starting on Sunday. This prevents tasks from appearing in an extra or incorrect column and keeps planning views accurate for localized configurations.
Original PR description
In Gantt views, for a given focus date, the appropriate time interval is displayed by finding its start and end date, based on the scale. For example, if I focus on 18 may 2026 with a month scale,…
In Gantt views, for a given focus date, the appropriate time interval is displayed by finding its start and end date, based on the scale. For example, if I focus on 18 may 2026 with a month scale, then the whole month of may is displayed. The behaviour was as expected in standard code, because all localisations agree on the beginning of the available scales (day, month, year). In custom code, however, some customer requires to see the gantt charts with a weekly scale. The differences in start of the week based on the localisations and the inconsistencies of use of localStartOf breaks the view. For example, if the localization has the start of the week on a sunday, and a task on the first column starts on a sunday as well, it will get assigned to column before (because it considers sunday as the last day of the previous week). The column before the first column does not exist, so one empty column is created to put the task in it. This commit fixes these inconsistencies so that GanttRenderer behaves as expected with weekly scales, without changing the standard behaviour. Tests are written to check both that the task is assigned to the proper localized week (starting on Sunday) and column (1, not 0). Forward-Port-Of: odoo/enterprise#129408 Forward-Port-Of: odoo/enterprise#118625
DHL deliveries from a company different from the main company now send a valid commercial invoice number. This prevents DHL validation errors and helps international shipments proceed correctly in multi-company setups.
Original PR description
Issue ----- When validating a delivery in a different company from the main one, the commercial invoice's number field is incorrect, which raises an error on DHL end.…
Issue ----- When validating a delivery in a different company from the main one, the commercial invoice's number field is incorrect, which raises an error on DHL end. `content/exportDeclaration/invoice/number: expected type: String, found: Boolean` Steps to reproduce ----- - Create a Belgian company - Setup DHL - DHL Product D - Express Worldwide - Dutiable Material enabled - Create an amrican customer - Deliver a product to the american customer > Validation Error Cause ----- The field is populated in https://github.com/odoo/enterprise/blob/cf3c2fce8a6b7b2d7547d44a0e4423f887986d52/delivery_dhl_rest/models/dhl_request.py#L204 The problem is that `next_by_code` uses the company found in the env, whereas the sequence's company is the main one, so it is not found when doing https://github.com/odoo/odoo/blob/e3b0ca11d99b2ef819cdad68b169112cd73668b6/odoo/addons/base/models/ir_sequence.py#L287 ----- Ticket: opw-6171886 Forward-Port-Of: odoo/enterprise#123170 Forward-Port-Of: odoo/enterprise#118379
The Kenya OSCU invoice form now hides the validation message area when there is no message to show. This removes an unnecessary blank space, making the form layout cleaner and easier to read.
Original PR description
When there is no validation message, an empty div causes a whitespace gap between the header and the sheet in the form view. This commit adds `invisible="not l10n_ke_validation_message"` to the div to prevent this issue. Forward-Port-Of: odoo/enterprise#129364 Forward-Port-Of: odoo/enterprise#129318
This update prevents an error when saving an employee payroll declaration before an employee has been selected. It makes the payroll reporting workflow more reliable by avoiding an unexpected crash in Belgian payroll individual accounts.
Original PR description
When creating an employee declaration without selecting an employee, a traceback occurs. Steps to reproduce the error: - Install ``l10n_be_hr_payroll`` module with demo data - Switch to Belgian…
When creating an employee declaration without selecting an employee, a traceback occurs. Steps to reproduce the error: - Install ``l10n_be_hr_payroll`` module with demo data - Switch to Belgian company - Go to Payroll > Reporting > Individual Accounts > Create a new Individual Account > Click on Eligible Employees > Create a new employee declaration without employee > Save Traceback: ```py ValueError: Expected singleton: hr.employee() ``` https://github.com/odoo/enterprise/blob/000544c3d5b93e194264e15bb73d9599525106e3/hr_payroll/models/hr_payroll_employee_declaration.py#L71 The ``_compute_version_id()`` method calls ``_get_version()``. When ``employee_id`` is empty, ``_get_version()`` is invoked on an empty ``hr.employee`` record, and its ``ensure_one()`` call raises the above traceback at [1]. [1]: https://github.com/odoo/odoo/blob/3c358ae2badad69b125695a97b4a14e8ab77fccd/addons/hr/models/hr_employee.py#L745-L750 Upgrade PR: https://github.com/odoo/upgrade/pull/11026 sentry-7625826444 Forward-Port-Of: odoo/enterprise#125175
The Indian payroll settings now validate EPF Employee IDs using the correct 15-character format. This helps payroll administrators enter compliant establishment identifiers and avoids accepting outdated or incorrect EPF values.
Original PR description
Steps to reproduce: - Install Indian localization, and use an Indian company - Go to Settings under Payroll > Indian Localization - Tick the "Employee Provident Fund (EPF)" - Insert a EPF Employee ID Issue: The "valid" format is not correct: XX/XXX/1234567/000/1234567 with the first series of 7 numbers having a flexible range from 1 to 7. The first series of 7 numbers must always equal to 7 (it represents the establishment ID), and the last series of number should be dropped as it represents the employee's unique PF account number. Solution: - Modify the constraint for the variable: - Remove last 7 trailing digits from the constraint. - Make the length of the first series of 7 digits strictly equal to 7. - Update placeholder value and help info. Task: 6482315 Forward-Port-Of: odoo/enterprise#128777 Forward-Port-Of: odoo/enterprise#128421
This fixes an issue where the online store could accidentally treat unrelated form fields as product option choices. The change helps ensure product selections behave correctly and avoids confusion during shopping.
Original PR description
The selector was matching unrelated inputs because it wasn't specific enough. Forward-Port-Of: odoo/odoo#283742 Forward-Port-Of: odoo/odoo#283464
This fix prevents an extra misleading error from appearing when web unit tests fail. It keeps the memory information in the logs but records it before the test result is finalized, making failed builds easier to diagnose.
Original PR description
Before this commit, a build whose unit tests suite fails collects a second runbot error on top of the real one: odoo.addons.web.tests.test_js.WebSuite.test_unit_desktop.browser: Error received after…
Before this commit, a build whose unit tests suite fails collects a second runbot error on top of the real one:
odoo.addons.web.tests.test_js.WebSuite.test_unit_desktop.browser:
Error received after termination: [MEMINFO] tests done (after GC)
- used: 220535896 - total: 341990868 - limit: 4395630592
Note that the figures themselves are fine (220MB used of a 4.4GB limit): the error is not about memory, only about when the line is logged.
This happens because the runner logs that line after `stop()`, and `stop()` is what reports the suite result: the server settles the test on that report and kills the browser. The final major GC outlasts the report. A passing build escapes because the browser dies within milliseconds; a failing one stays up for the screenshot, long enough for the GC to finish and the line to land after termination.
This commit fixes the issue by moving `stop()` after the final cleanups and their memory log: the [MEMINFO] line is still logged, but before the result report, while the server still waits on the browser.
https://runbot.odoo.com/odoo/error/229655
Forward-Port-Of: odoo/odoo#284957Closing an AI Livechat conversation no longer triggers an unexpected chat window showing the prior AI exchange. This improves the website visitor experience by keeping the close action clean and preventing confusing follow-up messages.
Original PR description
closing the chat with the ai agent on the ai_livechat snippet results in a chatwindow popping up which gives a bad user experience. Steps to reproduce: - Log in as Mitchell Admin. - Go to website. -…
closing the chat with the ai agent on the ai_livechat snippet results in a chatwindow popping up which gives a bad user experience. Steps to reproduce: - Log in as Mitchell Admin. - Go to website. - Click on edit and choose `Contact & Forms`. - Add the AI Livechat website snippet. - From the snippet options, add an AI Agent and choose a livechat team. Make sure that Mitchell Admin is configured as an operator for that livechat team (livechat channel). - Click on save. - Open an incognito tab. Log in as Marc Demo. - Go to Website. - Type a message inside the `ASK AI` text area and press enter. - Wait until you receive a response and then click close. - A chat window will popup with the messages of the conversation with the AI along with a message saying `Visitor has left the channel`. This happens because `close` button will call `closeConversation` => `livechatService.leave()` => `visitor_leave_session` => `_close_livechat_session` that posts a message that the visitor has left the channel. This commit solves the issue by setting the user who left the channel as the author of the `visitor left chat` message instead of OdooBot.
Breadcrumb navigation is restored on several customer portal pages, including signatures, subscriptions, field service, tickets, appointments, and equity pages. This helps users understand where they are and move around the portal more easily.
Original PR description
Problem: Since [this commit][1], breadcrumbs have not been visible from portal pages. Specifically, they have not been visible from the following: * /my/signatures * /my/subcriptions * /my/field-service * /my/tickets * /my/appointments * /my/equity Cause: The change split calls for `portal.portal_layout` and `portal.portal_searchbar`. Previously, `breadcrumbs_searchbar` was set to true just after portal layout was called and this spread into the portal searchbar call. After the commit, `breadcrumbs_searchbar` was no longer being passed to the portal searchbar. As this is what controls the visibility of breadcrumbs on a portal page, they were no longer displayed. Solution: Explicitly call portal searchbar with the `breadcrumbs_searchbar` in each of the above routes' portal page definitions. [1]: https://github.com/odoo/enterprise/commit/99d583d5d6362504b4e838c7825fe9ca8d2b342c task-6365169
This fixes an installation failure in the Brazilian AvaTax Sales module when automatic dependency installation is skipped. The module now includes the needed dependency so sales order tax fields are available, preventing setup errors for affected deployments.
Original PR description
When installing l10n_br_avatax_sale with --skip-auto-install, you'll get an error about the l10n_br fields listed in views/sale_order_views.xml, because these fields don't fully exist without sale_external_tax. This happens because they're defined on a mixin, which is an abstract model. Abstract models only add their fields to a model that actually lists them in `_inherit`. sale.order should list this mixin, but currently doesn't. Adding that dependency is an unstable fix, so it will be added in master (20.0, or 20.1) runbot-237866 Forward-Port-Of: odoo/enterprise#128142
AvaTax invoice PDFs now size section or divider rows correctly when the Taxes column is hidden. This prevents misaligned borders and avoids large blank gaps on longer printed invoices, improving the accuracy and appearance of customer documents.
Original PR description
Steps: 1. Turn on AvaTax for a company (connect it to Avalara). 2. In Settings > General Settings > Invoicing, set the Document Layout to Boxed (Box), just for a clear view. 3. Create a customer…
Steps: 1. Turn on AvaTax for a company (connect it to Avalara). 2. In Settings > General Settings > Invoicing, set the Document Layout to Boxed (Box), just for a clear view. 3. Create a customer invoice: - Use the customer with an AvaTax fiscal position and proper address details. - Add a product with an AvaTax category. - Add a section line (a divider/heading row). - Compute Taxes for the AvaTax. 4. Print the invoice as a PDF. 5. Look at the section/divider row. Its cell border does not line up with the other rows. On longer invoices, this can also cause a big blank gap at the bottom of a page. Cause: When an invoice is computed by AvaTax, the Taxes column is hidden from the PDF (this is intentional, since AvaTax shows tax differently). But a different part of the same template decides how wide the section row should be. This part does not know the Taxes column was hidden, so it still counts it. That makes the section row's width wrong by one column, which breaks the table layout in the PDF. Fix: Instead of hiding the Taxes column only in one place, fix it at the source: the variable that decides (does this invoice show a Taxes column) now also checks if the invoice is an AvaTax invoice. Every other part of the template that uses this variable (the header, the tax cells, and the section row width) automatically gets the right answer, since they all read from the same place. Result: - Section rows on AvaTax invoices now have the correct width. opw - 6455674 Forward-Port-Of: odoo/enterprise#129097
The Project activity menu now opens Project Updates filtered to the current user's own activities and the selected timing bucket, such as late or upcoming. This prevents users from seeing unrelated project updates when following activity counts, making the activity list clearer and more accurate.
Original PR description
**Problem:** Opening a Project Update activity from the activity menu (the clock icon in the systray) lists every project update of every user, instead of the ones carrying the current user's…
**Problem:**
Opening a Project Update activity from the activity menu (the clock icon in the systray) lists every project update of every user, instead of the ones carrying the current user's activities.
**Steps to reproduce:**
1. Schedule an overdue activity on a project update
2. Have a colleague create another project update with no activity
3. Click the clock icon in the systray
4. Under "Project Update", click the "Late" count
**Current behavior:**
The list opens unfiltered and shows all project updates, including the ones of other users and the ones carrying no activity at all.
**Expected behavior:**
The list shows only the updates carrying the user's own activities, restricted to the bucket that was clicked.
**Cause of the issue:**
The activity menu never builds a "my activities" domain. `openActivityGroup` in `mail/static/src/core/web/activity_menu.js` narrows the generic act_window it opens purely through context keys — `search_default_filter_activities_my` plus `search_default_activities_overdue` / `_today` / `_upcoming_all` — and the only domain it forwards comes from `_get_activity_groups`, which is limited to `[('active', 'in', [True, False])]`. A `search_default_<name>` key is resolved against a filter of that name in the model's search view, and is silently dropped when no such filter exists. `project.update`'s search view never declared them, so every key the menu sends is discarded and the action opens on an empty domain.
**Fix:**
`project.project` and `project.task` — the addon's two other `mail.activity.mixin` models — already declare this block of invisible activity filters, as does every other model reachable from the activity menu. Declaring them on `project.update` is what makes the menu's context keys resolvable, and keeps the model consistent with the rest of the codebase instead of special-casing `project.update` on the client side.
opw-6416397
Forward-Port-Of: odoo/odoo#281511This fix prevents an upgrade warning when employee records temporarily have no version information during migration. It helps upgrades complete more cleanly by handling that short-lived data state without raising an error.
Original PR description
### Issue: A warning `IndexError: tuple index out of range` is logged on RunBot when upgrading from 18.0 to 19.0 for employees without any version ### Cause: `_get_version` on `hr.employee` falls…
### Issue: A warning `IndexError: tuple index out of range` is logged on RunBot when upgrading from 18.0 to 19.0 for employees without any version ### Cause: `_get_version` on `hr.employee` falls back to `versions[0]` when no version matches the given date But if the employee has no versions at all, `versions` is empty and `versions[0]` raises an `IndexError` ### Steps to reproduce: - Create a database with `hr_attendance` installed in 18.0 - Upgrade to 19.0 Before the fix, a warning is logged during the upgrade ### Notes: An employee without any version is a transient state during upgrade `hr/saas~18.4.1.1/end-migrate.py` backfills a version for every employee still missing one (`current_version_id IS NULL`) once the whole upgrade chain is done Since it only runs at the very end, an employee can still be found without a version by earlier steps (e.g. modules reloading their demo data), which is when this warning was logged Confirmed on an upgraded RunBot database that employees without a version before the upgrade do end up with one once fully completed runbot-241187 Forward-Port-Of: odoo/odoo#284467
This update prevents unnecessary warning messages when Odoo uses newer operating system versions of certificate software. It keeps email and certificate-related connections working smoothly across different deployment environments.
Original PR description
pyOpenSSL 24.3.0 deprecated passing its own X509/PKey objects to Context.use_certificate()/use_privatekey(), and started accepting cryptography objects instead. Odoo pins pyopenssl 24.1.0, but the distro builds run the version shipped by the OS: since the test added by f0fb287c6502 covers that path, they now add a warning in the logs. Load the certificate and the key as cryptography objects when the installed pyOpenSSL supports them, keep the previous loaders otherwise. Reference: https://github.com/pyca/pyopenssl/commit/b0cb4b4 This fix is based on https://github.com/odoo/odoo/blob/a2b4f618328f3ce3f654fd2c1ee4410365706a7e/odoo/addons/base/models/ir_mail_server.py#L34-L46 runbot-944176 Forward-Port-Of: odoo/odoo#285569 Forward-Port-Of: odoo/odoo#277449
Restaurant table orders now keep the entered number of guests when opened from another device. This prevents staff from being asked to enter the same guest count again, reducing interruptions and keeping service flow smoother.
Original PR description
Steps to reproduce: - Enable presets on a restaurant PoS and tick "Amount of Guests" on the preset used for tables - On device A, open a table and enter the number of guests - On device B, open the…
Steps to reproduce: - Enable presets on a restaurant PoS and tick "Amount of Guests" on the preset used for tables - On device A, open a table and enter the number of guests - On device B, open the same table Issue: Device B pops the guest count numpad again, even though the guest count was already entered on device A. Cause: ensureGuestCustomerCount guarded the popup on order.uiState.guestSetted. uiState is only serialized to IndexedDB (SERIALIZED_UI_STATE_PROP, used by serializeForIndexedDB); it is never sent to the server, so the flag is local to one browser and a second device always considers the guest count as not yet asked. customer_count is synced and could carry that information, but PosStore createNewOrder pre-filled it with the table seats, so it was never 0 for a table order and could not tell "not asked" from "answered". Fix: Stop storing the seats default on the record and expose it from getCustomerCount() instead, so customer_count == 0 means "no guest count entered yet". ensureGuestCustomerCount now guards on that synced value, so an order whose guest count was entered on another device is not asked for it again. Every display goes through getCustomerCount(), so the values shown on the Guests button, the receipt and the preparation ticket are unchanged. opw-6470180 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285444 Forward-Port-Of: odoo/odoo#283002
Original PR description
Before this commit- we used to check only if the vat has a value or not. But in odoo we also use `/`, `NA`, `na` as False values for the vat After this commit- We add a compute field `has_vat`, to check if the vat has any default falsy value or not Related PR- https://github.com/odoo/enterprise/pull/111396 task-5935566 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
Before this commit- we used to check only if the vat has a value or not. But in odoo we also use `/`, `NA`, `na` as False values for the vat After this commit- We add a compute field `has_vat`, to check if the vat has any default falsy value or not related PR- https://github.com/odoo/odoo/pull/248132/ task-5935566