Tuesday, February 3, 2026
14 changes · saas-18.4
Resolved issues and error corrections
This fix ensures Saudi POS invoices and receipts show the correct legally required QR code after invoicing issues are corrected or when e-invoicing is disabled. It improves receipt reliability for businesses using Saudi localization and helps avoid empty or non-compliant POS receipt output.
Original PR description
Ensure proper handling of QR code generation and POS EDI behavior by fixing multiple issues across invoice reprints and journal onboarding. QR codes now correctly appear on reprinted invoices when…
Ensure proper handling of QR code generation and POS EDI behavior by fixing multiple issues across invoice reprints and journal onboarding. QR codes now correctly appear on reprinted invoices when journal problems occur during order confirmation. POS correctly falls back to the Phase 1 flow when the journal is not onboarded and electronic invoicing is not enabled. Additionally, POS receipts now display the proper Phase 1 QR code whenever the EDI module is installed. Problem 1: If ZATCA does not properly receive the invoice generated from a POS order (wrong onboarding, wrong details on company, etc.) the invoice printed from POS will not contain the QR code, even after successfully resubmitting the invoice to ZATCA, load the order and reprint the invoice to see this Testing the fix: Change the VAT number on the company to be faulty, create a POS order, Fix the VAT number and resubmit the invoice, load the POS order and reprint invoice, it will now show the QR code. Problem 2: When disabling the E-invoicing for a phase 2 journal, the POS receipt will still try to print the phase 2 QR code but the system will flag it as 'not legal' leaving the POS receipt empty, when it should instead print the phase 1 QR code Testing the fix: On the journal, disable the E-invoicing, and create a POS order, it will now show the phase 1 QR code on the receipt task-5032474 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#243984 Forward-Port-Of: odoo/odoo#234945
Customers browsing the online shop can now move between product result pages without losing selected filters such as color or size. This keeps search results consistent and avoids confusion when shoppers refine product listings.
Original PR description
Current behavior: When a user selects multiple filters (attributes) that result in multiple pages of products, navigating to the second page causes some filters to be lost. Specifically, only the…
Current behavior:
When a user selects multiple filters (attributes) that result in multiple pages of products, navigating to the second page causes some filters to be lost. Specifically, only the last selected attribute value is kept in the URL of the pager.
This happens because the `/shop` controller processes query parameters using a standard Python dictionary (**post). Since a dictionary cannot hold duplicate keys, an URL like `?attrib=1&attrib=2` is reduced to `{'attrib': '2'}`, losing all previous values.
Steps to reproduce:
1. Install `website_sale`.
2. Reduce "Products per Page" (e.g., to 4) to easily trigger pagination.
3. Go to the /shop page.
4. Select a first attribute (e.g., Color: White).
5. Select a second attribute (e.g., Size: M).
6. Ensure the result spans at least two pages.
7. Click on page "2".
8. Observation: The second attribute filter is lost, and the product list changes incorrectly.
Fix:
Ensure that `attribute_values` are stored as a list within the `url_args` passed to the pager. Since Odoo's `website.pager` uses `url_encode` internally, passing a list of values for a single key correctly generates repeated parameters in the resulting URL (e.g., `attrib=1&attrib=2`).
opw-4152637
Forward-Port-Of: odoo/odoo#244941Fixes a crash that could occur in Accounting on mobile when adding a journal item, selecting taxes, and saving the dialog. Tax selections are now kept in the dialog until the user saves, making journal entry editing more reliable.
Original PR description
**Steps to reproduce:** * Install the **Accounting** module. * Open a **Journal Entry** in mobile view. * Add a **Journal Item**, opening it in a form dialog. * Select a tax in the **Taxes Applied**…
**Steps to reproduce:** * Install the **Accounting** module. * Open a **Journal Entry** in mobile view. * Add a **Journal Item**, opening it in a form dialog. * Select a tax in the **Taxes Applied** field. * Click on the **Save and close** button. **Observed behavior:** * The web client crashes with `TypeError: Cannot read properties of undefined (reading 'resId')`. **Cause:** * The `autosave_many2many_tags` widget triggers `model.root.save()` immediately when a tag is selected. * When executing this save from within a transient dialog (common in mobile views), the client fails to correctly handle the record reload/synchronization leading to a crash when accessing `resId`. **Fix:** * Replace the autosave widget with the standard `many2many_tags` widget for **Journal Items** for form. * Tax changes are now kept locally in the dialog and saved only when the user explicitly saves and closes it. **Note:** * The crash happens when `autosave_many2many_tags` calls `model.root.save()` on a **new** `account.move` from within a dialog. * In QUnit tests, the mock server (`mockWebSave`) [1](https://github.com/odoo/odoo/blob/ca4d74c2a5d749ff8d41cd2fff80a73ba550a843/addons/web/static/tests/helpers/mock_server.js#L627). which creates records [2.](https://github.com/odoo/odoo/blob/ca4d74c2a5d749ff8d41cd2fff80a73ba550a843/addons/web/static/tests/helpers/mock_server.js#L1936) in-memory and does not trigger form reloads or component destruction. * As a result, the crash cannot be reproduced in tests. opw-5497342 Forward-Port-Of: odoo/odoo#246699 Forward-Port-Of: odoo/odoo#246084
This fix prevents website editor paste actions from accidentally removing protected elements, such as the Contact Us button, when editing content inside embedded areas. It helps preserve page layout and avoids unintended changes during routine website editing.
Original PR description
*: website Before this commit: when a editable container is wrapped inside a non-contenteditable, which could happen inside an iframe, pasting on a selection including an unremovable element will remove the element. This is because the config parameter `allowInlineAtRoot` is false by default, the editable container of Contact us button is considered as the edition boundary, and then `wrapInlinesInBlocks` is called on it at insert, which removes invisible nodes in the wrapping process. After this commit: we now use predicates to decide areInlinesAllowedAtRoot We add a predicate specifically for the container of Contact Us button, to allow inline element in the root. The `wrapInlinesInBlocks` won't be called on the container on paste anymore. task-5109662 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Customers using Taiwan ECPay checkout can now validate a mobile barcode without losing their selected carrier type. This prevents sales orders from being created with missing carrier information and makes the checkout labels clearer for users.
Original PR description
carrier type After the user clicks on "Validate" button to validate the mobile barcode, the carrier type selection is disabled and the carrier type pass to the SO is None. This commit fixes the issue by instead of disabling the carrier type selection, we just set the input box of carrier number to readonly and set back the carrier number to not readonly when the user changes the carrier type to ensure the carrier number input is editable. task-5880421 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#246438 Forward-Port-Of: odoo/odoo#246053
Website editors and designers can now update SEO settings for eCommerce categories without hitting an access error. This lets authorized website staff improve category search visibility directly from the shop page, without needing broader administrative rights.
Original PR description
### Issue: Due to this issue, website designer cannot optimize seo on category. #### Steps to reproduce: 1- Create a `eCommerce Category`. 2- In demo user, set Sale access to `All Documents` and Website access to `Editor and Designer`. 3- Login with demo user and navigate to website. 4- In shop page, open category. 5- From site tab, click on `Optimize SEO`. You get access error. Expected: You should be able to optimize seo with `Editor and Designer` access. ### Cause: The user needs write access on record in order to optimize seo: https://github.com/odoo/odoo/blob/ec5da99ca3f52420e7d973c3cf07167bd4104ffa/addons/website/controllers/main.py#L831-L834 opw-5443854 Forward-Port-Of: odoo/odoo#246842 Forward-Port-Of: odoo/odoo#244724
This fixes an issue where participants in live survey sessions stopped seeing the countdown timer after submitting an answer and moving to the next question. The change keeps timed live sessions clear and fair by showing the timer until the host ends the session.
Original PR description
Description of the issue/feature this PR addresses: Fixes timer not showing to participants of a live session after submitting an answer. Current behavior before PR: Steps to reproduce: Create a…
Description of the issue/feature this PR addresses: Fixes timer not showing to participants of a live session after submitting an answer. Current behavior before PR: Steps to reproduce: Create a Survey with two questions of any type. Set some Question Time Limit on both. Create Live Session for that Survey. Access Live Session as a participant. Start Survey as the host. The first question appears to the participant, with the timer on the top right. Answer the question as the participant and click Submit or press Enter. On the host side move to the next question. Bug: timer for the second question does not appear to the participant. Desired behavior after PR is merged: Fix: The logic to hide the timer is on `survey_form.js`. In the `_nextScreen` function. Specifically, when `options.isFinish` is set to `true`. It is set to `true` in the `_onSubmit` function. This function triggers when the participant submits an answer. The fix: only set that flag to `true` if there isn't a session in progress. This aligns with the other calls to `_nextScreen` for live sessions. That is, when a timer expires and the form is submitted: `'isFinish': !this.options.sessionInProgress` and when a notification is received for the session: `isFinish: nextPageEvent.type === 'end_session'` Meaning that for live sessions the only time `isFinish` should be set to `true`, is when the `end_session` notification is received. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#246147 Forward-Port-Of: odoo/odoo#241086
This fixes Saudi e-invoicing for non-Saudi customers by allowing an additional buyer identification number to be entered when needed. If a VAT number is unavailable, invoices can now use this alternative ID, helping businesses generate compliant ZATCA XML documents more reliably.
Original PR description
This commit makes l10n_sa_additional_identification_number visible for non-Saudi individuals and company partners when the active company is in Saudi Arabia and keeps the identification scheme fixed to OTH, keeping it invisible. When generating ZATCA XML for non-Saudi partners, VAT is used as the primary buyer ID if present, and fall back to the additional identification number when VAT is missing. task-4525956 Forward-Port-Of: odoo/odoo#246398 Forward-Port-Of: odoo/odoo#245832
Accounting settings now prevent users from choosing receivable or payable accounts as default income or expense accounts. This avoids configuration mistakes that could later block bill creation with user-facing errors.
Original PR description
**Steps to reproduce:** - Install Accounting - Go to "Accounting / Configuration / Settings" - Edit "Expense Account" property **Issue:** It is possible to set a receivable or payable account for the property. If a payable account is set, a constraint will trigger a UserError when trying to create a bill with that account. The same issue happens for "Income Account". **Cause:** The field is a related field. On the original field, there is a domain to prevent selecting these types of account, but the domain is not applied to the related field. **Solution:** Checking the company will retrieve the domain set on the original field. opw-5492092 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix lets public and portal customers access their shopping carts even when the related sales order has passed its expiry date. It prevents customers from being blocked by an error message when returning to an existing cart, improving the online purchasing flow.
Original PR description
### Issue: Due to this issue public and portal users cannot access their carts once the so is expired. #### Steps to reproduce: 1- Using a public user, add a product to cart. 2- Using the admin user, go to the sale order created for the cart. 3- Change the date to an earlier date. 4- Back to public user, try to access the cart. You will see the error message: `The sale order has expired.` ### Cause: This regression is due to #245772. `_get_payment_values` is not the right place to check if the so is expired, as it is also used inside `_get_express_shop_payment_values`. opw-5903033
Repeated invoice requests from the POS customer page could send the same electronic invoice multiple times when clicks happened concurrently. This fix prevents duplicate external invoice submissions, reducing customer confusion and avoiding repeated Peppol or EDI deliveries.
Original PR description
Steps to reproduce: - Set up a test company with Peppol enabled - Create a Peppol customer and ensure they can receive invoices - In the POS settings, enable the “Self-service invoicing” option - Create a POS order linked to the Peppol customer - Log in as this customer - Enter the POS order information to access the invoice download view (from /pos/ticket) - Repeatedly click the “Request invoice” button - On the Peppol side, you will notice that the same invoice is sent as many times as the button is clicked Why ? This error is not really Peppol related, it can actually happen with all EDI making API call: the transaction is rolled-back when Odoo tries to create multiple invoices with a serialization error but the API call happened so, in this cas, the invoice is sent over Peppol. opw-5469467 Forward-Port-Of: odoo/odoo#246731
Automatic purchase orders created from sales orders now skip vendors whose validity date has expired when choosing a fallback supplier. This helps ensure purchases are assigned to an active vendor, reducing ordering mistakes and manual corrections.
Original PR description
_______________________________________ ## Short functional explanation of the error Let's say we have a products that has 2 suppliers. In the list of suppliers, the first one set has an expired…
_______________________________________ ## Short functional explanation of the error Let's say we have a products that has 2 suppliers. In the list of suppliers, the first one set has an expired date. If we generate a PO from an SO with the MTO route, the assigned supplier for this product will be the first one set in the list, therefore having an expired date. ## Reproduction Steps 1. Go to Inventory > Configuration > settings. Check the option Multi-Step routes. 2. Click on Configuration > routes and unarchive the Replenish On Order (MTO) route. 3. Create or use an already existing product. Go to the Inventory tab, and under Operations, check the routes Replenish on Order (MTO) and Buy. 4. Click on the Purchase tab. There, set a first vendor for which the end date is earlier than today. For the second one, set an end date for which the date is later than today. 5. Go to Sales and create a new quotation. Set a customer and add a line with the product you just set. Click confirm. A smart button 'Purchase' should appear. Click on it. ### Expected behavior The assigned vendor of the PO should be the second vendor as it isn't expired yet. ### Unexpected behavior The assigned vendor is the first vendor, expired. ## Origin of the issue When generating a PO from an SO, the partner is the person ordering the product, not the vendor. Therefore, when this code is executed: https://github.com/odoo/odoo/blob/6b677319c47baacb5bee829b7e41448dec4136eb/addons/purchase_stock/models/stock_rule.py#L62-L66 no corresponding supplier is found, as ```self._get_partner_id(procurement.values, rule)``` returns the customer and not the vendor. This leads us to the fallback: https://github.com/odoo/odoo/blob/6b677319c47baacb5bee829b7e41448dec4136eb/addons/purchase_stock/models/stock_rule.py#L68-L72 which doesn't take into account the end date of vendors. __ opw-5030849 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#245313 Forward-Port-Of: odoo/odoo#225684
This update corrects a bug where canceled refunds were incorrectly included in global invoices generated from Point of Sale (PoS) orders. The fix filters out canceled refund lines during invoice generation, ensuring accurate invoice totals. This improves the reliability of financial reporting for Mexican businesses using the PoS system.
Original PR description
When generating global invoices for orders in the PoS, refund of those orders are also included in the global invoice. However, if the refund has been canceled, it should not be included in the global invoice. Steps to reproduce: ------------------- * Create a PoS order and validate it. * Go to the backend and create a refund for that order. * Cancel the refund. * Go to the PoS order list and select the original order * Click on "Generate Global Invoice" > Observation: The canceled refund is included in the global invoice. Why the fix: ------------ We simply filter out the canceled orders when searching for refunded order lines. opw-5492576 Forward-Port-Of: odoo/enterprise#105959 Forward-Port-Of: odoo/enterprise#105868
This update fixes an error in how secondary contract payrolls are calculated for Kenyan companies. The issue stemmed from an undefined variable, which has now been corrected to use the total taxable gross amount. This ensures accurate payroll processing for employees on secondary contracts.
Original PR description
Steps to reproduce: With a Kenyan company, create an employee. Check the "Secondary Contract" on the employee form view. Create a payslip and compute. There is an error in the payslip computation. Cause: There is an undefined variable "remaining_gross". Fix: Replace it by the total taxable gross. Task: 5462310 Forward-Port-Of: odoo/enterprise#103229