Monday, April 27, 2026
17 changes · saas-19.1
Enhancements to existing features
This update enhances the user experience for Dutch SBR tax returns by providing clearer status tracking and automated updates within the Odoo system. It streamlines the reporting process and ensures accurate visibility into the acceptance status of returns, preventing potential payment issues.
Original PR description
…workflow This commit integrates the Dutch SBR tax reporting process into the `account.return` workflow to provide better visibility, UI feedback, and tracking for users. Main changes: * Added a new…
…workflow This commit integrates the Dutch SBR tax reporting process into the `account.return` workflow to provide better visibility, UI feedback, and tracking for users. Main changes: * Added a new `l10n_nl_sbr_status` field (Pending, Accepted, Error) to `account.return` to track the submission status for Kanban UI color coding. * Updated the Kanban view to dynamically apply custom CSS classes (`nl-sbr-error`, `nl-sbr-pending`) to the state bubble based on the SBR status. * Introduced an `action_refresh_sbr_status` method and a corresponding "Refresh" button in the Kanban view to manually trigger the status check cron. * Consolidated Chatter messages so that status updates, errors, and submission success notifications log directly on the `account.return` record rather than the closing entry. * Added an `action_reset_tax_return_common` override to prevent resetting or modifying a tax return once it has been accepted by Digipoort. * Modified the UI to hide the "Pay" button unless the SBR return has been fully accepted. * Updated `_on_post_submission_event` to block further processing of the return until the SBR report is accepted. task-6034675 Forward-Port-Of: odoo/enterprise#110516
Resolved issues and error corrections
This update optimizes the performance of the project list view, making it significantly faster. Previously, the system was slow rendering large lists due to inefficient calculations. Now, the system only checks for company differences once, dramatically reducing loading times.
Original PR description
Before this commit, to render the table, the project list view computed the list of selected records once for each cell, and then iterated over that selection to check whether selected projects were all associated with the same company (to set the stage_id field readonly if not). However, computing the selection requires to iterate over all records, so the rendering was O(n^2). As a consequence, the rendering of (not so) large tables was very slow (~1s for 80 records). With this commit, we compute only once for the whole table whether the selection contains records from different companies. Forward-Port-Of: odoo/odoo#260752
This update corrects a calculation error in the HK payroll reports (IR56B/F/G) by adding adjustments for global reimbursements and deductions. This ensures that the reported taxable income aligns more accurately with Hong Kong's tax regulations, improving financial reporting reliability.
Original PR description
Added GLOBAL_REIMBURSEMENT and GLOBAL_DEDUCTION to the AmtOfSalary calculation for IR56B/F/G reports. This ensures adjustments are properly reflected in taxable income totals. task-6126661 Forward-Port-Of: odoo/enterprise#114041
This update fixes an issue where MyInvois consolidation documents incorrectly combined invoices with gaps in their sequence numbers. The system now accurately splits large batches into multiple XML lines when invoices are missing, ensuring correct invoice generation and reducing potential errors. This improves the reliability of the consolidated invoice process.
Original PR description
When generating a consolidated MyInvois document, the system must split the batch into multiple XML lines if there are gaps in the sequence (e.g., if an invoice in the middle of the range was already sent individually) The previous logic attempted to detect these gaps by searching the database(`account.move`) for any invoices that fell within the sequence range and lacked a EDI document. Because this search would find the "missing" invoices, it would fill the gap and fail to detect it and produce a single consolidated line. This commit replaces the search and relies only on the selected recordset, along with the addition of sorted `sequence_number` for gap detection and adds test for handling consolidation of broken sequences. Task: [6057214](https://www.odoo.com/odoo/project.task/6057214) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update allows users to create pricelist items that combine subscription and one-time sale options. Previously, products designated as recurring invoices were automatically excluded from pricelist configurations. Now, pricelists can accommodate ‘hybrid’ products, providing greater flexibility for sales models.
Original PR description
Currently, the pricelist item form strictly filters out any product marked as a recurring invoice (`recurring_invoice` = True). This prevents users from defining standard pricelist rules for "hybrid" products that are subscriptions but also have `allow_one_time_sale` enabled. This commit updates the domain on `product_tmpl_id` in the pricelist item form to include an OR condition. Products are now visible if they are NOT a recurring invoice, OR if they explicitly allow one-time sales. Task: 6052066 Forward-Port-Of: odoo/enterprise#111937
This update resolves an issue where exporting bills from the print function only included records visible on the current page. The fix ensures that all bills selected, regardless of their visibility on the page, are included in the export. This improves the accuracy and usability of the bill export process.
Original PR description
Right now when selecting all the records in the current page from bills list view and there are some other records not displayed in this page then click on "select all X" where X is all the records either displayed in the current page or not it shows "selected X", however when trying to export them through the print button it only exports the ones that are in the current page, this PR fixes this bug. opw-6129628 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#261058
This update corrects a previous error in Odoo's French reporting module. Accounts 657 and 757, introduced by a recent French accounting reform (PCG 2025), are now correctly classified as current operations, ensuring accurate profit and loss statements. This resolves a mismatch previously impacting financial reporting.
Original PR description
…tions As part of the PCG 2025 reform in France, accounts 657 and 757 were introduced to handle capital gains and losses on the disposal of tangible and intangible assets related to normal, current activities. Previously, Odoo incorrectly categorized these under exceptional items which led to mismatches in the P&L. Source: https://www.anc.gouv.fr/files/anc/files/1_Normes_fran%C3%A7aises/Plans%20comptables/PCG--1er-janvier-2025.pdf Relevant excerpts: <img width="630" height="372" alt="image" src="https://github.com/user-attachments/assets/88a66dac-1cc0-4a33-a902-edb6b626f18f" /> <img width="631" height="318" alt="image" src="https://github.com/user-attachments/assets/403b65dc-20ba-447e-937d-20575f1f45ab" /> opw-6105764 Forward-Port-Of: odoo/enterprise#115054 Forward-Port-Of: odoo/enterprise#114837
This update corrects a problem where invoices sent to Chorus Pro via Peppol in France were being rejected due to spaces in the SIRET number. The fix ensures SIRET numbers are exactly 14 characters long, resolving compatibility issues with the Chorus Pro system. This prevents invoice delivery failures and ensures accurate data transmission.
Original PR description
### Issue: When sending invoices to Chorus Pro via Peppol in France, SIRET numbers may contain spaces, causing the document to be silently rejected ### Cause: SIRET values must be exactly 14…
### Issue: When sending invoices to Chorus Pro via Peppol in France, SIRET numbers may contain spaces, causing the document to be silently rejected ### Cause: SIRET values must be exactly 14 characters long However, spaces were not removed when generating the XML, leading to invalid values in: `<cbc:ID schemeID="0009">` and `<cbc:CompanyID schemeID="0009">` This issue can occur for both the company and the customer SIRET, as both rely on `company_registry` As a result, Chorus Pro rejects the document due to invalid SIRET length ### Steps to reproduce: - Install `l10n_fr_facturx_chorus_pro` and switch to `FR Company` - Enable and Activate Peppol in Settings - Go in Settings > Users & Companies > Companies and open the `FR Company` - Add spaces in the Company ID: 968 515 7590 5808 - Create a Customer (Country: France, VAT: FR23334175221, Company ID/Siret: 123 456 7890 1234) - In the customer's Invoicing Tab (Invoice sending: by Peppol, eInvoice format: EU Standard (Peppol Bis 3.0), France SIRET: 11000201100044) - Create and Send an invoice via Peppol (Your Customer, any line with a tax) - Open the XML - Check the IDs and CompanyIDs in the document Before the fix, there is missing spaces opw-6047840 Forward-Port-Of: odoo/odoo#260578
This update fixes an issue where menu links with spaces in their URLs wouldn't correctly point to newly created pages. The fix ensures that menus with spaceful URLs are properly linked, and a warning is now displayed in the menu editor to alert users about potential issues with spaces in URLs. This improves the user experience and prevents broken links.
Original PR description
When a menu item URL contains a space and has no leading slash (e.g., "some url"), creating a page from its 404 screen does not link the menu to the newly created page. The menu keeps pointing to a…
When a menu item URL contains a space and has no leading slash (e.g., "some url"), creating a page from its 404 screen does not link the menu to the newly created page. The menu keeps pointing to a 404. **Steps to reproduce**: 1. Create a menu item with a URL containing spaces and no leading slash (e.g., "some url"). 2. Click the menu item -> a 404 page is displayed (expected). 3. Click "Create Page" -> the page is created and saved. 4. Click the menu item again -> it still returns a 404 (unexpected). **Issue**: During page creation, the path is slugified (e.g., "some url" -> "/some-url"). The controller then tries to link the menu to the new page by setting `page_id`. However, `pagenew()` only searches for menu URLs with a leading slash, so a menu saved as "some url" is not found when searching for "/some url". As a result, `page_id` is never set, and `_clean_url()` keeps resolving the menu to "/some url" instead of the page's actual URL "/some-url". Menus without spaces (e.g., "mypage") have the same issue where `page_id` is not set, but since `_clean_url()` prepends "/" and renders "/mypage", which matches the new page URL, the menu still resolves correctly. **Fix**: Match both URL formats (with and without leading slash) when linking a menu to a newly created page. Additionally, a warning is now shown in the menu editor when a URL contains spaces, since spaces are slugified during page creation, which could cause confusion. (Note: this is only a warning - URLs with spaces are still allowed.) task-[5095646](https://www.odoo.com/odoo/project/974/tasks/5095646) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#261208 Forward-Port-Of: odoo/odoo#248967
This update resolves an issue where mobile money transactions using DPO Pay were being declined due to a character limit exceeding 15 characters in the transaction ID. The fix ensures the transaction ID adheres to DPO Pay's requirements, preventing these errors and improving payment processing reliability for mobile money users.
Original PR description
Steps to reproduce: = - Configure a payment method with DPO Pay (Mobile Money). - Open POS. - Add a product and initiate a transaction using the DPO Pay mobile money method. - Process the transaction on the terminal device. Issue: = - Transaction is declined with error: - "Data mismatch in one of the fields - OriginatorSourceID exceeds maximum length of 15 characters." Fix: = - Updated `sourceId` generation logic for mobile money transactions. - Ensured `sourceId` complies with DPO Pay constraints (maximum 15 alphanumeric characters), as enforced in recent DPO Pay updates. - Prevented transaction failures caused by oversized `sourceId`. task-6143491 Forward-Port-Of: odoo/odoo#260553
This update corrects a problem where invoices sent to DIAN were incorrectly marked as failed, preventing proper QR code generation. The system now handles expected status update failures from DIAN, ensuring invoices are accurately reflected with the correct state. This resolves an issue impacting invoice printing and reporting.
Original PR description
Before this commit, the logic that computes the invoice's DIAN state would rely solely on the state of the most recent document. The problem with that approach is that we will regularly send status…
Before this commit, the logic that computes the invoice's DIAN state would rely solely on the state of the most recent document. The problem with that approach is that we will regularly send status updates to DIAN after the invoice was successfully submitted via the status cron here: https://github.com/odoo/enterprise/blob/19.0/l10n_co_dian/models/account_move.py#L484 Those status updates are expected to fail a certain number of times (typically when no commercial status information is available, or in other terms when `l10n_co_dian_commercial_state` is still in `'pending'`). As a result, an invoice successfully transmitted to DIAN could still end up with a `l10n_co_dian_state` not set to `'invoice_accepted'`. This leads, among other things to a QR code that cannot be reprinted as the logic here: https://github.com/odoo/enterprise/blob/19.0/l10n_co_dian/models/account_move.py#L225 determines which template to render based on that state. The issue cannot be easily reproduced or tested as `l10n_co_dian.document` are generated almost only in prod scenarios. The most common setup where the error happens is for invoices that have 2 DIAN documents: 1. the first (oldest, bottom-most) one reflects acceptance of the invoice sending by DIAN (`state = 'invoice_accepted'`) 2. the second one (most recent, top-most), which reflects the latest query for commercial status update by the cron and which failed (`state = 'invoice_rejected'` and the error message reflects the lack of commercial events) opw-6108318 opw-5931442 opw-6034035 Forward-Port-Of: odoo/enterprise#113929
This update fixes an issue where group payments were incorrectly applying the full amount of multiple bills to a single payment. Now, group payments accurately reflect the first installment of bills with multiple payment terms, ensuring correct accounting and payment processing. This improves the accuracy of financial reporting.
Original PR description
Steps to reproduce: 1- Install Accounting and make sure "Batch Payments" is enabled in settings 2- Go to [Accounting -> Vendors -> Bills] 3- Create two bills for the same vendor, ensuring one of them has multiple installments (i.e payment term with 3 installments) 4- Confirm the bills 5- In list view, select both bills and another bill from a different vendor and click on Pay 6- Select "Group Payments" and confirm the payment Description of issue: The batch payment of the first vendor has the full amount for both bills Expected behavior: The payment should consider the full amount of the first bill and the first installment only of the second bill opw-5969972 Forward-Port-Of: odoo/odoo#260933 Forward-Port-Of: odoo/odoo#257871
This update ensures that stock is properly reserved against waiting delivery orders, even when the destination warehouse is a child location. Previously, incoming stock wasn't automatically linked to these waiting orders, leading to potential stock discrepancies. This fix improves inventory accuracy and order fulfillment reliability.
Original PR description
Steps to reproduce: - Create a storable product with no stock on hand - Create a delivery order from WH/Stock → state is "Waiting for Availability" - Create a receipt with destination WH/Stock/Shelf1 and validate it Problem: The incoming quantity is not reserved against the waiting delivery, even though WH/Stock/Shelf1 is a child of WH/Stock. opw-6124879 Forward-Port-Of: odoo/odoo#261072
This update resolves an issue where CFDI generation for payroll payslips failed with errors due to discrepancies in decimal precision. The fix ensures all payroll amounts are rounded to 2 decimal places before XML formatting, aligning with CFDI requirements and preventing errors. This ensures accurate CFDI generation and compliance.
Original PR description
Currently, if the company is configured with a 4-decimal currency, the CFDI generation for payslips might fail with NOM111 and CFDI40119 errors. This occurs because the calculation of totals and subtotals uses the raw unrounded floats, which can cause penny differences when the XML template formats the individual lines to 2 decimal places. This commit forces all payroll concept amounts to be explicitly rounded to 2 decimal places before accumulating the totals. This ensures that the sum of the formatted XML nodes precisely matches the total and subtotal values reported in the CFDI. Accounting might require a higher decimal precision for the company's currency (e.g., 4 decimals for inventory). However, payroll CFDI stamping strictly requires 2 decimal precision. This fix isolates the payroll CFDI calculations from the company's currency settings. Forward-Port-Of: odoo/enterprise#114936
This update corrects a bug that caused overtime lines to be duplicated during the regeneration process, leading to system crashes. The change ensures the correct timezone is used when identifying overtime periods, preventing the creation of duplicate entries and improving stability.
Original PR description
Issue: ---------------------------------------- With a specific configuration it can happen that overtime lines are not deleted when regenerating them, causing crashes in…
Issue: ---------------------------------------- With a specific configuration it can happen that overtime lines are not deleted when regenerating them, causing crashes in `_set_real_overtime_intervals()` for example, because several records will be linked in the same interval. Steps to reproduce: ---------------------------------------- - Have a calendar where: - Attendance on Sunday - No attendance on Monday - Timezone = 'America/New_York' - Select this calendar for an employee in UTC timezone with the default overtime ruleset - Create an attendance for this employee: - Start: 10AM on a Sunday - End: 1AM the next day, Monday - It should have 2 overtimes, one on Sunday the other on Monday - Go on the ruleset and click "Regenerate ovetimes" - Go back to the attendance and notice the overtime line for Monday is duplicated - If you go in Payroll > Work Entries > Work Entries and select the time frame to see the attendance, it will crash Cause: ---------------------------------------- We use `_get_tz()` to get the timezone in which we want to convert the attendance start and end to, then create the domain to search for the overtimes. But `_get_tz()` returns the calendar timezone, not the resource one, which is the one used in the attendance and overtime lines dates. When converting the start and end of the attendance to calendar tz, they both end up on Sunday. So the domain includes the whole week before the attendance but not the Monday where there is an overtime line. So only the Sunday overtime line is selected, and only this one is [unlinked](https://github.com/odoo/odoo/blob/ca8f3e6f054748e8951a2cd05ccdc2d36388e928/addons/hr_attendance/models/hr_attendance.py#L304) leaving the one on Monday, which is later recreated. Solution: ---------------------------------------- Use `employee.tz` instead of `_get_tz()`. opw-6067969 Forward-Port-Of: odoo/odoo#259809
This update resolves an issue preventing AI chat functionality on Safari iOS devices running older versions of WebKit. The fix replaces a modern JavaScript feature (async iteration) with a more compatible, manual reading loop, ensuring the AI chat works reliably across a wider range of Safari devices. This improves the user experience for our iOS customers.
Original PR description
Steps: - Install ai - Open ai chat - Try to send a message - Traceback The AI chat was failing on iOS with a traceback when trying to process the stream response. Even though MDN suggests…
Steps: - Install ai - Open ai chat - Try to send a message - Traceback The AI chat was failing on iOS with a traceback when trying to process the stream response. Even though MDN suggests compatibility, Safari (WebKit) versions prior to 26.4 do not implement the AsyncIterator protocol on ReadableStream. This makes `for await (const chunk of response.body)` throw a TypeError as `[Symbol.asyncIterator]` is undefined. https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream This commit replaces the async iteration with a manual reader loop (`getReader().read()`). This is the low-level primitive supported by all versions of Safari and ensures the stream is properly consumed and unlocked even if the connection is interrupted. Steps to reproduce: 1. Open Discuss/AI Chat on an iPad or iPhone (eg. 26.4). 2. Send a message. 3. The response triggers a JS error. ```js Uncaught Promise > undefined is not a function (near '...chunk of asyncStream...') ``` opw-6054307
This update corrects a bug where purchase order quantities were incorrectly calculated when products had different unit measurements. The fix ensures that quantities are accurately reflected in the purchase order, preventing invoicing errors. Additionally, a related issue with error messages has been resolved for improved reliability.
Original PR description
Bug introduced in: https://github.com/odoo/odoo/commit/385e4cab3ba6d3b2c4975565125389eb07f2ef45 Steps to reproduce: - Create a new database with only the Purchase module installed - Enable the "Units…
Bug introduced in: https://github.com/odoo/odoo/commit/385e4cab3ba6d3b2c4975565125389eb07f2ef45 Steps to reproduce: - Create a new database with only the Purchase module installed - Enable the "Units of Measure & Packagings" option - Create a storable product "P1" with the following vendor pricelist: - Vendor: Azure Interior (1 pack of 6 for $10) - Create a purchase order: - Vendor: Azure Interior - Quantity: 3 packs of 6 of P1 - Confirm the PO - Click the Receive button (available without the Stock module since v19.1) Problem: 1:/ Expected behavior: qty_received = 3 (in purchase UoM: packs of 6) Actual behavior: qty_received = 18 (product UoM: units) `product_uom_qty` (expressed in the product's UoM) was used instead of `product_qty` (expressed in the purchase line's UoM) when computing `qty_received`. Since `qty_received` feeds into `quantity_to_invoice`, this caused the invoicing autocomplete to suggest 18 packs of 6 instead of the correct 3 packs of 6. 2:/ The parameter `invalid_targets` was not properly passed to the translation function in the error message, causing a traceback when trying to receive a PO in an invalid state. opw-6080527