Wednesday, April 15, 2026
12 changes · saas-18.4
Resolved issues and error corrections
Malaysia individual POS e-invoices now show the full invoice total as the amount payable, even when the order was already paid at the counter. This prevents the payable amount from incorrectly appearing as zero and helps ensure documents are accepted as expected by MyInvois.
Original PR description
For individual POS e-invoices, the PrePayment Amount was mapped to the payment linked to the invoice. This incorrectly decreased the Total Amount Payable to 0, since POS orders are already paid at the counter. MyInvois tax officer and helpdesk requires that the Total Amount Payable (cbc:PayableAmount) to reflect the total amount of the issued e-document, regardless of prior payments. This commit forces the PaidAmount to 0 for individual POS e-invoices, ensuring the PayableAmount correctly matches the TaxInclusiveAmount as expected by the MyInvois API. task-6057187 Forward-Port-Of: odoo/odoo#258824
Partner autocomplete now skips VAT numbers returned in an invalid format when full data cannot be retrieved. This prevents validation errors during partner creation, allowing users to continue without being blocked by bad tax ID data.
Original PR description
### Issue: When autocompleting some partners, the returned VAT could be in an invalid format, leading to a validation error You need `base_vat` installed to get the issue ### Cause: The partner autocomplete feature relies on IAP credits for full data retrieval When no credits are available, only the VAT is returned if it was already fetched before In some cases, this VAT value is incorrectly formatted, which triggers a validation error when applied to the partner ### Fix: Invalid VAT values returned by the autocomplete are now ignored to prevent errors Since the correct VAT cannot be retrieved without IAP credits, the value is simply removed ### Steps to reproduce: - Install `l10n_cy` (we need a localization to enable base_vat) - Create a new partner from an invoice - Enter TONYO 360 and select the autocomplete suggestion Before the fix: The VAT field is filled with an invalid value, causing a validation error opw-6030164 Forward-Port-Of: odoo/odoo#255476
Invoices now only suggest outstanding payments or credits from the same company as the invoice. This prevents users in multi-company setups from trying to apply payments from another company and avoids confusing validation errors.
Original PR description
The invoice outstanding credits/debits widget currently displays all reconcilable items for a partner across the same account, regardless of the company they belong to. In multi-company environments,…
The invoice outstanding credits/debits widget currently displays all reconcilable items for a partner across the same account, regardless of the company they belong to. In multi-company environments, specifically when accounts have been merged, this allows users to see and try to reconcile payments from Company A into an invoice from Company B. This action eventually triggers a validation error stating that entries must belong to the same company. This commit adds a company filter to the widget's logic to ensure only relevant outstanding payments are suggested, preventing cross-company reconciliation errors and improving UX. **Description of the issue/feature this PR addresses:** This PR fixes a validation error in multi-company environments where the invoice_outstanding_credits_debits_widget suggests payments or credit notes belonging to a different company than the current invoice. The issue typically arises when a partner has outstanding transactions in multiple companies and the accounts (e.g., Account Receivable) have been merged, allowing the widget to query lines that are not valid for the current record's company context. **Current behavior before PR:** When viewing an invoice for Company A, the "Outstanding Credits/Debits" widget displays all reconcilable account.move.line records for that partner that match the account type, regardless of their company_id. If a user clicks "Add" on a payment that belongs to Company B, Odoo attempts to reconcile them, resulting in a traceback or a validation error: "Invalid Operation: All tracebacks/entries must belong to the same company." This creates confusion for the end-user, as they are presented with "ghost" credits that cannot actually be applied. **Desired behavior after PR is merged:** The invoice_outstanding_credits_debits_widget (and the underlying logic in account.move) will strictly filter the suggested outstanding items by self.company_id. Users will only see and be able to reconcile payments, credit notes, or debits that belong to the same company as the invoice they are currently processing. This ensures data integrity and a seamless UX in multi-company setups. **Steps to reproduce:** 1) Enable Multi-Company: Ensure you have at least two companies (e.g., Company A and Company B) active in your database. 2) Chart of Accounts Setup: In both companies, use the same account for Receivables (or merge them so they share the same ID/Code if testing a migrated environment). 3) Ensure the account is marked as Allow Reconciliation. 4) Create a Payment in Company B: 5) Post the payment so it remains as an "Outstanding Receipt". 6) Create an Invoice in Company A 7) Confirm/Post the invoice. 8) Check the Widget: Scroll down to the bottom of the Invoice form in Company A. 9) Observe the "Outstanding Credits" widget. The Error: The payment from Company B will appear as an available credit for the invoice in Company A. 10) Click on "Add". A validation error (UserError) will pop up: "All entries must belong to the same company." **video** https://drive.google.com/file/d/1PfBxupP8t-t21wsP2FIgNXFnTP0Zq140/view --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#255875
This fix prevents Odoo from creating duplicate business records when the same incoming email is processed at the same time for multiple aliases, such as different helpdesk teams. It improves reliability by ensuring each email is handled only once per intended destination, reducing duplicate tickets or records for users to clean up.
Original PR description
Concurrent processing of emails with the same `Message-Id` can create duplicate records. ### Steps to reproduce 1. Configure multiple mail aliases (e.g., two helpdesk teams). 2. Send one email with…
Concurrent processing of emails with the same `Message-Id` can create duplicate records. ### Steps to reproduce 1. Configure multiple mail aliases (e.g., two helpdesk teams). 2. Send one email with both aliases as recipient. The Mail Transfer Agent may invoke `odoo-mailgate.py` once per recipient, resulting in concurrent processing of the same email in separate transactions. We expect one record per alias/team, but duplicates may be created. ### Cause This is a race condition in the `Message-Id` deduplication logic, caused by concurrent transactions and PostgreSQL snapshot isolation. Odoo uses the `REPEATABLE READ` isolation level. This means that each transaction takes a snapshot of the database at its first query and cannot see changes committed by other concurrent transactions. When two concurrent transactions process the same email: 1. Both enter `message_process` and take their snapshot. 2. Both search for the `Message-Id`. Because their snapshots don't include each other's work, both find nothing. 3. Both create records. Even if one transaction commits before the other performs the check, the second transaction still uses its original stale snapshot and create duplicates. ### Fix After the initial duplicate check, attempt to acquire a transactional advisory lock on a hash of the `Message-Id` using `pg_try_advisory_xact_lock`. If another transaction is already processing the same email and holds the lock, the call returns false and the email is treated as a duplicate. If the lock is acquired, processing continues as normal. opw-5116492 Forward-Port-Of: odoo/odoo#258847 Forward-Port-Of: odoo/odoo#250027
This fixes Spanish SII tax reporting so REAGYP agricultural compensation amounts are included in the deductible quota sent to the AEAT. Businesses using this Spanish tax regime should see more accurate electronic VAT reporting and fewer mismatches with tax authority submissions.
Original PR description
Currently, the deducible amount for REAGYP is not passing through to the AEAT. This happens because the REAGYP compensation amount (ImporteCompensacionREAGYP) was missing from the total deductible quota calculation in the SII JSON payload. To fix this, we add 'sujeto_agricultura' to the list that cheks if the tax value for l10n_es is in the list task-6072773 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#258932 Forward-Port-Of: odoo/odoo#256586
Website users can now save certain inner content snippets, such as maps, as custom snippets and reuse them both as full blocks and as inner content. This restores expected behavior and makes custom snippet reuse more consistent when editing pages.
Original PR description
Steps to reproduce: 1. Go to Website > Edit mode. 2. Drag and drop a `Map` from `Inner Content` snippets. 3. Save it as a custom snippet. Current behavior: - The custom snippet is saved only as inner content and does not appear as a block in custom snippets. Expected behavior: - The snippet should be available both as a block and as inner content, consistent with behavior in `saas-18.3`. Issue: - Custom inner content snippets were systematically extracted from `snippet_custom`. As a result, snippets that can serve both purposes were only kept as inner content, preventing their usage as blocks. Solution: - Update the logic to: 1. Keep dual-purpose snippets in `snippet_custom` (block usage). 2. Also add them to `snippet_custom_content` (inner content usage). 3. Remove only inner-only snippets from the block category to avoid display issues. task-6064917
The HTML editor now prevents its toolbar and formatting actions from applying to content that is marked as non-editable. This avoids freezes and accidental changes, while keeping expected editing tools available for supported website elements such as QWeb and icons.
Original PR description
Current behavior before PR: - Removing formatting on a contenteditable false element infinite loop when removing format. - The toolbar could appear even when the target element had contenteditable false Desired behavior after PR is merged: - Now,the toolbar no longer opens when the selected element is contenteditable false - The toolbar is now only shown for elements with contenteditable true, except for QWeb and icon elements, where it remains accessible. task-5265416 Forward-Port-Of: odoo/odoo#249065 Forward-Port-Of: odoo/odoo#231613
Dragging the gradient angle control in the website editor now updates the on-screen preview without repeatedly triggering expensive background recalculations. This reduces lag and makes customizing header gradients feel smoother for users.
Original PR description
Cause: ====== Because the debounce function is called with await, the execution of `debouncedSCSSColorsCusto` pauses for every mousemove event. This prevents subsequent calls from overlapping,…
Cause: ====== Because the debounce function is called with await, the execution of `debouncedSCSSColorsCusto` pauses for every mousemove event. This prevents subsequent calls from overlapping, meaning the debounce logic never triggers to cancel previous timers. This results in the heavy SCSS generation running sequentially for every single mouse movement, causing performance lag. In other words, the await forced the browser to handle one request at a time, completely finishing it before accepting the next one. Solution: ========== In the gradient picker, only update the visual CSS gradient preview during drag and defer the `onGradientChange` callback to mouseup to avoid triggering heavy operations (e.g. SCSS generation) on every mousemove. Steps to reproduce: =================== 1. Go to website & edit mode. 2. Click on Header block. 3. Click on background color preview & select Gradient & Custom. 4. Click and drag the Angle knob. => The website preview triggers excessive updates dragging the knob opw-5411628
This update fixes an error in how Odoo calculates depreciation for companies with non-standard fiscal years. Previously, depreciation entries were incorrectly skipped for months within the wrong fiscal year. The fix ensures accurate depreciation calculations, particularly for companies using shortened fiscal years like May-December.
Original PR description
When a company has a shortened fiscal year defined via account.fiscal.year (e.g. May-December), the depreciation board computation for degressive assets incorrectly computes the start of the next…
When a company has a shortened fiscal year defined via account.fiscal.year (e.g. May-December), the depreciation board computation for degressive assets incorrectly computes the start of the next fiscal year using `date_from + 1 year` instead of querying the actual next fiscal year. This causes entries for the months between the wrong and correct FY start (e.g. January-April) to be skipped entirely. Step to reproduce: - Create a company with a fiscal year starting in May (e.g. May 1st 2025 to 31st December 2025) - Create an asset with a start date in the 1 December 2025, with a 24 months duration and degressive method - Compute the board and observe that entries from January to April 2026 are missing Fix the FY boundary detection in _recompute_board to query the fiscal year containing the day after the current period end, revert the effective_start_date logic in _compute_board_amount that was masking the root cause, and move the prorata date clamping to _create_move_before_date where it is needed for disposal. opw-6016834 Forward-Port-Of: odoo/enterprise#113521
This update resolves an issue where users without administrator privileges accessing invoices created from email aliases with CFDI attachments would encounter an access error. The fix ensures that attachment records are properly configured, allowing the system to correctly fetch and display the attached XML files.
Original PR description
When accessing a bill created from an email alias with a user that is not system administrator, we get an access error if there is an xml attachment. Steps: - Configure an email alias for the…
When accessing a bill created from an email alias with a user that is not system administrator, we get an access error if there is an xml attachment. Steps: - Configure an email alias for the purchase journal - Receive a mail wth an xml attached - Create a user with group_user role and administrator right on accounting - log in with new user - access the created bill -> Access Error The root of the issue is that we don't attach xml files when we receive them from an email alias. To do so, we set res_model and res_id fields to False/0 (see `AccountDocumentImportMixin._fix_attachments_on_record`) Then, when trying to access the bill the method `AccountMove._get_mail_thread_data_attachments` add the `l10n_mx_edi_cfdi_attachment_id` to the attachments to fetch. Then the fetch method get a query from the `_search` method or `ir.attachment` and because the attachment has no res_id or res_model and user is not system (see https://github.com/odoo/odoo/blob/8f7807a763e7e272347e9c1622be862700409c34/odoo/addons/base/models/ir_attachment.py#L564-L578) we don't fetch the record and we end up with an access error (https://github.com/odoo/odoo/blob/8f7807a763e7e272347e9c1622be862700409c34/odoo/orm/models.py#L3497-L3500) Fix: Adding res_model and res_id to the `l10n_mx_edi_cfdi_attachment_id` record in its compute method opw-5953578
This update resolves a crash that occurred when confirming rental orders with kit products containing multiple components in different locations. The change uses a safer method to handle multiple pick transfers, preventing a common error related to assigning return IDs. This ensures rental orders with kits can be processed reliably.
Original PR description
Problem: When you confirm a rental order that has a kit product whose components use two different pack locations Odoo crashes with a singleton error. You get this singleton error because Odoo tries…
Problem: When you confirm a rental order that has a kit product whose components use two different pack locations Odoo crashes with a singleton error. You get this singleton error because Odoo tries to assign both picks as the `return_id` because they share the same `sale.order.line` here: https://github.com/odoo/enterprise/blob/2212b3f3f3d90894dd6351defe0d3ca090584955/sale_stock_renting/models/sale_order_line.py#L404
Purpose: Use [:1] to safely handle the case where multiple pick transfers are created, avoiding a crash when assigning return_id which expects a single record.
Steps to Reproduce on Runbot:
1. Enable mutli-step routes and rental transfers.
2. Set the warehouse to 3-step delivery.
3. Copy the existing packing location.
4. Copy the existing pick operation type and set the destination location to the new packing location.
5. Create a new route.
6. Create new rules on this new route with the following configurations:
1. Rule 1
1. Action: Pull
2. Source location: WH/Stock
3. Destination location: Partners/Customers
4. Operation type: The new pick operation type
2. Rule 2
1. Action: Push
2. Source location: New pack location
3. Destination location: WH/Output
4. Operation type: Pack
3. Rule 3
1. Action: Push
2. Source location: WH/Output
3. Destination location: Partners/Customers
4. Operation type: Delivery
7. Create 2 component products tracked by inventory, and apply the new route on one of the component products.
8. Create a new rental product with a kit, which has the 2 component products.
9. Create a new rental order for the kit product and confirm it.
opw-6026918
Forward-Port-Of: odoo/enterprise#112543This update corrects a flaw in how Odoo calculates the available capacity for appointments booked through Google Reserve. The previous system incorrectly reserved the full party size, leading to potential overbooking. This fix ensures accurate capacity allocation, improving the reliability of appointment scheduling.
Original PR description
The current logic inside the appointment google reserve controller to compute reserved and used capacity per resource was incorrect. It was reserving the full party size for each resource instead of properly computing how much spots we are reserving for each. The code was fixed and a test was adapted for proper coverage. Task-6120016 Forward-Port-Of: odoo/enterprise#113805