Friday, April 10, 2026
16 changes · saas-18.3
Enhancements to existing features
This update enhances financial reporting by providing a 'coverage ratio' for each analytic plan. This allows users to easily identify if journal items are fully distributed across their planned analytics, which is crucial for accurate audit trails and financial analysis. The change improves transparency and reduces discrepancies in reports.
Original PR description
The objective is to bridge the gap between journal items and analytic items in the reporting. As Odoo allows for partial analytic distribution, this creates discrepancies in the P&L and BS when filtered by an analytic plan. This features aims to provide users with a clear 'coverage ratio' per plan to identify incomplete distributions during audits. task-5887978 Forward-Port-Of: odoo/enterprise#106750
Resolved issues and error corrections
This update fixes a tax-related issue where invoices displayed multiple company names (like 'X, Y') when using a child contact. This ensured invoices comply with tax regulations by clearly identifying the correct recipient, preventing potential input tax deduction denials. The change updates the display of delivery addresses on invoices.
Original PR description
If two GmbH (Ltd.) names appear in the invoice address field (e.g., "Proveco GmbH, Test GmbH"), the invoice is problematic from a tax perspective, as two GmbH are considered separate legal entities, and it is not clearly identifiable who the actual recipient of the service is. Consequently, the tax office can refuse the input tax deduction if the invoice recipient is not clearly identifiable. Steps: - Create a company contact (X) and a delivery address (Y) - Create an invoice for X, delivery address will be Y - Open preview -> The delivery partner's display name is 'X, Y', it should be only 'Y' Fix: Adding a context key to the invoice document to conditionally display the parent contact name in the display_name Ticket [link](https://www.odoo.com/odoo/project.task/5900567) opw-5900567 Forward-Port-Of: odoo/odoo#258317 Forward-Port-Of: odoo/odoo#257652
This update corrects a reporting issue where employee leave balances incorrectly displayed outdated department information. The fix ensures that leave balances always reflect the employee's current department, resolving duplicate entries in reports and providing accurate data for payroll and HR analysis. This improvement enhances the reliability of our leave reporting functionality.
Original PR description
Steps to reproduce: ------------------------- 1. Install the Time Off module. 2. Go to Time Off > Management > Allocations, create an allocation for an employee, and approve it. 3. Go to Reporting >…
Steps to reproduce: ------------------------- 1. Install the Time Off module. 2. Go to Time Off > Management > Allocations, create an allocation for an employee, and approve it. 3. Go to Reporting > Balance and apply the filter Department > Employee. 4. Change the employee’s department. 5. Create an allocation for the same employee and approve. 6. Apply the Department > Employee filter again. Observed behaviour: ---------------------------- After a department change: * Existing allocations keep the old department * New allocations use the new department As a result, duplicate employee entries appear in the report Cause: ---------- It is using [allocation.department_id.](https://github.com/odoo/odoo/blob/4c91eb3b2469cd04718005162b4572e9e3d07e72/addons/hr_holidays/report/hr_leave_employee_type_report.py#L62) Allocations store the department at creation time, which may differ from the employee’s current department, causing an incorrect report filtering. Solution: ------------ Fetch department_id from hr_employee instead of hr_leave_allocation in the hr_leave_employee_type_report and hr_leave_report. This ensures: * Leave balances always follow the employee’s current department * Correct aggregation when grouping by Department → Employee **NOTE:** Before this [commit](https://github.com/odoo-dev/odoo/commit/976e0f9a667f4573e73e33737425d370ec1e1452), the issue was resolved starting from version saas-18.4, as the balances were filtered using the employee's department (via `hr_version`). https://github.com/odoo/odoo/blob/104a33f093ca583c91db9705f087c73d8464c173/addons/hr_holidays/report/hr_leave_employee_type_report.py#L65-L74 related commit: https://github.com/odoo/odoo/commit/21f18b1a6fdbf1a01c3dda83acfa66addd01a759 Since `hr_version` is no longer used in the query, this issue needs to be addressed again and forward-ported to all versions up to master. Before: <img width="1238" height="857" alt="image" src="https://github.com/user-attachments/assets/15e1293b-dc50-4067-a12f-079c046c2074" /> After: <img width="1247" height="824" alt="image" src="https://github.com/user-attachments/assets/feea32de-0d9c-4eef-9ae5-639f4658560c" /> opw-5220577 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#243076
This update fixes an issue where sales invoice email notifications incorrectly displayed a zero amount and a missing record reference. The fix ensures that the correct invoice amount and record details are sent to the salesperson, improving the accuracy of email communications. This impacts sales order processing and reporting.
Original PR description
Steps to produce: --- - Install `Sales` module. - Create a sale order, set a product, and assign Marc Demo as salesperson in the Other Info tab. - Confirm the sale order and create an invoice. Issue:…
Steps to produce: --- - Install `Sales` module. - Create a sale order, set a product, and assign Marc Demo as salesperson in the Other Info tab. - Confirm the sale order and create an invoice. Issue: --- - In the email notification sent to the salesperson, the record reference displays as False and the amount shows as 0.00. Root cause: --- - Here at [1], the record name is False because the invoice is still in draft state. - In [18], _sync_invoice sets amount_currency = line.balance for new lines, but balance is precomputed as 0 before the INSERT because _compute_balance returns 0 for invoice lines. In [17] it read price_subtotal directly, which is always correct. - In 17.0 the same mail fires at the same moment, but _sync_invoice had already set balance = −295 and amount_currency = −295 from price_subtotal, so the email reads the correct 295.00. Solution: --- - Use record.display_name instead of record.name, as display_name is always present regardless of the record state. - Use the tax totals amount instead of amount_total, which is not yet computed on draft invoices. [1]https://github.com/odoo/odoo/blob/0bcc34ec2f92b9b95cde321423d810002bb317ce/addons/account/models/account_move.py#L6478 [18]https://github.com/odoo/odoo/blob/b0a50104a12b205958316d382b4c7b2176395877/addons/account/models/account_move_line.py#L1566-L1610 [17]https://github.com/odoo/odoo/blob/73c076893de79df5a86aa970fde46a7aacbeaf3d/addons/account/models/account_move_line.py#L1536-L1585 Before: --- <img width="400" height="175" alt="image" src="https://github.com/user-attachments/assets/48c2dc03-765a-49c3-bad3-fd0b14405786" /> After: --- <img width="400" height="175" alt="image" src="https://github.com/user-attachments/assets/53682171-155f-46b3-85dd-0c7c98482067" /> opw-6023827 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#254862
This update corrects a bug that prevented KPI cards (Billable Hours, etc.) from accurately reflecting timesheet data when global filters were applied. The issue stemmed from a hardcoded filter limiting data to a single user. Removing this restriction ensures KPI cards display correct data based on selected filters.
Original PR description
Steps to reproduce: - 1. Go to the dashboard app > Timesheets. 2. Apply any global filter. Issue: - The main KPI cards (Billable Hours, Non-billable Hours, Billable Rate) do not update correctly when any global filter is applied. Filtering by 'Employee' causes the cards to show zero. Other filters like 'Project' or 'Department' show incomplete and incorrect data, reflecting only the timesheets of a single hardcoded user. Cause: - The pivot tables (`pivot 5` and `pivot 6`) that source the data for the KPI cards contained a hardcoded domain `['user_id', '=', 2]`. This condition changes any selection made in the global filter and shows incorrect data. Fix: - The hardcoded `['user_id', '=', 2]` condition has been removed. task-4782213 Forward-Port-Of: odoo/odoo#258200 Forward-Port-Of: odoo/odoo#224810
This update fixes an issue related to how accounting data is handled for multiple companies within the Odoo system. Specifically, it ensures that company-specific information is correctly used when processing accounts for Danish businesses (l10n_dk). This improves the accuracy and reliability of financial reporting for businesses operating in Denmark.
Original PR description
Fixes https://github.com/odoo/odoo/pull/257623. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#257852
This update resolves an issue where Chrome processes weren't completely shutting down, leading to potential disruptions in Odoo's operation. By ensuring all Chrome subprocesses are terminated, we prevent lingering connections and errors that could impact performance and stability. This is a critical fix to maintain a smooth user experience.
Original PR description
It's not clear when it started to happen (possibly forever), but apparently waiting for the main chrome process to terminate does not ensure all subprocesses are also terminated. Depending how long these subprocesses stick around, this can lead to: - remaining http or websocket requests - the inability to delete or re-creation of the user directory - possibly other confusing situations Update the chrome termination flow to make sure every subprocess is gone before proceeding to the next step. Kinda-sorta backport of #258062 Forward-Port-Of: odoo/odoo#258069
This update fixes an issue where archived warehouses were incorrectly included when creating quotations from Contacts. The change ensures that only active warehouses are considered, resolving a potential confusion for users and improving data accuracy. This was caused by a misconfiguration in how the system determines warehouse selection.
Original PR description
Currently, when a user creates a quotation from the Opportunities available in Contacts, archived warehouses are also considered for the quotation. ## Steps to replicate: - Install Sales, CRM, and…
Currently, when a user creates a quotation from the Opportunities available in Contacts, archived warehouses are also considered for the quotation.
## Steps to replicate:
- Install Sales, CRM, and Inventory.
- Create a new Warehouse and move it to the first position in the warehouse list view (highest priority).
- Archive the newly created warehouse.
- Navigate to Contacts > Acme Corporation > Opportunities >Office Design Project
- New Quotation > Other Info > Delivery and check the Warehouse field.
## Observed behavior
The archived warehouse is still assigned to the Warehouse field in the Delivery section. This issue does not occur when the user creates a quotation directly from the CRM app, it only happens when the quotation is created through the Contacts apps.
## Root cause:
This unintentional behavior was introduced after commit [1]. When a user opens 'Opportunities' from the contacts view, the function `action_view_opportunity` [2] is triggered, which sets `active_test` to False in the context.
Later, when a new quotation is created, the compute method [3] is executed. This method calls `_get_default_warehouse_id` [4], which performs a search. Because `active_test` is already set to False in the context, the search also includes archived warehouses, causing them to be fetched unintentionally.
[2]:
https://github.com/odoo/odoo/blob/bd18ecdf179f9a5f902d78af13dad62125c82550/addons/crm/models/res_partner.py#L39-L53 [3]:
https://github.com/odoo/odoo/blob/bd18ecdf179f9a5f902d78af13dad62125c82550/addons/sale_stock/models/sale_order.py#L194-L204
[4]:
https://github.com/odoo/odoo/blob/bd18ecdf179f9a5f902d78af13dad62125c82550/addons/stock/models/res_users.py#L9-L12
## Solution:
Explicitly set `active_test=True` in the context when preparing the quotation for an opportunity. This ensures that archived warehouses are excluded from the search and prevents them from being selected.
**Alternate Solution:**
We could also make `active_test` to true when searching for warehouse directly as well.
```py
def _get_default_warehouse_id(self):
# !!! Any change to the following search domain should probably
# be also applied in sale_stock/models/sale_order.py/_init_column.
return self.env['stock.warehouse'].with_context(active_test=True).search([('company_id', '=', self.env.company.id)], limit=1)
```
[1]:
https://github.com/odoo/odoo/commit/59feed9f26937ae8e2cab5cd7d2b6743ab6c0717
opw-5999034
Forward-Port-Of: odoo/odoo#252166This update resolves an issue where importing invoices could cause a system crash due to excessive matching of purchase orders. The fix filters out purchase orders with empty vendor references, which were being incorrectly identified during the import process. This improves the stability and performance of the invoice import feature.
Original PR description
Description of the issue/feature this PR addresses:
A Peppol message contains a reference with a trailing comma.
```
<cac:OrderReference>
<cbc:ID> Uw bestelling,</cbc:ID>
</cac:OrderReference>
<cac:BillingReference>
<cac:InvoiceDocumentReference>
<cbc:ID> Uw bestelling,</cbc:ID>
</cac:InvoiceDocumentReference>
</cac:BillingReference>
```
Current behavior before PR:
A MemoryError occurs in `_cron_peppol_get_new_documents` because the trailing comma leads to a fetching hundreds of thousands of purchase orders with domain `[('partner_ref', 'in', [('Uw bestelling', '')]`
Desired behavior after PR is merged:
Purchase orders are not matched by empty reference.
opw-6102641
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#257851This update fixes a bug that prevented Peppol invoices from importing correctly. Specifically, changing a Peppol journal type to non-purchase caused import errors. This change ensures that Peppol invoices are processed reliably, avoiding disruptions in receiving and accounting.
Original PR description
Prevent changing a Peppol journal to a non-purchase type, to avoid import errors when receiving Peppol invoices. Step to reproduce: - Setup a company with Peppol - Change the Peppol reception journal type to non-purchase - Try to run Peppol cron to import invoice, it fails with "Cannot create a purchase document in a non purchase journal" opw-6071992 opw-6064502 Forward-Port-Of: odoo/odoo#258274 Forward-Port-Of: odoo/odoo#256823
This update significantly speeds up the process of retrieving sale order information linked to stock lots. Previously, searching through millions of stock records took 20-40 seconds. By adding an index to the lot ID field, the lookup time has been reduced to just 1-4 seconds, improving overall sales efficiency.
Original PR description
Issue: -- The query created by the function '_compute_sale_order_ids' takes around 20-40 seconds since its doing "WHERE" searches on millions of stock.picking records Fix: -- Added an index to the…
Issue: -- The query created by the function '_compute_sale_order_ids' takes around 20-40 seconds since its doing "WHERE" searches on millions of stock.picking records Fix: -- Added an index to the "lot_id" field of the stock.move.line model. (Already existing databases can be fixed by using the webshell) Steps to recreate the issue: -- 1. Have around a million stock.picking records 2. Go to "Inventory/Products/Lots/Serial Numbers" 3. Select any existing record or create and save a new one 4. These actions will take around 20-40 seconds to complete Before this commit: -- Selecting or saving any 'stock.lot' takes around 20 seconds After this commit: -- Selecting or saving any 'stock.lot' takes around 1-4 second Benchmark: -- Benchmark test was done one a database with ~4000 stock.move records and 1.74 million stock.picking records Opw-5459842 | Before this Commit | ~20 seconds | After this Commit | **~1-4 seconds & Memory usage just a little bit higher --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#255255
This update fixes a bug where a new Sale Order was incorrectly created when adding lines to a sale order. The fix ensures that existing Sale Orders are reused, streamlining the process and preventing duplicate orders. This improves order management efficiency.
Original PR description
_*= sale_project **Issue:** When a new sale order line is created on the fly, a new Sale Order is being created instead of reusing an existing one. **Root Cause:** This happens when the project_sale_order is not being passed correctly in the context, causing a new Sale Order to be created. **Fix:** Explicitly pass the default_order_id in the context. If an existing Sale Order is found, it will be reused instead of creating a new one. **Technical Details:** Updated the `default_order_id` to use sale_order_id instead of `project_sale_order_id` in `sale_project`. This ensures that the field functions correctly even when `project_sale_order_id` is empty. **task-4276677** --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#258201 Forward-Port-Of: odoo/odoo#203230
This update corrects a previous restriction in the Spanish tax module that prevented "Comunidades de Bienes" (CBs) – entities taxed as freelancers – from being correctly identified. The change broadens the regex to include VATs starting with 'E', ensuring accurate fiscal categorization and reporting for these businesses. This improves compliance with Spanish tax regulations.
Original PR description
In Spain, "Comunidades de Bienes" (VAT starting with 'E') are entities without legal personality that tax via income attribution to their members. For accounting and tax reporting purposes, they must be treated as individuals/freelancers rather than corporations. The current _l10n_es_freelancer logic was too restrictive, only matching standard DNI (8 digits + letter) or NIE (starting with X, Y, Z). This caused CBs to be excluded from freelancer-specific logic, leading to incorrect fiscal categorization in reports and tax modules. The regex has been updated to optionally allow the 'E' prefix while ensuring the rest of the string maintains a valid format, effectively broadening the scope of what the system considers a Spanish freelancer. task-6014192 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#253087
This update resolves an issue where QR codes generated for Swiss invoices were being rejected by banks. The fix filters out unauthorized Unicode characters from the QR-Bill, ensuring compliance with the specific character set allowed by Swiss banking regulations. This prevents invoice rejections and ensures smooth payment processing.
Original PR description
**Description of the issue/feature this PR addresses:** QR code is rejected by the bank, when it contains an invalid character `U+202F`. **Current behavior before PR:** Unauthorized Unicode characters are encoded in the QR-Bill, and it is rejected on the receiving part. **Desired behavior after PR is merged:** Any Unicode codepoint which is not in the subset of 324 allowed codepoints has to be filtered out. > spec of QR-bill allows only a subset of characters, a precise list of 324 Unicode codepoints (section 4.1.1, page 30 of the Swiss Implementation Guidelines for the QR-bill) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#257961 Forward-Port-Of: odoo/odoo#254980
This update enhances the stability of the Odoo integration with Fiskaly, the French point-of-sale system. Specifically, it fixes issues with retry logic, ensures correct SCU usage, and simplifies configuration tracking, leading to a more reliable and manageable connection.
Original PR description
- Retry only on server errors (5xx) instead of client errors (4xx) to avoid masking meaningful API responses with a confusing RetryError. - Reuse the existing active SCU when E_SCU_LIMIT_REACHED is returned, since Fiskaly only allows one active SCU per account. - Pass the pos.config id as metadata when creating a cash register on Fiskaly, making it easier to match Fiskaly register IDs back to their config when the local link is broken. - Hide the test mode toggle once the company is registered with Fiskaly (l10n_at_fiskaly_organization_id is set) to prevent mode changes after registration. opw-5958673
This update prevents a bug where changing a child company's ZATCA API mode would inadvertently reset and unboard the parent company's related sales journal. The fix ensures that changes are applied only to the intended company, maintaining data integrity and preventing unexpected journal removals.
Original PR description
**Steps to reproduce:** * Install `l10n_sa_edi` module. * Create a parent company and a child company, both with the same VAT number. * Onboard the parent company's Sales journal with ZATCA. *…
**Steps to reproduce:**
* Install `l10n_sa_edi` module.
* Create a parent company and a child company, both with the same VAT number.
* Onboard the parent company's Sales journal with ZATCA.
* Onboard the child company's Sales journal with ZATCA. Create a journal for the child if there is no journal.
* Change the child company's ZATCA API mode to any other mode.
**Observed behavior:**
* Changing the child company's API mode resets and unboards the parent company's Sales journal as well.
**Cause:**
* In `res.company.write`, when `l10n_sa_api_mode` changes, journals to reset are fetched using `_check_company_domain(company)`.
* `account.journal` uses `check_company_domain_parent_of`, which returns journals where `company_id` is a parent of the given company — so passing a child company also matches journals belonging to the parent.
**Fix:**
* Replace `_check_company_domain(company)` with a direct `('company_id', '=', company.id)` filter, so only journals strictly owned by the company being modified are reset.
opw-6099340
Forward-Port-Of: odoo/odoo#258006