Monday, May 4, 2026
16 changes · saas-19.3
Enhancements to existing features
This update ensures accurate tax calculations for employees with Income from Let-Out Property in India, aligning with local regulations. A 30% standard deduction is now applied to rental income, and a new configuration option allows for future adjustments to this deduction. This improves compliance and reporting accuracy.
Original PR description
Purpose: Ensure correct tax computation for Income from Let-Out Property by applying the standard 30% deduction as per Indian tax regulations. This PR includes: - Applied 30% standard deduction on let-out property during tax input value computation. - Added rule parameter for let-out property deduction to allow future changes via configuration. - Updated tax input values to consider only 70% of the declared rental income for tax calculation. - Added tooltip on let-out property field to clarify that a 30% standard deduction is applied on gross rental income for tax computation. This change aligns the payroll tax computation with Indian income tax rules, Income from Let-Out Property is calculated after deducting the standard maintenance deduction of 30% from the gross annual rental income. task-5848157
Resolved issues and error corrections
This update clarifies the tax selection process on Saudi invoices by disabling the incorrect '0% Not Subject to VAT' option. Previously, users were selecting this option instead of the appropriate '0%' tax rates, leading to potential compliance issues with ZATCA regulations. This change ensures invoices accurately reflect supply characteristics and meets legal requirements.
Original PR description
This commit aims to make the invoice line tax drop-down cleaner and more straightforward. The tax `15% PH PE HS` is already handled through the `0% PE` and `0% PH` taxes and `0% Not Subject to VAT`…
This commit aims to make the invoice line tax drop-down cleaner and more straightforward. The tax `15% PH PE HS` is already handled through the `0% PE` and `0% PH` taxes and `0% Not Subject to VAT` is visible with the other `0%` taxes such as `0% EX G`, etc. The users were incorrectly selecting the `0% Not Subject to VAT` instead of the actual `0%` taxes according to the characteristics of the supply; leading to non-compliance with the ZATCA rules. Before this commit: - `15% PH PE HS` tax was `active`. - `0% PE & 0% PH` had distribution for invoices: `base tax grid = 3(B)` and distribution for refunds: `base tax grid = 3(B)`. - `0% Not Subject to VAT` tax was `active` and had distribution for invoice: `base tax grid = 3(B)` and distribution for refunds: `base tax grid 3(B)` - `0% IT G and 0% QT` taxes were `active`. After this commit: - `15% PH PE HS` tax is `inactive`. - `0% PE & 0% PH` taxes have distribution for invoices: `base tax grid = 2(B)` and distribution for refunds: `base tax grid = 2(B)` - `0% Not Subject to VAT` tax is set to `inactive`, `sequence = 17` and tax grid has been removed. - `0% IT G and 0% QT` taxes have been set to `inactive`. task-5959979 Forward-Port-Of: odoo/odoo#253598
This update resolves a bug in the POS system where tours would incorrectly terminate. The issue stemmed from a misinterpretation of the tour progress, preventing the tour from stopping when the JS bundle wasn't fully loaded. The fix ensures the tour correctly identifies its completion point, improving the user experience.
Original PR description
There is an issue with the POS since 19.3, when a tour is running, the tour service would get the tour name from the backend with 0 steps, and because the JS bundle of the tours is not loaded by the…
There is an issue with the POS since 19.3, when a tour is running, the tour service would get the tour name from the backend with 0 steps, and because the JS bundle of the tours is not loaded by the POS frontend the tour would continue with 0 steps.
Most of the time the currentActionIndex would be > 0, so the check on line 87 of `tour_interactive.js` would pass
```
if (this.currentActionIndex === this.actions.length)
```
and later run into a
```
TypeError: Cannot read properties of undefined (reading 'step')
at TourInteractive.play (http://localhost:8069/web/assets/debug/point_of_sale.assets_prod.js:121705:33) (/web_tour/static/src/js/tour_interactive/tour_interactive.js:95)
at TourInteractive.start (http://localhost:8069/web/assets/debug/point_of_sale.assets_prod.js:121655:14) (/web_tour/static/src/js/tour_interactive/tour_interactive.js:45)
at TourService.resumeTour (http://localhost:8069/web/assets/debug/point_of_sale.assets_prod.js:123226:39) (/web_tour/static/src/js/tour_service.js:260)
```
I'm not sure what's the correct fix,
- either what I did here
- change the condition on the `play()` to `this.currentActionIndex >= this.actions.length` instead of `this.currentActionIndex === this.actions.length`
- bundle the tours inside the POS front end (although they are not used there so it seems unnecessary)
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prThis update resolves an issue where correction payslips were failing validation due to missing information. The update ensures that correction payslips include the necessary structure ID and adjusts the payrun period to accommodate multi-month corrections, improving the accuracy of payroll processing.
Original PR description
Ensure `structure_id` is set when creating correction payslips to avoid validation errors. Group payslips by structure before creating pay runs for corrections/reverts, and set the payrun period from the minimum to maximum payslip dates. Fix condition in "Payslip period does not match payrun" to exclude correction payslips (they may span multiple months). task: 6089082
This update resolves a bug where editing an already-edited message would cause unexpected behavior, like incorrect selection or empty message fields. The fix removes a redundant 'edited' span from the editor content, ensuring messages are edited correctly and consistently. This improves the user experience for message composition and editing.
Original PR description
When editing an already-edited message, the `(edited)` span was included in the editor's editable content because `prepareBodyForEditing` (formerly `getNonEditableMentions`) passed the raw body…
When editing an already-edited message, the `(edited)` span was included in the editor's editable content because `prepareBodyForEditing` (formerly `getNonEditableMentions`) passed the raw body directly to the composer. This caused two issues: - CTRL+A would select the `(edited)` label, allowing it to be styled, repositioned, or accidentally deleted. - CTRL+A + Backspace + retype left the body empty after saving, because the self-closing `<span class="o-mail-Message-edited"/>` in the stored HTML was parsed by browsers as an open tag (spans are not void elements), swallowing subsequent content as its children. Fix: strip `.o-mail-Message-edited` from the body before loading it into the editor. The server always re-appends a fresh `(edited)` span at the end when saving, so removing it from the editor content is safe and correct. Also align the mock server with the real server behavior: - Place the span *inside* the last `div`/`p` element instead of after it. Rename `getNonEditableMentions` → `prepareBodyForEditing` to reflect that the function now covers both making mentions non-editable and stripping the edited label. task-6172161 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#261772
This update resolves critical issues with the Documents Sign feature, preventing crashes when deleting sign templates and ensuring accurate tracking of document lineage across multiple templates. The change improves stability and data integrity for users managing signature workflows.
Original PR description
Steps to reproduce: Bug 1 (The Crash): 1. Open Documents app, select a PDF, and click Action > Sign. 2. In the Sign app, delete the newly created Sign Template. 3. Return to the Documents app. 4. A…
Steps to reproduce:
Bug 1 (The Crash):
1. Open Documents app, select a PDF, and click Action > Sign.
2. In the Sign app, delete the newly created Sign Template.
3. Return to the Documents app.
4. A traceback occurs (`KeyError: <document_id>`) in `web_read`.
Bug 2 (The Broken Lineage):
1. Create two separate Sign Templates from the exact same Document.
2. Send a signature request from the second template.
3. The `reference_doc` on the signature request fails to link back to the original Document.
Current behavior:
When creating a sign template from a document, `documents_sign` intentionally unlinks the original `ir.attachment` (`res_model = False`) to pass custody to `sign.document`. If the template is deleted, the attachment is orphaned, permanently corrupting the original `documents.document` and crashing the UI.
Furthermore, the lineage tracking (`reference_doc`) relies strictly on a 1:1 shared `attachment_id`. If a user creates multiple templates from one document, the system is forced to make a copy for the second template, natively breaking the lineage tracking because the IDs no longer match.
Expected behavior:
Documents should not be corrupted when generating or deleting sign templates. Furthermore, lineage tracking (`reference_doc`) should successfully link back to the original document regardless of how many templates have been generated from it.
Fix:
1. Replaced the `res_model = False` custody-handoff hack in `documents_sign` with a safe `.copy({'original_id': attachment.id})`. This sandboxes the Sign app's files, completely preventing the deletion crash and the multi-template conflicts.
2. Updated the `reference_doc` computation in `sign.request` to dynamically search for both the current `attachment_id` AND its `original_id` (utilizing a minimal-diff recordset union `|`). This perfectly preserves the lineage tracking for all templates without requiring database schema changes.
Task: 5432116
Forward-Port-Of: odoo/enterprise#114221
Forward-Port-Of: odoo/enterprise#113167This update optimizes how prices are calculated in Point of Sale (POS) transactions. Previously, redundant price recalculations were slowing down the system, especially during order merges. This change consolidates price updates, resulting in faster and more efficient POS operations. The update also improves performance by preventing unnecessary data serialization in production environments.
Original PR description
Calling triggerRecomputeAllPrices() synchronously on every orderline mutation (create, merge, delete) caused the full tax/price computation to run 3 times per addLineToOrder call when a merge occurred, and triggered an expensive JSON.parse/JSON.stringify of the price data on each of those calls via logPosMessage. Replace the eager synchronous recompute with a microtask coalescer: the first call within a synchronous operation schedules a single Promise.resolve().then() microtask; subsequent calls within the same tick are no-ops. Because the microtask is queued before the outer await continuation (updatePrograms), _updatePrograms still reads fresh prices when it accesses line.prices. Also guard the args serialization in logPosMessage behind odoo.debug so the JSON deep-clone of the price data object is skipped entirely in production. opw-6091501 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#260436
This update adds logging of Amazon's request identifiers to the sale module. Previously, support teams struggled to debug issues with Amazon's SP-API, now investigations are simplified and more efficient. This improves overall support response times and reduces troubleshooting complexity.
Original PR description
Each response from Amazon's SP-API includes a request identifier used by their support team for debugging. This identifier was not previously logged, making support investigations more difficult. Forward-Port-Of: odoo/enterprise#114483
This update resolves an issue preventing the system from correctly calculating overtime pay. The fix re-introduced a missing data file and corrected a reference, ensuring that work entry types are properly linked to overtime rulesets when generating payslips. This ensures accurate overtime compensation.
Original PR description
Issue: The issue here is that work_entry_type is not associated with the overtime ruleset. That's why when creating a payslip, it is not considering overtime hours. From this PR - https://github.com/odoo/enterprise/pull/98952 the data file is missing from the manifest file. Fix: Added back file to manifest and fixed reference for work_entry_type_id. task-6073921 Forward-Port-Of: odoo/enterprise#115720 Forward-Port-Of: odoo/enterprise#112934
This update significantly speeds up the process of writing bank reconciliation records in Odoo. The change optimizes how the system handles large numbers of statement lines, reducing processing time and memory usage. This results in faster and more efficient reconciliation workflows, especially for businesses with extensive transaction data.
Original PR description
When writing on reconcile models, the ORM fetches all the unreconciled bank.statement.lines then calls filtered on `unreconciled_statement_lines.line_ids`. In databases with a lot of unreconciled…
When writing on reconcile models, the ORM fetches all the unreconciled bank.statement.lines then calls filtered on `unreconciled_statement_lines.line_ids`. In databases with a lot of unreconciled statement lines scattered across multiple models, this `filtered` can be heavy, both in memory (fetching all the amls fields) and in time. This commit fixes this by using a search to prefilter the amls using the reconcile models in self. Then we filter the result on `suspense_account_id`. This greatly reduces the number of lines loaded up in memory and speeds up writing on reconcile models. ### speedup In a customer database with 484 042 statement.lines, 141 000 unreconciled statement.lines, 37 account.reconcile.model. Time to write on a reconcile_model. | Nbr linked unreconciled lines| Before PR | After PR | |:----------------------------:|:---------:|:--------:| | 702 | 40s | 3s | | 2000 | 40s | 4.47s | | 10000 | 40s | 11.28s | Since most of the slowness comes from the `filtered` call, the number of unreconciled statement lines linked to the reconcile.model being updated has no impact on the timing before this patch. ### memory For the 702 unreconciled lines case, the memory consumption goes - 2.2 Gb -> 200Mb. Forward-Port-Of: odoo/enterprise#115583
This update corrects a previous issue where invoices rejected by the Italian tax system (SdI) remained marked as 'sent' in the system. Now, when an invoice is rejected, the system automatically clears this status, allowing users to easily resubmit the invoice after resolving the underlying problem. This improves the efficiency of invoice processing for Italian customers.
Original PR description
When an invoice is rejected by the SdI or by a PA partner, the move remained flagged as sent, preventing the user from resending it after fixing the underlying issue. Override write() on account.move to clear is_move_sent whenever l10n_it_edi_state transitions to 'rejected' or 'rejected_by_pa_partner', so the invoice can be re-submitted. task-4490454 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#262075 Forward-Port-Of: odoo/odoo#260591
A previous bug prevented custom loyalty program domains (using many2many fields) from functioning properly when accessing the point-of-sale (POS). This fix ensures that loyalty rewards can be correctly applied when using these custom domains, resolving a critical issue that blocked reward usage. The update expands a previous compatibility function to support many2many fields.
Original PR description
When you set many2many fields in a custom loyalty domain, when opening the pos a popup will open saying that the domain must be compatible with the POS.
Steps to reproduce:
-------------------
* Setup a loyalty program, as the reward it should be something like: 10% discount on specific products.
* Set a discount product domain: [("product_tag_ids", "not ilike", "test")]
* Open the pos
> Observation: A popup appears saying that the domain is not compatible with
the pos.
As a result this reward can never be used.
Why the fix:
------------
There's already a function responsible for chnaging `ilike` and `not ilike` with compatible pos operators `in` and `not in`. However this was only applied to `many2one` fields. We extand that.
opw-5990415
Forward-Port-Of: odoo/odoo#258560This update fixes an issue where combo products weren't displayed in the correct order within the Point of Sale system. The change ensures that combo items are consistently ordered according to the choices made when creating the combo, improving the user experience and accuracy of order lines.
Original PR description
When creating a combo product, its items were not ordered according to the combo choice sequence, and extra items were always appended at the end. As a result, combo items could appear in the wrong order. This fix ensures that, when computing combo prices and merging combo items with extra items before creating the order lines, the correct sequence is derived from the parent product. All items are then sorted based on this sequence so that they consistently follow the order defined by the combo choices. --- Task: https://www.odoo.com/odoo/project/1737/tasks/6064642 Forward-Port-Of: odoo/odoo#262102 Forward-Port-Of: odoo/odoo#255596
This update resolves issues where imported BIS3 invoices didn't accurately reflect the total amounts from the original XML files. The changes improve the import process by synchronizing values and using a more robust tax calculation method. A new testing approach, 'Partial Imports,' has been implemented to streamline testing and improve test file readability.
Original PR description
This commit refactors the import code of BIS3 Invoice to fix various issues about unsynchronized values between the imported invoice and the source XML file. The new way we import BIS3 invoice can be…
This commit refactors the import code of BIS3 Invoice to fix various issues about unsynchronized values between the imported invoice and the source XML file. The new way we import BIS3 invoice can be categorized as: - collecting all the values from the XML to a dictionary object - prepare the values and amounts to write to the invoice in its entirety using the tax computation engine helpers - write the whole processed values to the invoice (as a single write) - (in 18.0 ~ 18.2) recalculate discrepancies and update the invoice lines (if needed) with the corrected amounts This commit also includes a new test suite for BIS3 import, and a new approach of import testing, "Partial Imports", is introduced to better atomize the big import test files (and make it understandable). In the long term, `l10n_account_edi_ubl_cii_tests` will eventually be removed in favor of these small-but-many partial tests. task-id: 5058687 Co-authored-by: Yosua Nicolaus <yoni@odoo.com> Forward-Port-Of: odoo/odoo#261052 Forward-Port-Of: odoo/odoo#250160
This update enhances how Odoo matches invoice data by adding a 'partner' domain to the move line query builder. This ensures more accurate synchronization of values, particularly during imports, and resolves issues related to inconsistent data between invoices and accounting records. This change is part of a larger effort to improve invoice import reliability.
Original PR description
This commit is part of a bigger commit on the community side- to refactor the import code of BIS3 Invoice to fix various unsynchronized values issues. task-id: 5058687 Forward-Port-Of: odoo/enterprise#114893 Forward-Port-Of: odoo/enterprise#108356
This update fixes an issue where applying a global discount and a gift card resulted in incorrect order totals. The fix ensures gift card deductions are properly handled, preventing the system from recomputing discounts and adding back incorrect amounts. This ensures accurate pricing and a better customer experience.
Original PR description
Applying a global order discount and then paying with a gift card (or eWallet) caused the POS to recompute the global discount as if the gift card deduction were part of the taxable merchandise base.…
Applying a global order discount and then paying with a gift card (or eWallet) caused the POS to recompute the global discount as if the gift card deduction were part of the taxable merchandise base. The negative reward line was included in `isGlobalDiscountApplicable()`, so the percentage discount shrank and the order total increased by “global_discount% × gift card amount” (e.g. 10% of $50 → $5 added back). Steps to reproduce: ------------------- * Open PoS with global discount and a gift card / eWallet program. * Add a product (e.g. $100), apply a global discount (e.g. 10%). * Apply a gift card for part of the balance (e.g. $50). > Observation: Total is wrong (e.g. $45 instead of $40): the gift card amount is reduced, then a fraction of that amount is added back via the recomputed global discount line. Why the fix: ------------ `pos_discount` only skips tip and discount-product lines; gift card reward lines use another product and were still “discountable”. The fix belongs in `pos_loyalty`: override `isGlobalDiscountApplicable()` to return false only for gift card and eWallet reward lines (`isGiftCardOrEWalletReward()`), and fall to `super` otherwise so promotion/loyalty reward behavior stay correct. opw-6104090 Forward-Port-Of: odoo/odoo#259577