Wednesday, May 13, 2026
6 changes · saas-18.3
Resolved issues and error corrections
This update corrects a display issue where certain quality point types were incorrectly shown regardless of the selected operations. The fix ensures these types are only visible when a manufacturing operation is selected and a work order operation is defined, improving data accuracy and usability.
Original PR description
**Steps to reproduce:** - Install `quality_control` and `mrp` modules with demo data - Go to Quality → Quality Points → Create(New) - In the `Type` field, observe that options such as 'Print Label',…
**Steps to reproduce:**
- Install `quality_control` and `mrp` modules with demo data
- Go to Quality → Quality Points → Create(New)
- In the `Type` field, observe that options such as
'Print Label', 'Register Production', etc. appear
regardless of the selected `Operations`
**Issue**:
Types like `print_label`, `register_production`,
`register_byproducts`, and `register_consumed_materials`
are displayed even when the Operations is not
Manufacturing and when no work order operation is defined.
**Expected behavior**:
These `types` should only be available when:
- `Operations` Type = Manufacturing
- `work order operation` is set
**Cause**:
The custom search logic for the `allow_registration` boolean field
relied on receiving a direct `True` or `False` value.
https://github.com/odoo/enterprise/blob/de3682c7f50b68c19d3a3429fa3768c447074f50/mrp_workorder/models/quality.py#L104
Before `saas-18.3`, the `value` passed to the domain search method was
a plain boolean(True/False).
https://github.com/odoo/enterprise/blob/de3682c7f50b68c19d3a3429fa3768c447074f50/mrp_workorder/models/quality.py#L21-L24
Starting from `saas-18.3`, the value is passed as
`OrderedSet([True])`. while the operator received is `in`
or `not in`.
Because the value always arrives `OrderedSet([True])`,
the condition checking the `value` is always
evaluated as truthy. As a result, the code never reaches the
else branch of the logic, which causes the incorrect domain
behavior.
The issue is caused by how Odoo optimizes boolean domains.
When we pass:
('allow_registration', '=', False)
the value correctly reaches the base domain logic as
`allow_registration = False`.
But during domain processing at:
https://github.com/odoo/odoo/blob/347c46f8ecff7ae97c2a686bf5750374b6c3e2d3/odoo/orm/domains.py#L967
it optimize it to:
('allow_registration', 'in', [False])
Then `_optimize()` is called, which again calls
`_optimize_step()`:
https://github.com/odoo/odoo/blob/347c46f8ecff7ae97c2a686bf5750374b6c3e2d3/odoo/orm/domains.py#L459
Inside `_optimize_step()` at:
https://github.com/odoo/odoo/blob/347c46f8ecff7ae97c2a686bf5750374b6c3e2d3/odoo/orm/domains.py#L971
it is further simplified to:n ('allow_registration', 'not in', [True])
Similarly, when we pass: ('allow_registration', '=', True)
it becomes: ('allow_registration', 'in', [True])
This is expected behavior.
The custom search method for `allow_registration`
was not handling these optimized forms correctly,
which caused Issue.
**FIX:**
- Use the domain `operator (in / not in)` to determine the intended
boolean condition instead of relying on the received value(OrderedSet([True])).
- This is correct because Odoo normalizes boolean domains to only two
forms during optimization: in [True] and not in [True]. Therefore,
the operator reliably indicates whether the condition expects
True or False, allowing the search method to handle the domain
correctly.
---
opw-5915197This update fixes an issue where returned subcontracted products were incorrectly routed to the subcontractor's location instead of the user's stock. When returning products 'for exchange', the system now correctly directs returned items to the subcontractor's location and new deliveries to the user's stock, ensuring accurate inventory tracking. This improves the efficiency of subcontracting operations.
Original PR description
## Issue When making a request for quotation for a subcontracted product and returning the delivery "for exchange", the new incoming delivery does not have the correct destination. Instead of having…
## Issue
When making a request for quotation for a subcontracted product and returning the delivery "for exchange", the new incoming delivery does not have the correct destination. Instead of having the stock of the user, the destination of the new incoming delivery is the same as its source: the subcontracting location.
<img width="1254" height="257" alt="5479900" src="https://github.com/user-attachments/assets/c7e6d392-8328-4a03-a71e-466e768f448b" />
## Steps to reproduce
1. Install MRP Subcontracting (`mrp_subcontracting`) and Purchase (`purchase`)
2. In Settings, enable *Subcontracting*
3. Create a Product P and a subcontracting BoM with Subcontractor S
4. Create a Request for Quotation
- Vendor: Subcontractor S
- Product: Product P (any quantity > 0)
5. Confirm the RFQ, receive the PO, validate the picking
6. On the validated picking, click *Return*, set the quantity of products to return, and click *Return for Exchange*
- This creates two new pickings, one to return the product(s) we received, and one to receive new products
7. Validate the two new pickings
8. **In Inventory > Reporting > Moves History, the very last `stock.move.line` has the same location in the *From* (`location_id`) and the *To* (`location_dest_id`) columns**
## Cause
The `location_dest_id` of the new `stock.move` is updated in `StockReturnPickingLine._prepare_move_default_values`.
https://github.com/odoo/odoo/blob/fb534f1eadcb8ef74e2ee6fd5b68872dddb978e3/addons/mrp_subcontracting/wizard/stock_picking_return.py#L20-L25
The condition added by https://github.com/odoo/odoo/commit/5404b426aac9 sets the destination of all returned subcontracted moves to the subcontractor location. This is incorrect when using "return for exchange", as in this case, the return move is directed towards the user's stock. In fact, when using "return for exchange", the following pickings are created:
| id | name | return_id | |
|:--:|--------------|:---------:|---|
| 1 | WH/IN/00001 | | Initial RFQ delivery |
| 2 | WH/OUT/00001 | 1 | Return of the initial RFQ delivery |
| 3 | WH/IN/00002 | 2 | New products delivery to replace the initial delivery. The stock.move.line of this stock.picking has a wrong `location_dest_id` |
## Fix
In the context of return for exchanges, the returned item must be directed to the *Subcontracting Location* while the new item must be directed to the *Stock*. In the `_prepare_move_default_values`, we should only set the `location_dest_it` to the subcontractor location for outgoing pickings.
opw-5479900This update resolves an issue where users accessing bank reconciliation within a child company were encountering access errors. The fix involves using 'sudo' to ensure the correct currency ID is retrieved, allowing proper bank reconciliation functionality within the child company environment. This improves usability for users working with multiple company structures.
Original PR description
The bug is easy to reproduce, but niche. 1. Have a company set up with a child company 2. Have a non admin user with administration rights for accounting 3. Create a bank statement in a journal with no set currency_id and fully reconcile it 4. While only in the child company, try to access the bank reconciliation widget -> access error The error occurs because of how journal_currency_id is computed on the bank rec widget. The fallback value for the currency is derived from the journal_id.company_id.currency_id which is inaccessible from the child company. To circumvent this, we just add sudo() to the call. Forward-Port-Of: odoo/enterprise#117005
This update ensures Odoo's audit trail feature in India (l10n_in) remains active, complying with Ministry of Corporate Affairs regulations. Previously, the audit trail could be disabled, but this change permanently enforces its maintenance to meet legal requirements. This ensures data integrity and reduces potential compliance risks.
Original PR description
After the refactor introduced in https://github.com/odoo/odoo/commit/f280f762b6417fa1a0b09649ffbdecafcc7e7579, The audit trail feature was split into two modes: a lightweight general-purpose mode and a force-restricted mode for specific localizations (e.g., Germany), where deactivation is not allowed once enabled. In India, as per the requirements of the Ministry of Corporate Affairs, the audit trail must be maintained and cannot be disabled once activated. This commit extends the force-restricted audit trail mode to the Indian localization (l10n_in) to ensure compliance with statutory requirements. task-6182002
This update fixes an issue where tax calculations were incorrect after removing a tax line on a sales order or invoice. The fix ensures that dependent taxes (those affected by the base amount) are properly recalculated, preventing inaccurate tax amounts. This improves the reliability of financial reporting.
Original PR description
**Steps to reproduce:** * Install the *Accounting* module with French localization (*l10n_fr_account*). * Create a *Sales Tax* with: * A new tax group (e.g., 'Codifab'). * Enable *Affect Base of…
**Steps to reproduce:** * Install the *Accounting* module with French localization (*l10n_fr_account*). * Create a *Sales Tax* with: * A new tax group (e.g., 'Codifab'). * Enable *Affect Base of Subsequent Taxes*. * Create a *Sales Order*: * Add the first tax (with *Affect Base of Subsequent Taxes*). * Then add the second tax (eg VAT tax). * Confirm the *Sales Order*. * Create a *Down Payment Invoice* (percentage-based). * Open the generated invoice and: * Remove the first tax (the one affecting the base). **Observed behavior:** * The amount of the second tax group does not update after removing the first tax, leading to incorrect tax computation. **Cause:** * In `_import_base_line_extra_tax_data`, the condition: `all(str(tax.id) in extra_tax_data['manual_tax_amounts'] for tax in sorted_taxes)` only ensured partial matching of taxes. * This allowed reuse of stale `manual_tax_amounts` when taxes were removed or modified, causing incorrect base values for dependent taxes (e.g., *Affect Base of Subsequent Taxes*). **Fix:** * Update the condition to enforce an exact match between current taxes and cached `manual_tax_amounts` by checking both size and membership. * Prevent reuse of outdated tax data when taxes change, ensuring proper recomputation of dependent taxes. * Align Python logic with the JS implementation for consistency between `account_tax.py` and `account_tax.js`. opw-6063970
This update resolves an issue that prevented the final invoice from being correctly sent to ZATCA when down payments had been reversed. The fix ensures that the system properly handles both reversed and non-reversed down payment invoices, preventing a critical error. This improves the reliability of ZATCA invoice generation for SA companies.
Original PR description
Sending the final invoice of a sale order to ZATCA crashed with `ValueError: Expected singleton: account.move(a, b)` when the sale order had multiple down-payment references. Steps to reproduce: 1.…
Sending the final invoice of a sale order to ZATCA crashed with `ValueError: Expected singleton: account.move(a, b)` when the sale order had multiple down-payment references. Steps to reproduce: 1. Configure a SA company and setup ZATCA 2. Create a sale order and confirm it 3. Deliver the product line. 3. From the sale order, create a down-payment invoice (fixed amount, e.g. 115) and post it (DP1). 4. On DP1, click "Credit Note" and choose "Full refund and new draft invoice"; validate. DP1 becomes `reversed` and a new draft down-payment DP2 is created. Post DP2. 5. From the sale order, create the final regular invoice and post it. 6. Send the final invoice to ZATCA (or generate its XML) -> `ValueError: Expected singleton: account.move(a, b)`. Root cause: _l10n_sa_get_line_prepayment_vals looks up the related down-payment move through the down-payment sale order line shared with the product line. The filter matched any out_invoice with _is_downpayment() == True, so the reversed DP1 and the active DP2 both ended up in the recordset, and reading .name raised the singleton error. Prefer non-reversed down-payment moves when available, but fall back to reversed ones if no alternative exists (e.g. when generating a credit note of the final invoice after the original down-payment was itself reversed). opw-6116265 Forward-Port-Of: odoo/odoo#260980 Forward-Port-Of: odoo/odoo#259384