Tuesday, September 8, 2026
53 changes · saas-19.4
New functionality added to Odoo
This adds optional Turkish Nilvera e-invoicing support for commercial invoices and return invoice types. Businesses can now send credit notes as return invoices linked to the original invoice, and manage accept/reject responses for commercial invoices directly in Odoo.
Original PR description
This commit is a backport of [1] and [2], which were merged in master, delivered as a new optional module for 19.0. Adds the Commercial (TICARIFATURA) invoice scenario and the "Return" (IADE) and "Withholding Return" (TEVKIFATIADE) invoice types to the Nilvera e-invoicing flow: - Credit notes can be sent to Nilvera as return invoices with a reference to the original invoice. - Commercial invoices/bills track the counterpart's response: accept or reject commercial bills from Odoo and synchronize responses from Nilvera. [1]: https://github.com/odoo/odoo/pull/245173 [2]: https://github.com/odoo/odoo/pull/246950 task-6365900 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278285
Enhancements to existing features
Duplicating sections with many lines is now much faster and less likely to time out. The system batches required updates into a single request instead of making repeated requests for every copied line, improving performance for large sales documents.
Original PR description
Issue: Sections containing a large number of subsections or lines can take too long to duplicate and may eventually time out. The slowdown stems from the onchange issues in `_duplicateRecords()` in…
Resolved issues and error corrections
The live map now shows technicians as their locations are found instead of waiting for every address lookup to finish. This reduces waiting time when OpenStreetMap is used and helps dispatchers see field staff on the map sooner.
Original PR description
Opening the live map took a long time whenever technicians were sharing their live location and OpenStreetMap was used to find their address, since only one address lookup can be done per second, and every technician had to be fully processed before anything was shown on the map. Technicians are now displayed on the map as soon as their address is found, one by one, instead of waiting for all of them at once, similar to the way customer pins are already handled. task-6524180
Issue: Sections containing a large number of subsections or lines can take too long to duplicate and may eventually time out. The slowdown stems from the onchange issues in `_duplicateRecords()` in sale.order.line. Each sale.order.line is first created as an empty datapoint, which triggers an onchange RPC. The copied values are then applied, triggering a second onchange RPC for every duplicated line. Fix: Prepare the copied values before creating the datapoints and send them through a batched onchange. This retrieves the required onchange values for all duplicated lines in a single RPC. Benchmarks: sale.order.line onchanges: Note: "Timing Before" is calculated by the difference between the first and last sale.order.line onchange completion times. | Lines | RPCs Before | RPCs After | Timing Before | Timing After | Speedup | | ----: | ----------: | ---------: | ------------: | -----------: | ------: | | 10 | 20 | 1 | 0.31s | 0.04s | 5.52x | | 50 | 50 | 1 | 1.62s | 0.14s | 11.67x | | 100 | 200 | 1 | 3.41s | 0.25s | 13.64x | | 500 | 1000 | 1 | 10.98s | 0.96s | 11.43x | | 1000 | 2000 | 1 | 31.07s | 2.74s | 11.34x | Related: opw-6395475 Forward-Port-Of: odoo/odoo#280418
The web interface framework used by Odoo has been updated to its latest Owl version. This helps keep the platform current and can improve reliability and maintainability of future web interface changes.
Original PR description
Release notes: https://github.com/odoo/owl/releases/tag/v3.0.0-alpha.49 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 test suite now includes continuous production scenarios again for shop floor work orders. This helps ensure users can reliably mark work orders as done after a previously fixed limitation.
Original PR description
During the continuous production cleaning, a limitation was introduced that blocked users from marking a WO as done in shopfloor. This limitation was addressed and fixed in: odoo/enterprise#125787 , so we can safely uncomment the continuous flag to ensure we are covering different usecases in the tests.
Timesheet suggestions from Gmail and Calendar now use the most recent relevant project across a customer’s full company/contact hierarchy, rather than only one matching contact. This helps users log time to the right project more consistently and reduces manual correction.
Original PR description
Before this commit: - Gmail emails are resolved to a random timesheeted project linked to a partner having the same email - Calendar events are resolved to the most recent timesheeted project linked to partner_ids In this commit: - Instead of looking to the partner, the most recent timesheeted project is taken from the partner tree (child_ids, parent_id) task-6254947 Forward-Port-Of: odoo/enterprise#128801
Opening a point-of-sale register for a newly created company is now much faster in databases with very large accounting histories. The change avoids slow database searches across existing accounting entries, reducing delays from seconds to milliseconds in large multi-company environments.
Original PR description
When you create a new company in a database that has a lot of existing account move lines and you attempt to open a PoS register from the list view, `_compute_company_has_template` checks…
When you create a new company in a database that has a lot of existing
account move lines and you attempt to open a PoS register from the list
view, `_compute_company_has_template` checks `_existing_accounting` for
the new company and wil run a sequential scan on the entire account_move_line
table followed by a nested loop as the query planner assumes AMLs company_ids
will be roughly evenly distributed.
This is not the case in a new company that has no/very few AMLs.
This is because the query ran is:
`SELECT COUNT(*) FROM
(SELECT FROM "account_move_line"
WHERE (
"account_move_line"."company_id" IN
(SELECT "res_company"."id" FROM
"res_company" WHERE
("res_company"."parent_path" LIKE '3/%')
))
LIMIT 1)`
and the values of company_id being searched for aren't known until the
subquery runs.
Running a query more like
`SELECT COUNT(*) FROM
account_move_line
WHERE company_id IN (%s)`
is much faster
Since res_company will always be a smaller table, we can do the inexpensive
search first and then pass in the values so Postgres can do a cheaper
search and return faster.
Benchmark time of `_existing_accounting`:
| Company 1 AML count | Company 2 AML count | Pre-fix | Post-fix | Multiplier |
|---|---|---|---|---|
| 10,000,000 | 0 | 700 Milliseconds | 500 Microseconds | 1,400x |
| 20,000,000 | 0 | 1.35 Seconds | 1 Millisecond | 1,350x |
| 20,000,000 | 20,000,000 | 2 Milliseconds |1.3 Milliseconds | 1.5x |
| 50,000,000 | 0 | 3.25 Seconds | 1 Millisecond | 3,250x |
| 100,000,000 | 0 | 5.3 Seconds | 1.3 Milliseconds | 4,075x |
Query Plan Before:
```
"Aggregate (cost=0.08..0.09 rows=1 width=8) (actual time=5573.001..5573.002 rows=1.00 loops=1)"
" Buffers: shared read=571435"
" -> Limit (cost=0.00..0.08 rows=1 width=0) (actual time=5572.995..5572.997 rows=0.00 loops=1)"
" Buffers: shared read=571435"
" -> Nested Loop (cost=0.00..https://github.com/odoo/odoo/commit/1571439c0c70c1f1dc3229421e696b97ce1678f8.33 rows=20000086 width=0) (actual time=5572.988..5572.989 rows=0.00 loops=1)"
" Join Filter: (account_move_line.company_id = res_company.id)"
" Buffers: shared read=571435"
" -> Seq Scan on account_move_line (cost=0.00..971432.72 rows=40000172 width=4) (actual time=0.432..1913.934 rows=40000000.00 loops=1)"
" Buffers: shared read=571431"
" -> Materialize (cost=0.00..4.03 rows=1 width=4) (actual time=0.000..0.000 rows=0.00 loops=40000000)"
" Storage: Memory Maximum Storage: 17kB"
" Buffers: shared read=4"
" -> Seq Scan on res_company (cost=0.00..4.03 rows=1 width=4) (actual time=0.785..0.785 rows=0.00 loops=1)"
" Filter: ((parent_path)::text ~~ '3/%'::text)"
" Rows Removed by Filter: 2"
" Buffers: shared read=4"
"Planning:"
" Buffers: shared hit=574 read=77"
"Planning Time: 15.323 ms"
"Execution Time: 5573.060 ms"
```
Query Plan After:
```
"Aggregate (cost=4.46..4.47 rows=1 width=8) (actual time=1.972..1.973 rows=1.00 loops=1)"
" Buffers: shared read=3"
" -> Limit (cost=0.44..4.46 rows=1 width=0) (actual time=1.967..1.968 rows=0.00 loops=1)"
" Buffers: shared read=3"
" -> Index Only Scan using account_move_line__company_id_index on account_move_line (cost=0.44..4.46 rows=1 width=0) (actual time=1.965..1.966 rows=0.00 loops=1)"
" Index Cond: (company_id = 3)"
" Heap Fetches: 0"
" Index Searches: 1"
" Buffers: shared read=3"
"Planning:"
" Buffers: shared hit=3"
"Planning Time: 0.219 ms"
"Execution Time: 1.998 ms"
```
opw-6513885
Forward-Port-Of: odoo/odoo#285096Dutch SBR and ICP report submissions are now routed through Odoo's IAP proxy instead of connecting directly to Digipoort. This simplifies the submission process and lets businesses use either their own certificate or Odoo's group certificate.
Original PR description
*: l10n_nl_reports_sbr{,_icp,_status_info}
---
Description of the issue this commit addresses:
Dutch SBR and ICP reports still send directly to Digipoort with duplicated SOAP/signature code and no shared group-certificate flow.
---
Desired behavior after this commit is merged:
This commit routes both reports through IAP proxy signing/sending, and lets users submit with either a personal certificate or Odoo's group one.
---
IAP PR: https://github.com/odoo/iap-apps/pull/1593
task-3439634
Forward-Port-Of: odoo/enterprise#117617Integer quantities on point of sale preparation receipts now display as whole numbers instead of decimals when printed through self-order devices. This makes kitchen or preparation receipts clearer and avoids confusion for staff fulfilling orders.
Original PR description
Fix issue where integer qty were displayed as float in preparation receipt when printed through obox (self order). task-id: 6545678 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The restaurant point of sale test now waits until an order has fully finished saving before ending. This prevents false test failures where the system checked the order too early and saw outdated quantities or edit status.
Original PR description
The order tracking tour only waited for the feedback screen to be shown after validating the payment. Since order validation is performed asynchronously while the feedback screen is displayed, the tour could finish before the updated order was synced to the backend. This caused the Python test to still see the original quantity and `is_edited` set to false. To fix we wait for the feedback screen continue button to be enabled, which ensures order validation and synchronization have completed before the tour ends. [error-940386](https://runbot.odoo.com/odoo/error/940386) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282726
This fix prevents an accounting calculation from failing when no end date is provided. The system now uses today's date by default, helping financial workflows continue without interruption.
Original PR description
`date_to` is accessed directly from `self.env.context` in `_compute_sql_consolidation_rate`. When the key is missing from the context, this raises an error and breaks the flow. Use today's date as the default value when `date_to` is not provided in the context, preventing the traceback and allowing the computation to continue normally.
Vendor bills can now correctly create assets that do not need depreciation. This removes an unnecessary restriction, helping accounting teams record those assets without manual workarounds.
Original PR description
This commit fixes the ability to create no depreciation assets from vendor bills. Previously, a condition on the depreciation account and expense account restricted the asset creation. backport of https://github.com/odoo/enterprise/pull/123070 task-6283929 opw-6540498 Forward-Port-Of: odoo/enterprise#130653
This change prevents an error when users edit the prefix or suffix of a sequence that uses date-based subsequences. It keeps the sequence form usable during edits by safely handling temporary records before they are fully saved.
Original PR description
Currently an exception is generated when the user tries to change the value of `Prefix` or `Suffix` in sequence as the following steps - Create a sequence with `Use subsequences per date_range` and…
Currently an exception is generated when the user tries to change the value of `Prefix` or `Suffix` in sequence as the following steps - Create a sequence with `Use subsequences per date_range` and add any `From` and `To` dates - Save record > Change the value of `Prefix` or `Suffix` Error: `TypeError: %d format: a real number is required, not NewId` This issue was introduced by the recently refactored changes in commit [1], which added an onchange method for the prefix and suffix fields. When the user changes either field, the onchange is triggered and recomputes all dependent fields, including `_get_number_next_actual` on `ir.sequence.date_range`. During this computation, the record contains the `NewId` record. As a result, using `%03d` to format the record ID raises the reported error, since NewId cannot be formatted as an integer. This commit fixes the issue by defaulting `number_next_actual` to `0` when `_get_number_next_actual` is invoked with a `NewId` for the related sequence while computing the value during `onchange`. [1]: https://github.com/odoo/odoo/commit/387b2289da68454599666bf96844b22c41c5cc55 Sentry-7609468821
Users can now select Unsplash images in product and other relational image fields without hitting a save error. The change ensures external Unsplash images are properly converted into Odoo attachments before being linked to the target record.
Original PR description
**Description of the issue/feature this PR addresses:** When selecting Unsplash images from a relational field (which uses `CustomMediaDialog`), the images would fail to process or link correctly to…
**Description of the issue/feature this PR addresses:** When selecting Unsplash images from a relational field (which uses `CustomMediaDialog`), the images would fail to process or link correctly to the target record. In the web editor, Unsplash image selections are handled by patching the base `MediaDialog`. When a user selects an Unsplash image, that patch intercepts the save action and uses the `unsplash` service to notify the backend. The server then fetches the external Unsplash URL and converts it into a native `ir.attachment` record before the frontend completes the save. Because `CustomMediaDialog` is a distinct component used for relational fields, it bypassed the existing `MediaDialog` patch entirely and lacked this specialized fetch-and-convert logic. This commit introduces a parallel patch specifically for `CustomMediaDialog`. It intercepts `imageSave`, routes any Unsplash records through the `unsplash` service, and replaces the raw Unsplash records with the newly generated Odoo attachments before executing the underlying save. **Steps to reproduce:** - POS > Products > Products > choose any product > click the ‘edit’ button in the image > search something, e.g. ‘burger’ > add Unsplash Access Key and Application ID when prompted > select one of the resulting Unsplash images **Current behavior before PR:** - Error when saving an unsplash image when editing product images **Desired behavior after PR is merged:** - No error when saving an unsplash image when editing product images opw-6445843 Forward-Port-Of: odoo/odoo#283694
Employee unavailable time is now calculated consistently across Time Off and Attendance, including periods outside a contract and flexible schedules. This ensures calendars correctly grey out unavailable days and gives managers a more reliable view of employee availability.
Original PR description
purpose: 1- We should have a consistent way to compute `_gantt_unavailability` of employees in time off and attendance. Currently, some cases have inconsistent behavior such as out of contract days,…
purpose:
1- We should have a consistent way to compute `_gantt_unavailability` of employees in time off and attendance. Currently, some cases have inconsistent behavior such as out of contract days, flexible and fully flexibe employees. 2- In time off calendar view, if the employee does not have a contract at all, the current working schedule will appear in the calendar and it will not be greyed out. This is inconsistent with the behavior of the attendance application.
Fix:
1:
- implemented `_get_employee_unavailable_intervals` in employee model to be used in both time off and attendance.
- more optimized than the old implementation in time off as it calls `_work_intervals_batch` once per calendar instead of calling it for each contract in `_unavailable_intervals_batch`
- greys out "out of contract" periods
- for flexible and fully flexible employees, the whole period is considered available except leave periods
- for duration based calendars, morning and afternoon map to 12 hours of availability and full day maps to 24 hours of availability
- made `_get_calendar_periods` use version date start instead of contract date start and corrected a bug in tz conversion 2:
- made `_get_unusual_days` return True for all the days outside of contracts for the employee instead of not returning anything for them or getting values from the working schedule of the employee (means that they will be greyed out in the callendar view) and added a test for it
task-id: 5473055
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#286854
Forward-Port-Of: odoo/odoo#258604Inventory users without Accounting permissions can now view and create Indian E-Waybills without encountering access errors. This helps warehouse teams complete shipping documentation smoothly without needing extra accounting rights.
Original PR description
Before this commit Inventory users without Accounting permissions could get an Access Error when viewing or creating an E-Waybill because they could not access the required document types. After this commit Inventory users can now view and create E-Waybills without an Access Error. task-6515093 Forward-Port-Of: odoo/odoo#285023
The Field Service Gantt schedule now opens without waiting for buffer time calculations to finish first. This helps users see their planning view sooner while the remaining timing details update in the background.
Original PR description
This commit fixes a potential performance issue in the Gantt view of Field Service when computing the buffer times. Prior to this commit, the view waited for the buffer times to be computed before rendering. With this commit, we trigger the buffer time computation in the background (i.e., fire and forget), while letting the view to render. As buffer times are computed, the view will be notified. no-task
Rental order lines created from the rental schedule now keep normal product names instead of adding stock quantities to the line description. Stock quantities still appear where intended in the schedule rows, reducing confusion for users preparing rental orders.
Original PR description
Versions -------- 19.0 and later Steps ----- - Install `sale_stock_renting`. - Go to Rental > Orders > Rental Schedule. - Create a new rental order line from a cell of the gantt view, on a row…
Versions -------- 19.0 and later Steps ----- - Install `sale_stock_renting`. - Go to Rental > Orders > Rental Schedule. - Create a new rental order line from a cell of the gantt view, on a row grouped by a storable rentable product. Issue ----- The first line of the description of the created line is named "Bike (3 items)" instead of "Bike". Cause ----- The `display_name` override adding that quantity is keyed on the `in_rental_schedule` context key. That key is set on the `action_rental_order_schedule` action itself, so it is part of the search context and is propagated to every record, dialog and dropdown opened from the schedule, while it is only meant to flag that we are in the schedule (default values conversion, hidden onchange buttons, group expansion, ...). Solution -------- Introduce a dedicated `display_renting_stock_quantity` context key and depend on it instead when fetching data to build the gantt rows, leaving the records opened from the schedule with their regular display name. Forward-Port-Of: odoo/enterprise#130651 Forward-Port-Of: odoo/enterprise#130322
Employee availability is now shown consistently across Attendance and Time Off planning views. Days outside an employee's contract are correctly greyed out, while flexible work schedules and leave periods are handled more predictably, reducing confusion for managers reviewing calendars.
Original PR description
purpose: 1- We should have a consistent way to compute `_gantt_unavailability` of employees in time off and attendance. Currently, some cases have inconsistent behavior such as out of contract days,…
purpose:
1- We should have a consistent way to compute `_gantt_unavailability` of employees in time off and attendance. Currently, some cases have inconsistent behavior such as out of contract days, flexible and fully flexibe employees. 2- In time off calendar view, if the employee does not have a contract at all, the current working schedule will appear in the calendar and it will not be greyed out. This is inconsistent with the behavior of the attendance application.
Fix:
1:
- implemented `_get_employee_unavailable_intervals` in employee model to be used in both time off and attendance.
- more optimized than the old implementation in time off as it calls `_work_intervals_batch` once per calendar instead of calling it for each contract in `_unavailable_intervals_batch`
- greys out "out of contract" periods
- for flexible and fully flexible employees, the whole period is considered available except leave periods
- for duration based calendars, morning and afternoon map to 12 hours of availability and full day maps to 24 hours of availability
- made `_get_calendar_periods` use version date start instead of contract date start and corrected a bug in tz conversion 2:
- made `_get_unusual_days` return True for all the days outside of contracts for the employee instead of not returning anything for them or getting values from the working schedule of the employee (means that they will be greyed out in the callendar view) and added a test for it
task-id: 5473055
Forward-Port-Of: odoo/enterprise#130587
Forward-Port-Of: odoo/enterprise#113498This fix changes how the mail module records active database transactions, using a compact list instead of a large mostly empty map. It reduces memory usage significantly in cases with long-running transactions, helping avoid memory errors and improving reliability.
Original PR description
The store version snapshot encoded in progress transactions (xip) as a bitmap spanning the whole [xmin, xmax) range, so its size grows with how far apart those bounds are rather than with how many transactions are actually in progress. A single long-lived transaction can push that range into the hundreds of thousands, leading to memory errors, most of it being zeroes. Sending it as a list of strings is much smaller (95-98% smaller tested on odoo). 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
This fix updates Point of Sale stock test flows so they correctly find customers even when many demo customer records are present. It helps prevent automated test failures caused by customer search results being limited to the initially loaded list.
Original PR description
When running tests with demo data, the partner list is populated with many records, causing 'Partner Test 1' to fall outside the initial 100 loaded partners in the PoS session cache. Because clickCustomer defaulted to pressEnter=false, searching in the UI filtered the in-memory cache and displayed 'No customers found, press Enter to load more.', but never dispatched the Enter key to fetch the partner from the backend, timing out the tour step. Pass pressEnter=true in pos_stock customer selection tours so that the Enter key is dispatched and the partner is fetched from the server via RPC. runbot-error: 242001 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286513
This fix ensures that when a copied or restored database is neutralized, scheduled background jobs cannot start before the neutralization is complete. This reduces the risk of unwanted automated actions, such as emails or integrations, running from a database that is meant to be safely inactive.
Original PR description
Before this commit: If you restore a backup or duplicate a database via /web/database/manager with "Neutralize" enabled, the cron workers had a small window between the creation of the Registry and the neutralization of the database where they could try to execute crons. Since neutralize_database does not require a full registry to run (it only does raw SQL operations), we can run it before the Registry creation (which has the effect, among other things, of making the cron workers aware of the new database). opw-6517819 Forward-Port-Of: odoo/odoo#286870 Forward-Port-Of: odoo/odoo#286532
This fixes a crash in Discuss when users selected emojis from search results and then cleared the search field. The emoji picker now keeps a stable view of recently used emojis while it is open, preventing the interface from breaking during normal chat use.
Original PR description
Steps to reproduce: - open Discuss, open any chat, open the emoji picker (no 'Frequently used' emojis) - search a term and select emojis without closing the picker (shift+click on desktop, plain…
Steps to reproduce:
- open Discuss, open any chat, open the emoji picker (no 'Frequently used'
emojis)
- search a term and select emojis without closing the picker (shift+click on
desktop, plain click on mobile)
- clear the search with backspace
=> traceback: 'Cannot read properties of null (reading
`getBoundingClientRect`)' in adaptNavbar().
This happens because when we clear the search input it calls
`highlightActiveCategory()`, which sets `categoryId` to the topmost category of
the grid, which is now the 'Frequently used' category (sortId 0), added to the
picker since the emojis we just picked updated the recent state. To update the
navbar, `currentNavbarPanel` then looks for the panel holding it in
`emojiNavbarRepr`, but that representation is only built in `adaptNavbar()`,
which runs on mount and from the `ResizeObserver` only, so it was built without
the 'Frequently used' category and no panel contains it. It returns undefined,
the navbar renders empty, its size change wakes the `ResizeObserver`, and
`adaptNavbar()` crashes on querySelector('.o-Emoji').getBoundingClientRect()`.
This commit solves the issue by rendering the `recentEmojis` from a snapshot
taken when the picker is opened, so they are not added to the picker while the
while `emojiNavbarRepr` does not contain their category id.
partial backported PR: https://github.com/odoo/odoo/pull/281104
Task-[6204249](https://www.odoo.com/odoo/project/1519/tasks/6204249)
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#286986
Forward-Port-Of: odoo/odoo#284372Export invoices in Argentina now use the correct recipient identification code in their QR data. This prevents ARCA from showing the wrong ID type and allows these invoices to be verified as valid legal documents on the ARCA verification page.
Original PR description
Before this change we were sending id type code 0 and this generate two problems * ARCA verification page it was wrongly taking "CI Policia Federal" as the identification type of the receptor * We were not able to validate the expo invoice, we get always an error With this change the expo invoice can be checked as a real legal document in the ARCA page https://servicioscf.afip.gob.ar/publico/comprobantes/cae.aspx Forward-Port-Of: odoo/enterprise#126704
Email delivery failure messages now display the configured outgoing mail server name instead of 'None'. This makes it easier for users and support teams to identify which email server caused a sending problem.
Original PR description
Steps to reproduce: 1. Run a local SMTP server that responds with a server error. The server file is provided in the task [refuse_smtp.py](https://github.com/user-attachments/files/27166855/refuse_smtp.py) 2. Create an outgoing server for this SMTP server 3. Send an email Issue: The delivery failure reason displays Mail delivery failed via SMTP server 'None' instead of the configured server name. Cause: ir.mail_server.send_email() builds the failure message from the smtp_server argument, but in the common path the mail is sent via mail_server_id. In that case, the actual SMTP server is resolved in connect(), while smtp_server remains unset, so the error message shows None. Solution: Store the resolved server label on the SMTP connection when opening it, and reuse that value when formatting send failures. opw-6139168 Forward-Port-Of: odoo/odoo#280750 Forward-Port-Of: odoo/odoo#261776
Preparation and pickup receipts in Point of Sale now use the shop/company timezone instead of the account that happens to print them. This prevents customer-facing tickets from showing incorrect UTC times, reducing confusion around order pickup or preparation schedules.
Original PR description
Preparation tickets are rendered server-side for self orders. format_datetime and format_time only fall back on env.user.tz, and the render runs under whichever user triggered it: the public user for an online payment confirmed on /payment/status/poll, OdooBot for the payment cron, the self ordering default user for an OBOX print. None of them is guaranteed to have a timezone, so the ticket could be printed in UTC: an 18:15 pickup showed as 16:15 Take the timezone from res.company.tz instead. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285370
This fix checks for an existing local proxy user before contacting Odoo's online service to create a new one. It prevents rare race conditions that could leave a company database with outdated credentials and cause later electronic invoicing proxy calls to fail.
Original PR description
In the current flow, _register_proxy_user first calls IAP create_user, then inserts the returned credentials in account_edi_proxy_client.user. At the same time, IAP create_user_2 may replace an existing user with a new one with different credentials before local persistence settles. So Tx A calls IAP and gets credentials for remote user U1. Tx B calls IAP and gets credentials for remote user U2 and unlinks U1. Tx A persists U1 locally. Tx B fails local insert due to a unique constraint. Client DB keeps U1 credentials, but IAP now expects U2. Subsequent proxy calls from the client fail. The DB is left with stale, unusable credentials and cannot recover without re-registration. IAP: https://github.com/odoo/iap-apps/pull/1816 task-6520501 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285648
This fixes an issue where dropshipped kit products could show a zero cost on sales orders when using FIFO or average costing. Sales margins now reflect the purchase and component costs more accurately, helping businesses avoid understated costs and misleading profitability figures.
Original PR description
Currently, when a user dropships a kit product with FIFO/AVCO category costing, the computed cost of the kit on the Sales Order becomes 0. ## Steps to replicate: - Install Sales, Purchase, and…
Currently, when a user dropships a kit product with FIFO/AVCO category costing, the computed cost of the kit on the Sales Order becomes 0. ## Steps to replicate: - Install Sales, Purchase, and Manufacturing modules. - Enable Margins and Dropshipping in settings. - Go to Product Categories > Goods and set costing method to FIFO - Create two products MOBO and CPU: - Cost: $300 - Category: Goods - Add a vendor in Purchase section with unit price same as cost - Enable Dropshipping route - Create a kit product Computer Kit with the same configuration as above (except cost) and add MOBO and CPU as components on its BoM. - Open the Computer Kit and click Compute Price from BoM. - Create and confirm a Sales Order with the Computer Kit. - Confirm the related Purchase Order and validate the dropship picking. - Return to the Sales Order > make the `Cost` field visible on SO lines . ## Observed Behavior: The product cost appears as 0 on the Sales Order, even though a price is set on the related Purchase Order. ## Root cause: When the dropshipping picking is confirmed, the method `_compute_purchase_price` is triggered to compute the cost on the Sales Order line. It calls `_get_price_unit_delivery` at [1], which then calls `_get_price_unit_dropshipped` at [2] since the products are dropshipped. Because dropshipping moves do not carry stock values, it calls `_get_value` at [3] to determine an appropriate value. This method uses `_get_value_data` at [4], which retrieves the value from the quotation via `_get_value_from_quotation` at [5]. Here, a cost ratio is applied at [6] to distribute the cost based on the BoM cost share (i.e., the percentage split of cost across kit components). Since no cost share is defined on the BoM, the ratio is 0, causing the final computed cost ratio to also be 0 at [7] and the cost value being returned as zero as shown in at [6]. **Why not in lower versions?** This issue did not occur in versions 18.4 and earlier due to the presence of the stock valuation layer and the defined logic for kit products to calculate cost, as shown in [8]. [1]: https://github.com/odoo/odoo/blob/d92feb4a6f463cae59aa107f46b5699f83f8ef9c/addons/sale_stock_margin/models/sale_order_line.py#L18-L21 [2]: https://github.com/odoo/odoo/blob/d92feb4a6f463cae59aa107f46b5699f83f8ef9c/addons/stock_account/models/stock_move.py#L672 [3]: https://github.com/odoo/odoo/blob/d92feb4a6f463cae59aa107f46b5699f83f8ef9c/addons/stock_account/models/stock_move.py#L681-L684 [4]: https://github.com/odoo/odoo/blob/d92feb4a6f463cae59aa107f46b5699f83f8ef9c/addons/stock_account/models/stock_move.py#L336 [5]: https://github.com/odoo/odoo/blob/d92feb4a6f463cae59aa107f46b5699f83f8ef9c/addons/stock_account/models/stock_move.py#L392-L397 [6]: https://github.com/odoo/odoo/blob/d92feb4a6f463cae59aa107f46b5699f83f8ef9c/addons/purchase_stock/models/stock_move.py#L225-L241 [7]: https://github.com/odoo/odoo/blob/d92feb4a6f463cae59aa107f46b5699f83f8ef9c/addons/purchase_mrp/models/stock_move.py#L12-L25 [8]: https://github.com/odoo/odoo/blob/7f1cd04259202bcafc94965d6420df360f9152c1/addons/mrp_account/models/product.py#L65-L89 ## Solution: It should not be assumed that users will always define a cost share on the Bill of Materials. In many cases, they may expect the kit price to be derived directly from the costs of its component products. To support this, we can override `_get_price_unit_dropshipped` to properly handle kit products, ensuring the cost is computed based on the component product costs instead. opw-6113398 Forward-Port-Of: odoo/odoo#282616 Forward-Port-Of: odoo/odoo#261705
This fixes incorrect Spanish TicketBAI reporting when a POS order uses a gift card during a refund-related transaction. Gift card lines are now signed consistently with the overall order, preventing mismatches between product totals and invoice totals in official XML reporting.
Original PR description
The issue fixed in commit[1] is again reproducible after commit [2] the pos order line for gift card is now not considered as a refund Step to reproduce: - Install pos_loyalty and…
The issue fixed in commit[1] is again reproducible after commit [2] the pos order line for gift card is now not considered as a refund Step to reproduce: - Install pos_loyalty and l10n_es_edi_tbai_pos with demo data - Create a gift card (add a tax to the discount product, any 0%) - start pos, add a product and use the gift card - fulfill the order - go to backend and open that order - In the TicketBAI XML, the values for the giftcard product will be positive, causing an inconsistency between the product line total and the invoice total [1] https://github.com/odoo/odoo/commit/0bbc5ebdcc7e014d87130b2ff9cee98e3aa7479a [2] https://github.com/odoo/odoo/commit/b17c9713e7a3305c240298fa54fcf1bc87a9bb8c FIX - we used to determine `sign` based on each order line's `is_refund` property - this property is quite sensitive as it depends on factors like line's qty, price, is reward or not. - so its better to depend on order's refund property for sign reversal https://github.com/odoo/odoo/blob/4fef2c5b69fac10594fac81b149d542ee4621b13/addons/point_of_sale/models/pos_order.py#L1768 opw-6226003 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286448 Forward-Port-Of: odoo/odoo#278042
Point of Sale settings now remove linked preparation printers when the preparation printer option is turned off. This prevents old printer settings from remaining active unintentionally and keeps restaurant or kitchen printing configuration consistent.
Original PR description
Before, when the user was unticking the preparation printer checkbox, the preparation printers were not cleared. This fix ensure that when you set the boolean to false, the preparation printers are cleaned. task-id: 6484254 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283548
Odoo now recognizes five new response codes introduced by Chile's tax authority (SII). This prevents affected supplier electronic documents from getting stuck during processing after the regulatory update.
Original PR description
**Before this PR:** After the implementation of Resolution 161 of November 13th 2025, SII responses included keys not supported by the current l10n_cl_edi implementation. This resulted in supplier DTEs not being processed as they were before the change, because the five new keys were not found in Odoo's current `l10n_cl_claim` field, causing that the documents with these responses, were kept in a loop not solved. **After this PR:** The five new values from the resolution, along with their translations, were added to the selector field, fixing the process flow. **SII Reference:** https://www.sii.cl/normativa_legislacion/resoluciones/2025/reso161.pdf (see Event Code, page 5) Forward-Port-Of: odoo/enterprise#121833
Fixes incorrect totals on French association balance sheets so active and passive amounts are calculated accurately. This prevents missing or double-counted accounting entries, giving organizations more reliable financial reports.
Original PR description
## [FIX] l10n_fr_reports: fix missing lines in association balance sheet ### Issue: The Active part of the Balance Sheet for associations shows an incorrect `TOTAL ACTIVE` — some accounts are not…
## [FIX] l10n_fr_reports: fix missing lines in association balance sheet ### Issue: The Active part of the Balance Sheet for associations shows an incorrect `TOTAL ACTIVE` — some accounts are not propagated to their parent aggregations ### Cause: `ACTIF_IMMOBILISE` was missing `BIEN_PAR_DONATION` in its formula `ACTIF_CIRCULANT` was missing both `DISPONIBILITES` and `INSTRU_FINAN` in its formula ### Steps to reproduce: - Install `l10n_fr_reports` and `accountant` - Create a new FR asso company and switch to it - In Accounting Settings, select: France - Associations accounting plan - Create a Journal Entry: Account: 240000, Debit: 100 (BIEN_PAR_DONATION) Account: 512001, Debit: 100 (DISPONIBILITES) Account: 520000, Debit: 100 (INSTRU_FINAN) Account: 509000, Credit: 300 - Open the Balance Sheet and select Balance Sheet for associations Before the fix, `TOTAL ACTIVE` is 0 instead of 300 -------------- ## [FIX] l10n_fr_reports: add force_date_scope to asso cross_report ### Issue: The Passive part of the Balance Sheet for associations shows an incorrect `TOTAL PASSIVE` — the same move line is counted twice, once in `Retained earnings` and once in `Profit or loss for the year` ### Cause: In 19.1, `cross_report` aggregations were refactored: https://github.com/odoo/enterprise/commit/e699a14a1922d6b8a5bc6b88ad9c02a411e4b073 By default, a `cross_report` no longer forces its `date_scope` to the terms it calls — `force_date_scope` must now be explicitly passed in the subformula The association balance sheet was added in 19.1 without this parameter, so `RESULT_LEXERCICE` (`from_fiscalyear`) and `REPORT_NOUVEAU` (`to_beginning_of_fiscalyear`) both used the current report's `date_scope` instead of their own This caused both expressions to match the same entries and double the `TOTAL PASSIVE` ### Steps to reproduce: - Install `l10n_fr_reports` and `accountant` - Create a new FR asso company and switch to it - In Accounting Settings, select: France - Associations accounting plan - Create a Journal Entry: Account: 512001, Debit: 100 Account: 701100, Credit: 100 - Open the Balance Sheet and select Balance Sheet for associations Before the fix, `TOTAL PASSIVE` is 200 instead of 100 opw-6520639 Forward-Port-Of: odoo/enterprise#130147
Colombian point-of-sale receipts now include the required DIAN information when generated in Odoo. This helps businesses provide compliant receipts to customers and avoid missing tax validation details.
Original PR description
Receipts are now generated in POS using the `generateReceiptData` function. This commit extends this function and adds all the data necessary to properly show the receipt in Colombian POS. opw-6414901 Forward-Port-Of: odoo/enterprise#130005 Forward-Port-Of: odoo/enterprise#112881
This fix ensures Saudi Arabia tax tag updates are applied during Odoo 19 upgrades. It prevents outdated VAT tax grids and invoice tags, reducing the need for manual corrections after migration.
Original PR description
**Issue:** During migration to Odoo 19, the SA tax tag migration logic is present in: "migrations/2.1/pre-migrate.py" was not executed during the migration of databases because this: * SA compact tax…
**Issue:** During migration to Odoo 19, the SA tax tag migration logic is present in: "migrations/2.1/pre-migrate.py" was not executed during the migration of databases because this: * SA compact tax tags were not renamed * VAT tax grids still use old tags * Localization reload was partially updating taxes * manual intervention was required Added logs/debugging in: * 2.1/pre-migrate.py * 2.1/end-migrate.py * 2.2/end-migrate.py - Verified in both local and customer databases that only the 2.2 migration path was executed during upgrade, while the 2.1 migration scripts were skipped because the Odoo 19 manifest upgrade path already targeted the 2.2 migration version. - 19:https://github.com/odoo/odoo/blob/67510fd36f7af31f83ef110602f9922ed5a43024/addons/l10n_sa/__manifest__.py#L6 **Solution:** - Bumped the l10n_sa module version to 2.3 to apply these changes to databases that are already in production. - Moved `migrations/2.1/pre-migrate.py` to `migrations/2.3/pre-migrate.py` to ensure the SA tax tag migration logic is executed during the `2.3` upgrade flow. - Moved `migrations/2.2/end-migrate.py` to `migrations/2.3/end-migrate.py` to align with the `2.3` version bump and refresh the SA tax mappings correctly during migration. * SA tax tags migrate correctly * VAT tax grids update automatically * invoices use new tags correctly * No manual localization reload required OPW - [6117408](https://www.odoo.com/odoo/project/70/tasks/6117408), [6224788](https://www.odoo.com/odoo/project/70/tasks/6224788) Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#264571
Fixed a spelling mistake in the Italian electronic invoicing withholding tax reason. This helps ensure the displayed tax reason is accurate and avoids confusion in Italian localization records.
Original PR description
Correction of a typo in italian withholding tax reason. opw-6514615 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284992
The Send to eTransport action is now available when a stock transfer is ready as well as when it is completed. This helps Romanian eTransport users send required transport information at the right operational stage instead of waiting until after completion.
Original PR description
Currently, the Send to eTransport button on `stock.picking` is only visible when picking is done. This PR fixes this behaviour and makes it visible when picking is ready or done both. task-5930984 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285429 Forward-Port-Of: odoo/odoo#257321
Users can now update analytic information in the bank reconciliation widget without being stopped by lock date restrictions. This prevents unnecessary errors when only analytic details are changed, making reconciliation corrections smoother while preserving normal lock date controls for other edits.
Original PR description
In the case of the bank rec widget, when modifying a line, we actually unlink it and create a new one. In that case, modifying the analytic would trigger the lock date error. But user should be allowed to modify the analytic all the time. In the edit of the bank rec widget, we add a context key that will be added if analytic distribution is the only key modified. task-6397993 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#277260
Fixes an inventory issue where relocating stock in a package that was already reserved for delivery could fail with an access error. Warehouse users can now move reserved packaged stock between locations without being unexpectedly blocked, improving reliability in package-based inventory workflows.
Original PR description
**Issue** If a move's package is already reserved, relocating that move raises an AccessError. **Steps to reproduce** - Activate the "Packages" feature in Inventory settings. - Activate track…
**Issue** If a move's package is already reserved, relocating that move raises an AccessError. **Steps to reproduce** - Activate the "Packages" feature in Inventory settings. - Activate track localization in settings - Create a new storable product. - Using an inventory adjustment, add some quantity of that product in Stock, in a new package X. - Create a sales order for that product and confirm it, so the quantity in Stock gets reserved. - Go to Inventory > Reporting > Locations. - Select the quant and try to relocate it, e.g. to WH/Input. -> Raises an AccessError: "Failed to write field stock.package.picking_ids" **Cause** Relocating a quant creates a move: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/addons/stock/models/stock_quant.py#L1531 which creates a new move line without a `picking_id`: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/addons/stock/models/stock_quant.py#L1276-L1287 Since both move lines (the new one and the one linked to the SO delivery) share the same `result_package_id`, in `_compute_picking_ids`, both move lines are grouped: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/addons/stock/models/stock_package.py#L174-L176 Thus, two "pickings" end up associated with the package: the SO's, and `None`. While setting those pickings on the package, it tries to access them: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/addons/stock/models/stock_package.py#L182 https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/odoo/orm/fields_relational.py#L1497-L1503 And since `self` isn't just `None`, this check won't be skipped: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/odoo/orm/models.py#L4152 This eventually raises an AccessError since `None` gets filtered out by `filtered_domain`: https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/odoo/orm/models.py#L4154-L4156 https://github.com/odoo/odoo/blob/b9eb72eb1d3be841396cd5dcce827ec88ed9ee31/odoo/orm/fields_relational.py#L1504-L1505 opw-6427070 Forward-Port-Of: odoo/odoo#283937 Forward-Port-Of: odoo/odoo#282209
Website builder automated checks were adjusted to stay reliable with newer Chrome behavior. This helps prevent false test failures without changing what users see or how the website builder works.
Original PR description
In Chrome 152, single-value `background-size` properties may be serialized or expanded to include implicit dimensions (e.g., appending `auto` like `100px auto`), causing strict exact-string test assertions to fail. This commit updates `website` builder test expectations to use regex prefix matching or substring inclusion so tests remain reliable across different Chrome versions. runbot-946570 Forward-Port-Of: odoo/odoo#286094 Forward-Port-Of: odoo/odoo#285591
This fix prevents access errors when selling shared products through Point of Sale with invoicing in multi-company setups. It ensures Odoo only uses vendor information relevant to the active company, so sales can proceed without being blocked by another company's restricted supplier data.
Original PR description
**Steps to reproduce:** - Install PoS and Purchase - Make 2 companies, A and B - On product "Office Lamp" that is accessed by both companies, put Partner A in the vendor tab for company B - Company A…
**Steps to reproduce:**
- Install PoS and Purchase
- Make 2 companies, A and B
- On product "Office Lamp" that is accessed by both companies, put Partner A in the vendor tab for company B
- Company A should have a Partner A and Partner B in this tab
- In the stock, set a replenishment for company A, with Partner A on Office Lamp
- Go to company B and open the PoS
- Try to buy Office Lamp while requesting an invoice
- An access error appears
**Why the fix:**
When requesting an invoice in the PoS, we try to create the stock picking. Doing so will trigger the replenishment rules linked to the product to be recomputed.
Those are executed when we **flush_all()**, processing Company A's replenishments as sudo(), meaning all of Company A's **seller_id** are fetched and cached. This means the product's **seller_ids** now contains Company A's **seller_id**, even though we are currently in Company B.
While trying to get the product's code, we iterate over **product.seller_ids**, but we do not have access to every record in that product.
https://github.com/odoo/odoo/blob/b8e5291d103d9f43bd8db6d2dfe708076a57ea37/addons/product/models/product_product.py#L337-L343
As we don't have access to those, we get an access error when we stumble upon it.
To avoid those errors, we now filter the sellers to only have the ones compatible with our current Company in the given product we are currently buying.
Another solution would be to do **product.invalidate_recordset(['seller_ids'])** before looping over it, but feels more like a band-aid than the current fix IMO.
We could also write **self.lines.product_id.mapped('code')** in **_create_order_picking(self)** to have the solution be in PoS directly, but the error might arise from somewhere else at some point, and this just hides the issue by adding the code to the cache so that we don't have to fetch it again later.
opw-6308182
Forward-Port-Of: odoo/odoo#286656
Forward-Port-Of: odoo/odoo#275354Point of Sale now correctly includes product variant extra charges when applying a pricelist that is based on another pricelist. This prevents discounted or chained pricelists from showing prices that are too low, helping cashiers charge customers accurately.
Original PR description
## Steps to reproduce: - Create a product, with never variant, the variant has an extra price of 100 - Make Pricelist 1, just leave it as default - Make Pricelist 2, make it a discount, based on Pricelist 1, for all products - Go to the PoS, click on the created product - Change the pricelist to Pricelist 2 -> the price does not take the extra price into account ## Why the fix: When we have a pricelist based on another pricelist, we recursively calculate the price on the base pricelist. Before this commit, in the recursive call, we gave 0 as the extra price. We now give the extra price in the recursive function call. opw-6500086 Forward-Port-Of: odoo/odoo#286608 Forward-Port-Of: odoo/odoo#285371
This fix lets users update analytic information from the bank reconciliation widget even when accounting lock dates are in place. It prevents an incorrect lock-date warning during edits that only change analytic distribution, reducing friction in reconciliation workflows.
Original PR description
In the case of the bank rec widget, when modifying a line, we actually unlink it and create a new one. In that case, modifying the analytic would trigger the lock date error. But user should be allowed to modify the analytic all the time. In the edit of the bank rec widget, we add a context key that will be added if analytic distribution is the only key modified. task-6397993 Forward-Port-Of: odoo/enterprise#124852
This fix ensures restaurant table orders edited on one point of sale device remain marked for synchronization even after another device checks the same table. It prevents newly added order lines from being lost, helping staff keep shared table orders accurate across devices.
Original PR description
Steps to reproduce: - Device A opens table 10 and adds a product - Device B opens table 10, then goes back to the floor plan - Device A goes back to the floor plan => The lines added on A are lost, they are never synced to the database, nor to the other device When B triggers a synchronisation, A reads the open orders from the server. The local lines of A are kept, but `setup`, which is also called when a record is updated, resets the dirty flag of the order. Going back to the floor plan calls `syncAllOrders`, which filters the order out because it is not dirty anymore, so the lines are never sent. The dirty flag is now kept when the record is updated with server data, since that data does not contain the changes made locally and must be synced in the next synchronization call task-id: 6486258 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286279 Forward-Port-Of: odoo/odoo#284701
Early payment discount entries now preserve the analytic allocation from the original invoice lines for all discount calculation methods. This helps ensure accounting reports and cost tracking remain accurate when invoices are paid early with mixed or excluded discount settings.
Original PR description
_get_invoice_counterpart_amls_for_early_payment_discount_per_payment_term_line only split the early payment discount amount per invoice line (and thus preserved each line's own analytic distribution)…
_get_invoice_counterpart_amls_for_early_payment_discount_per_payment_term_line only split the early payment discount amount per invoice line (and thus preserved each line's own analytic distribution) when the payment term's early_pay_discount_computation was "included". For "mixed" and "excluded", it instead built a single counterpart line, discarding the analytic distribution of the invoice lines. Compute the per-invoice-line base amounts (and the corresponding price_unit for "mixed") for all three computations, keeping only the tax calculation restricted to "included". This way "mixed" and "excluded" discount entries now keep the analytic distribution of the invoice line they come from, while behavior for "included" is unchanged. Steps: - Create 3 payment terms with EPD, each one with a different `early_pay_discount_computation` setting - For each payment terms, create one invoice with two lines, only one having an analytic distribution - Confirm and pay the 3 invoices (early payment, discount applied) Issue: For 'mixed' and 'excluded', the discount line is the sum of the discount from the 2 invoice lines, and the analytic distribution is lost. opw-6380278 Forward-Port-Of: odoo/odoo#286816 Forward-Port-Of: odoo/odoo#282541
The stock module's automated test now searches for the exact product name instead of a partial word. This prevents similarly named or referenced products from being selected by mistake, making test results more reliable without changing business workflows.
Original PR description
When searching for the product created in the test we were only searching for "Serial" but another product with this word in the internal reference was showing up alone. To fix this we now look for the exact product name to avoid finding another product. The other matching record was introduced in this commit : https://github.com/odoo/enterprise/commit/6d4f4ec471d0d20c4deae5ad5d4fd4f803c20933 runbot-242820 Forward-Port-Of: odoo/odoo#284499
This fixes an issue where a sales order line removed from a planning shift could be automatically restored when other shift details changed. Now the sales link is only refreshed when the related project or task is changed, helping users keep their manual updates intact.
Original PR description
Before this commit, when a change is made in a shift, the SOL initially removed by the user can be set by the one set on the task/project linked to that shift. The reason is because each time the compute of SOL field is triggered, the compute will set the SOL of the task or the project linked. The problem is we only want that behavior when the user changes the project or the task. This commit removes the logic the compute to set the SOL of the project/task, to do that logic inside an onchange method instead. Task-6354078 Forward-Port-Of: odoo/enterprise#125814
Non-admin accounting users can now view and select the email template when sending manual payment reminders. This prevents an access error that blocked invoice reminder workflows for regular accounting staff.
Original PR description
**Steps to reproduce:**
1. Log in as demo user
2. Go to Accounting > Customers > Invoices
3. Select at least one invoice and click on "Send Reminder"
4. Attempt to change or view the available choices in the "Email Template" field.
**Issue:**
An AccessError is raised:
`You are not allowed to access 'Model' (ir.model) records.`
**Cause:**
The view domain on `template_id` was set to `[('model_id.model', '=', 'res.partner')]`. Traversing `model_id.model` forces the ORM to evaluate security permissions on the `ir.model` relation, which fails for non-admin users.
opw-6452738
Forward-Port-Of: odoo/enterprise#129966The timesheet timer now filters out entries that are not linked to a project when loading timer data. This prevents irrelevant records from appearing in the timer and helps users focus on valid project-related timesheets.
Original PR description
exclude AAL without a project when loading systray timer data task: 6538364 Forward-Port-Of: odoo/enterprise#130515
Public holidays created from the form now require the payroll work entry type, preventing incomplete records from being saved. This avoids payslip creation failures for employees in months that include those holidays and keeps payroll processing more reliable.
Original PR description
**Steps to reproduce:** 1. Install Payroll and Time Off modules on v19.2. 2. Create a public holiday via the form view (Time Off -> Configuration -> Public Holidays). Do not enter a work entry type…
**Steps to reproduce:**
1. Install Payroll and Time Off modules on v19.2.
2. Create a public holiday via the form view (Time Off -> Configuration -> Public Holidays). Do not enter a work entry type and save.
3. Open Payroll and try to create a payslip for any employee in the same month as the public holiday you will face below traceback.
**Issue:**
The `work_entry_type_id` is required for payroll calculations. If it is null the `_round_days` calculation evaluates an empty recordset, producing a [ValueError](https://github.com/odoo/enterprise/blob/39095789c2b6a7e558d11f479a249871b3b800b6/hr_payroll/models/hr_payslip.py#L1021
).
While in this [PR](https://github.com/odoo/odoo/pull/254666/changes) made this field was made required in the
list view, it was missed in the form view. This allows users to save a holiday without a work entry type, crashing payslip generation later.
**Solution:**
Make the `work_entry_type_id` field required in the form view as well to prevent the creation of inconsistent public holiday records.
**Traceback:**
```.py
File "/home/odoo/src/enterprise/saas-19.2/hr_payroll/models/hr_payslip.py",
line 1021, in _round_days
day_rounded = float_round(days, precision_rounding=precision_rounding,
rounding_method=work_entry_type.round_days_type)
File "/home/odoo/src/odoo/saas-19.2/odoo/tools/float_utils.py", line 152, in
float_round
raise ValueError(msg)
ValueError: unknown rounding method: False
```
opw-6477587
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#285597
Forward-Port-Of: odoo/odoo#283762Guest shoppers who enter a valid EU VAT number during checkout now have that number verified immediately. This ensures eligible intra-community orders receive the correct 0% VAT treatment instead of being charged domestic VAT by mistake.
Original PR description
# Description **Steps to Reproduce** 1. Install Belgium Localization, then go to **Settings → Accounting/Invoicing**. 2. Enable **Verify VAT Numbers** (`vat_check_vies`). 3. Go to **Accounting →…
# Description **Steps to Reproduce** 1. Install Belgium Localization, then go to **Settings → Accounting/Invoicing**. 2. Enable **Verify VAT Numbers** (`vat_check_vies`). 3. Go to **Accounting → Configuration → Fiscal Positions** and confirm or create an **Intra-Community** fiscal position with: - Detect Automatically (`auto_apply`): enabled - VAT Required (`vat_required`): enabled - Country Group: EU, no specific country configured 4. Open an incognito/private browser window and make sure the session is unauthenticated. 5. Go to the website's `/shop` page and add any product to the cart. 6. Proceed to checkout until reaching the Address step (`/shop/address`). 7. Enter a delivery address in an EU country different from the company's country (e.g. company in Belgium, delivery address in the Netherlands). 8. In the VAT Number field, enter a real, valid, VIES-registered VAT number corresponding to the delivery country (e.g. a valid NL VAT number for a Netherlands address). 9. Click **Save Address / Continue** and proceed to the Payment step (`/shop/payment`). 10. Check the tax applied to the delivery line and the resulting order total. **Issue** The delivery product and the overall order are taxed at the standard/domestic VAT rate instead of the expected 0% intra-community rate — even though the customer provided a valid, VIES-registered EU VAT number matching the delivery country. **Root Cause** In `base_vat`, `res.partner.create()` unconditionally removes `vies_valid` from the ORM's pending computation queue via `env.remove_to_compute()`, relying on a subsequent `write()` to trigger the actual VIES check. This holds for the standard backend flow, where creation is followed by a `write()` — but the website guest checkout flow differs: - `website_sale` creates the guest partner through `_create_new_address()`. - The partner is created via a single `create()` call, with no follow-up `write()`. - `_compute_vies_valid()` is therefore never triggered. - `vies_valid` remains permanently unset (`NULL`), despite a VAT number being provided. Downstream, `account.fiscal.position._get_vat_required_valid()` reads this unset value as falsy, so the Intra-Community fiscal position's `vat_required` condition fails and is rejected in favor of another applicable position (e.g. EU B2C or Domestic). **Solution** After partner creation, explicitly trigger `_compute_vies_valid()` when the partner has a VAT number and the operation is not part of a file import (`import_file` context) — performing the VIES check immediately instead of relying on a `write()` that guest checkout never issues. **Result** Guest customers providing a valid EU VAT number now get `vies_valid` computed immediately at creation. Fiscal position detection correctly identifies the Intra-Community position, and the expected 0% VAT treatment is applied to the delivery and order. OPW: 6522992 Forward-Port-Of: odoo/odoo#286201
The color picker now identifies the Solid tab using a stable internal marker instead of the translated tab text. This prevents issues where users in non-English languages could see incorrect color picker behavior.
Original PR description
### Purpose of this PR: - The color picker tabs are registered with a translated name (`_t(Solid)`), and the tab button renders that name as its only content. ColorUIPlugin read the active button's `innerHTML` and compared it to the literal string Solid to know whether the solid tab was the one in use. - Rely on the `solid-tab` class instead, which is built from the untranslated tab id. task-6441654 Forward-Port-Of: odoo/odoo#279941
This update removes an outdated setting from a Philippines localization tax form view that was no longer needed. It helps keep the system compatible with current Odoo standards without changing how users see or use the form.
Original PR description
The `modifiers` attribute was used in older Odoo versions to define field properties (invisible, readonly, required, etc.) Since the field already declares these same properties directly…
The `modifiers` attribute was used in older Odoo versions to define field properties (invisible, readonly, required, etc.) Since the field already declares these same properties directly [state](https://github.com/odoo/odoo/blob/14.0/addons/account/models/account_move.py#L150-L155) , [amount_tax_signed](https://github.com/odoo/odoo/blob/14.0/addons/account/models/account_move.py#L229)
(e.g. `invisible=...`, `readonly=...`), the `modifiers` attribute is redundant and serves no purpose.
This attribute was never added manually by us — it was auto-generated by Odoo Studio when the default view was created. Studio's default views inject `modifiers` alongside the direct attributes. [Here](https://github.com/odoo/odoo/pull/104741/changes/975e875046691c898e8c1acb87d3626cd299e5aa#diff-dfebe5a93e1b8880e88268b024be4c6f106d144b20298d7bb6c4ae09a18bafd0L67-L145)
Also the `modifiers` attribute was fully simplified [removed](https://github.com/odoo/odoo/pull/104741/changes/975e875046691c898e8c1acb87d3626cd299e5aa#diff-849f1ed2a35a8b0b9cdd67f8e34de5d2ea7bf928103a83828587ba7ec14a62e4L52) starting from version 17.0, where views rely exclusively on direct attribute expressions (`invisible`, `readonly`, `required`) instead of the `modifiers` JSON encoding [main Patch](https://github.com/odoo/odoo/pull/104741) Keeping it around in the arch is therefore dead code with no effect.
However it needs to give the error on 17.0+ like this
```
ERROR LOG:
<string>:1:0:ERROR:RELAXNGV:RELAXNG_ERR_NOELEM: Expecting an element data, got nothing
<string>:1:0:ERROR:RELAXNGV:RELAXNG_ERR_INVALIDATTR: Invalid attribute modifiers for element field
<string>:1:0:ERROR:RELAXNGV:RELAXNG_ERR_EXTRACONTENT: Element tree has extra content: field
```
As the modifer has been remove from the field [common.rng](https://github.com/odoo/odoo/pull/104741/changes/975e875046691c898e8c1acb87d3626cd299e5aa#diff-849f1ed2a35a8b0b9cdd67f8e34de5d2ea7bf928103a83828587ba7ec14a62e4L52) RelaxNG schema but modifiers set on fields here root tag is **form**, and the modifiers sit on fields inside a nested list. And Form views aren't RNG-validated from 17.0 till now —
[@validate('calendar', 'graph', 'pivot', 'search', 'list', 'activity')](https://github.com/odoo/odoo/blob/f0e58b9324af18d0cf0264aec2886d098e997f03/odoo/tools/view_validation.py#L314) has no form, and there's no [form_view.rng](https://github.com/odoo/odoo/tree/19.0/odoo/addons/base/rng).
Current senario
<img width="998" height="415" alt="image" src="https://github.com/user-attachments/assets/1a678c8f-8401-4e12-826f-9e98f6f2fe20" />
After removing the modifer: it show the same view because of field property
<img width="998" height="415" alt="image" src="https://github.com/user-attachments/assets/1a678c8f-8401-4e12-826f-9e98f6f2fe20" />
After removing the modifer still it shows the **modifiers="{'readonly':true, 'required':true}"** because the modifer is stay in the 14.0 but the 17.0 onwards it was not please see the scrrenshot its field preprty always.
<img width="1003" height="462" alt="image" src="https://github.com/user-attachments/assets/5e833924-b17c-417f-9e63-5a01c185f588" />
This Fix removes the unused `modifiers` attribute from the view arch, keeping only the direct attribute already present, with no functional change to the view's behavior.
Description of the issue/feature this PR addresses:
Current behavior before PR:
Desired behavior after PR is merged:
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#283310
Forward-Port-Of: odoo/odoo#279976Creating a new CRM stage no longer displays a warning meant only for editing existing stages. This avoids confusing users with an unnecessary message while keeping the warning available when changing settings that may affect existing opportunities.
Original PR description
Changing whether a CRM stage is won may trigger the recomputation of its opportunities. An onchange warning was added to inform users about this potentially expensive operation. However, the warning was also displayed when creating a stage because the onchange was triggered while initializing the form. Fix: Only display the warning when editing an existing stage, as it's useless to show this warning when creating a new stage. Task-6424174 Forward-Port-Of: odoo/odoo#284679