Wednesday, September 9, 2026
20 changes · saas-18.3
New functionality added to Odoo
Belgian companies can now generate SAF-T reports directly in Odoo. This supports local audit and reporting requirements by making standardized tax audit files available for Belgium.
Original PR description
SAF-T reports can now be generated for Belgium. task-5129628 Forward-Port-Of: odoo/enterprise#107270
Enhancements to existing features
Digitally signed PDFs now include visible and embedded information about the database and base URL they came from. This makes it easier for recipients and businesses to verify the origin of official PDF documents.
Original PR description
Before this commit, the digitally signed PDF had no mention of the database or the base URL. This commit adds visible information and metadata to allow checking more easily the origin of the file. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Resolved issues and error corrections
Refreshing the attachment view from an expense report now keeps the view limited to the relevant expense receipt. This prevents users from unexpectedly seeing a broader list of readable attachments and keeps the workflow focused on the selected expense.
Original PR description
Scenario: - go on an expense with an attachment (or add it with "Attach Receipt") - go to View Report then in the expense list view, click on the attachment icon - you see only the expense attachment, then refresh the page Result: you see all the (readable) attachments of the database Cause: since we are giving the id of the action base.action_attachment (action used to show all Attachments), on reload we apply that action and the domain is lost. Fix: remove the id of the action so on reload we just keep the current "custom" action parameters. opw-6514407 Forward-Port-Of: odoo/odoo#286220
Odoo can now restart its main server without interrupting most incoming requests when the new graceful reload option is enabled. This improves service availability during maintenance or updates, while also fixing small server worker handling issues that could cause errors or duplicate actions.
Original PR description
Before, the only reload option that existed was the `server_phoenix` mode that reexecuted the server upon receival of the SIGHUP signal. This came with a consequential downtime while the server was…
Before, the only reload option that existed was the `server_phoenix` mode that reexecuted the server upon receival of the SIGHUP signal. This came with a consequential downtime while the server was restarting. We add the option that allows to reload the server with no downtime, by adding the environment variable ODOO_GRACEFUL_RELOAD=1 (POSIX + Prefork mode) With this environment variable is set and a SIGHUP signal is sent to the main server, it will initiate the shutdown procedure in a separate process that will first wait until the main process has finished restarting and is ready to handle new incoming HTTP requests. The reexecuted server will reuse the same socket and notify its stopping fork with a SIGHUP signal, which will then proceed with the graceful shutdown procedure. It will not be able to reap the children processes, so it will monitor them closely in a slightly different way using psutil. The newly restarted server will reap them without consequence. Note: the gevent longpolling server is not handled gracefully. (This comes along with 2 small fixes.)
Finnish electronic invoicing now includes support for a new XML invoice format aligned with Finvoice 3.0 requirements. This helps businesses operating in Finland prepare compliant invoice files and lays the groundwork for future Finvoice EDI workflows.
Original PR description
This commit add a new invoice xml format, following Finvoice 3.0 requirements. See legal requirements in the task. task-4500798 Forward-Port-Of: odoo/enterprise#80590
Partner pricelist assignment now uses the parent partner's grade pricelist when no other pricelist can be found. This helps ensure related partners receive an appropriate pricing setup automatically, reducing manual corrections and inconsistent pricing.
Original PR description
This commits modify the pricelist assigning method so that if no other pricelist is found, the pricelist of a partner will be the pricelist found in the grade of its parent. TASK-4985900
Messaging screens now have more consistent and polished action menus across Discuss headers, sidebars, chat windows, messages, attachments, and composers. This improves usability by making common actions easier to recognize, while also refining chat bubble previews and several small visual details.
Original PR description
- improved thread actions style: discuss header, discuss sidebar, chat windows - chat bubble message preview uses bubble color of last message - minor miscellaneous tweaks
Messaging actions now have a more consistent and polished look across Discuss, chat windows, live chat, WhatsApp, AI, and Knowledge areas. This makes thread controls easier to recognize and improves message preview consistency, creating a cleaner communication experience for users.
Original PR description
- improved thread actions style: discuss header, discuss sidebar, chat windows - chat bubble message preview uses bubble color of last message - minor miscellaneous tweaks
Fresh databases without demo data now correctly prepare the accounting dashboard setup for the default company. This prevents an error when users open Tax Returns, making first-time accounting setup smoother and more reliable.
Original PR description
Steps to reproduce: 1. Create a new DB without demo data. 2. Go to Accounting Dashboard. 3. Click on the 'Tax Returns' card. 4. Input the date and click 'Apply'. Before: Since saas-18.3, the…
Steps to reproduce: 1. Create a new DB without demo data. 2. Go to Accounting Dashboard. 3. Click on the 'Tax Returns' card. 4. Input the date and click 'Apply'. Before: Since saas-18.3, the post-init hook only iterated over companies with a chart_template defined. In databases created without demo data, the default company had no chart_template set at post-init, so `_initiate_account_onboardings()` was never called. This prevented the creation of an `onboarding.progress` record for the accounting dashboard. When clicking the 'Tax Returns' button, it attempted to access the onboarding progress but found none, raising a `ValueError: Expected singleton`. After: We now iterate over all companies and only guard `_get_tax_closing_journal()` behind the chart_template check. This ensures that even companies without a localization chart template (such as the default company in a fresh database without demo data) will have their onboarding progress correctly initialized, avoiding the crash on the accounting dashboard Tax Return button.
This fix prevents chart legend clicks from accidentally opening linked dashboard menus. It also makes middle-click behavior consistent when users click directly on chart elements, reducing confusion when working with spreadsheet dashboards.
Original PR description
### [FIX] charts: avoid conflicting click listeners We have several click listeners on charts: one on the legend, one on all odoo chart elements, and one on the chart itself in dashboard. The first…
### [FIX] charts: avoid conflicting click listeners We have several click listeners on charts: one on the legend, one on all odoo chart elements, and one on the chart itself in dashboard. The first two are handled in ChartJS, and the last one is handled in a `t-on-click`. The problem is that the `t-on-click` was called for every click on the chart, even if the click was on the legend. This is because ChartJS handles events at the next animation frame, so the `t-on-click` was always called before the ChartJS click listener. To fix this, we also handle the click on the chart inside ChartJS. The middle mouse click was also handled when clicking on the chart to open its linked menu, but not when clicking on an element of the chart(eg. bar of odoo bar chart) chart. Task: [4636147](https://www.odoo.com/web#id=4636147&cids=1&menu_id=4720&action=333&active_id=2328&model=project.task&view_type=form) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Full-width website headers and eCommerce shop pages now use the same spacing rules on large screens. This removes visible misalignment between headers, content, snippets, and shop layouts, creating a more polished storefront experience.
Original PR description
The website header fullwidth option and the eCommerce shop content fullwidth option were merged at different times and currently behave differently on large screens, causing noticeable misalignment…
The website header fullwidth option and the eCommerce shop content fullwidth option were merged at different times and currently behave differently on large screens, causing noticeable misalignment between the header and content. The header option only applies a container-fluid class, while the shop option goes further by adjusting padding for XL to prevent content from appearing too "stuck" to the edges of the screen. When both options are active, this creates visual inconsistency. Furthermore, since website_sale had its own container-fluid padding logic while the rest of the website handled it differently, this led to inconsistencies. This commit establishes a consistent container-fluid padding system across the entire website that applies to headers, snippets, page layouts, and any other components using container-fluid class. It also updates (in website_sale) some conditional sidebar padding logic to handle presence/absence of sidebar, removes unnecessary container padding, and refines variables for better maintainability. The solution provides unified spacing behavior whether container-fluid (fullwidth option) is used in headers, eCommerce layouts, snippet content, or page layout, eliminating visual inconsistencies. task-4840034 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Members can now use AI-generated field content when prompts include custom Studio fields, without being blocked by an email template editor permission error. This makes AI field assistance behave consistently for both standard and custom fields.
Original PR description
Description of the issue/feature this PR addresses: 'Member' users can use the AI field button when the prompt contains standard field references. When the prompt contains a Studio field reference,…
Description of the issue/feature this PR addresses: 'Member' users can use the AI field button when the prompt contains standard field references. When the prompt contains a Studio field reference, there is an error message stating 'email template editor' access is required. Current behavior before PR: -Create an internal 'Member' user -Add a 'user' group for the app you want to test on (bug spotted in Helpdesk). -Make a plain text Studio field -Make a text AI field where the prompt references the plain text Studio field you just created. -Make a text AI field where the prompt references a standard available field. -Log into the database as the 'Member' user -Push the 'AI' button on each of the two fields you created. -The one referencing the standard field should populate as normal; the one referencing the Studio field will trigger an error message asking for 'Email template editor access'. Desired behavior after PR is merged: Allowing AI fields unrestricted rendering to avoid the access error when the studio field is used in the prompt. Task-5016767 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Turkish e-Ledger exports now fill in line numbers automatically and keep the numbering continuous across the full fiscal period. This helps monthly filings match GIB expectations and avoids manual corrections after export.
Original PR description
Before: the `LineNumber` column of the e-Ledger CSV was always empty, left to be filled after the export. Since the ledger is filed monthly, whatever filled it restarted at 1 every month, while GİB expects the numbering to start once, at the beginning of the fiscal period, and to run unbroken until its end. Now: the column is written by the export. An export starting after the first day of the fiscal period is offset by the number of rows that period already counts, so February continues where January stopped, and a full-year export numbers the same rows identically. The lines are also read in ascending date order. They were sorted by the default order of `account.move.line`, the most recent first, so the first row of the file was the last entry of the period and could not be numbered 1. This reverses `EntryNumberCounter` as well, which now counts from the oldest entry. task-6424730 Forward-Port-Of: odoo/enterprise#130486 Forward-Port-Of: odoo/enterprise#127518
This fixes issues introduced during a previous code update that could affect expense filtering and employee lookup in the organization chart. The change helps ensure employees and managers see the correct expense records and that the org chart continues to load employee information properly.
Original PR description
Due to guilty lack of tests, fw-port PR odoo/odoo#279530 was merged with errors. - `_search_filter_for_expense` domains fix. - `_check_employee` was renamed to `_get_employee`
Users can now open their Discuss inbox and history even when a notification refers to a business record that has since been deleted. This prevents an error message from blocking access to mailbox content and keeps older notifications viewable where possible.
Original PR description
Before this commit, when a message notification from inbox or history has its related record deleted, the user couldn't load inbox or history. Steps to reproduce: - Have mitchell admin receive…
Before this commit, when a message notification from inbox or history has its related record deleted, the user couldn't load inbox or history. Steps to reproduce: - Have mitchell admin receive notification in Odoo - Install `mail_group` - Wait a few seconds to receive following user_notification from mail group: ``` on My Company News Hello, You have messages to moderate, please go for the proceedings. ``` - Go to related record then delete it - Go back to inbox or history => One of them show `An error occurred while fetching messages.` This happens because the related record is not a `mail.thread`, but still creates some `mail.message` to send user notifications. `mail.thread` cascade delete the messages in message list, but threadless records do not. Because the related record is deleted, these messages are attempted to be displayed in mailbox, but due to related record having no `display_name` by non-existing, the fetch data of inbox/history crashes with: ``` MissingError: Record does not exist or has been deleted ``` This commit fixes the issue by doing its best to show message even when the related record has been deleted. opw-4546920
Livechat users who do not have payroll access can now view needed employee-related information without running into access errors. The change uses employee public records where appropriate, improving reliability for internal livechat workflows while preserving existing access restrictions.
Original PR description
Some users have access to livechat but not to payroll, which leads to access errors when fetching employee-related data. To avoid this issue, employee public IDs are used instead, as they are accessible to all internal users without requiring payroll access. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix prevents outdated activity information from being reused when a user returns to an older browser tab. It also preserves rich text formatting in activity-related content, helping avoid incorrect names, avatars, or visible HTML text in the interface.
Original PR description
1. record.toData() keeps markup of html fields Before this commit, `record.toData()` was not retrieving the "markup" flag of html fields. This is a problem because inserting this data would remove…
1. record.toData() keeps markup of html fields Before this commit, `record.toData()` was not retrieving the "markup" flag of html fields. This is a problem because inserting this data would remove the `Markup` of these html fields, therefore showing html as text content in UI. This commit fixes the issue by having `record.toData()` return `["markup", string]` rather than `string` on html fields that have markup content, so that insert preserves the markup. 2. broadcasted activity data is time-limited When an activity has updated content in a tab, the activity data is broadcasted to all other tabs. Before this commit, the broadcasted data was taken into account to other tabs, even if other tabs woke up much later. This is a problem because tabs would feed old activity data, which leads to wrong discuss state. For example, the assignee of activity is also broadcasted to display avatar and name, and the assignee of activity may have outdated data like different name. This commit fixes the issue by limiting the broadcasted activity data to other tabs in time to 3 seconds, so that other tabs do not feed from outdated data.
Receipts now continue to show next-order loyalty coupons even after the point of sale page is refreshed or data is reloaded. This prevents staff from reprinting incomplete receipts and helps customers retain the discounts they earned for future purchases.
Original PR description
Step to reproduce: = - Complete order with Next order coupon - Open Order and print receipt (This time coupon is visible) - Refresh page Or Reload Data - Same order > Print receipt Issue: = - Next time on-wards next order coupon will not shown on the receipt. Reason: = - Loyalty program data is stored in the uistate which destroyed on refresh page OR reload data. Fix: = - Fetched data from the server when uistate doent have any loyalty program data for that order while receipt printing only. task-5890998 related pr: https://github.com/odoo/enterprise/pull/128730 Forward-Port-Of: odoo/odoo#247127
Combo meals with added extras now keep the correct total when the order is sent to payment and saved in the backend. This prevents incorrect totals and negative order lines for restaurant self-order purchases.
Original PR description
**Steps to reproduce:** - Order a combo with some extra products - The displayed total will be correct - Go in the restaurant or the orders in the backend - The total is not correct, some lines are…
**Steps to reproduce:** - Order a combo with some extra products - The displayed total will be correct - Go in the restaurant or the orders in the backend - The total is not correct, some lines are in negative **Problem:** When ordering a combo in the self order, the displayed price will be correct, up until the payment where it is recomputed wrongly. This also affects the backend as this is the value that is used to save the order's price. **Why the fix:** In the frontend, everything is correctly computed, explaining why the correct price is displayed. But when going back to the backend to retrieve some data and to make sure that the product prices did not change, the data is wrongly computed. The _verify_line_price function is the one that changes the data. This arises when we order some extras in the combo, so it seems the function was not changed when the combo's REF was done. This fix was made by keeping the implemented logic to distribute the unit_price among the lines as it was done before, so it might be different from what is done in the frontend, but this is how it was done even before the combo's REF. opw-4990886
SEPA Direct Debit export files no longer include an extra identifier field for countries where banks may reject it, such as Italy. The field is now kept only for Nordea countries that explicitly require it, reducing failed payment file submissions.
Original PR description
### Issue: An unexpected `<SchmeNm><Cd>CUST</Cd></SchmeNm>` node was added inside `<InitgPty><Id><OrgId><Othr>` for all countries Some Italian banks reject SDD files containing this node ### Cause:…
### Issue: An unexpected `<SchmeNm><Cd>CUST</Cd></SchmeNm>` node was added inside `<InitgPty><Id><OrgId><Othr>` for all countries Some Italian banks reject SDD files containing this node ### Cause: https://github.com/odoo/enterprise/commit/3c3c64b511d07fc1ba33c30363972f5b0f7283d6 added `<SchmeNm><Cd>CUST</Cd></SchmeNm>` unconditionally for all countries The original fix was intended for Nordea (Sweden) only, which requires this node explicitly The assumption that other countries would accept it was incorrect ### Steps to reproduce: - Install `account_sepa_direct_debit` and `l10n_it` - Switch to the IT company - In Settings, set SEPA Direct Debit Creditor Identifier to `BE30ZZZ300D000000042` - Create and confirm a Payment (Method: SEPA Direct Debit, any customer and amount) - Create and validate a Batch Payment with that payment - Open the generated PAIN008 XML Before the fix, `<SchmeNm><Cd>CUST</Cd></SchmeNm>` is present opw-6530996 Forward-Port-Of: odoo/enterprise#130304