Thursday, August 28, 2025
17 changes · saas-18.3
Enhancements to existing features
Odoo now treats ZATCA's duplicate-submission response for Saudi B2C invoices as confirmation that the invoice was already received. This prevents invoices from remaining in an uncertain or failed state after a timeout and reduces manual follow-up for Saudi e-invoicing users.
Original PR description
ZATCA introduced a new response code (409) to handle duplicate invoice submissions for B2C. This is helpful because when the submission timesout, we are left unsure whether ZATCA successfuly received the invoice or not. The next time Odoo tries to send the same invoice, Zatca will respond with a 409 error if it was received earlier. In which case, we mark the invoice as successfully sent. A similar flow applies for B2B, with a response code of 208 for duplicate invoices. However, for B2B, ZATCA accepts the duplicate with a warning instead of an error, So the invoice gets marked as sent in Odoo, and no changes need to be done there. task-id: 4745275 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#224478 Forward-Port-Of: odoo/odoo#223754
The UBL BIS3 e-invoicing process now uses the newer generation helpers by default, making the updated approach standard across supported versions. Businesses benefit from more consistent electronic invoice output while retaining the option to switch back if needed.
Original PR description
In 0f3a9dee5cf15 we back-ported the new refactored helpers for UBL BIS3 generation to 18.0. However the new helpers would be used only if the `ir.config.parameter` `account_edi_ubl_cii.use_new_dict_to_xml_helpers` was set to True. This commit switches the new helpers on by default in 18.0 -> 18.3, but they can still be switched off by setting that parameter to False. task-none Forward-Port-Of: odoo/odoo#218793
This update adds database indexes that help Odoo find related partners and purchase orders more quickly. Business users should see faster loading and counting on partner records, especially in databases with many contacts or purchase orders.
Original PR description
Adding some missing indexes: - `res.partner.company_registry`: necessary for the second search in `_compute_same_vat_partner_id` - `purchase.order.partner_id`: necessary for the `_read_group` in `res.partner._compute_purchase_order_count` --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#224436 Forward-Port-Of: odoo/odoo#223706
Resolved issues and error corrections
Exchange returns now treat replacement items as new stock movements, preventing old serial numbers from being reused incorrectly. This also keeps stock counts and forecasts accurate after exchanges and limits the exchange action to relevant incoming or outgoing transfers.
Original PR description
When returning tracked-by-serial products from an incoming picking for an exchange, the exchange move will have selected existing serial numbers for the new incoming picking. This happens because…
When returning tracked-by-serial products from an incoming picking for an exchange, the exchange move will have selected existing serial numbers for the new incoming picking. This happens because `move_orig_ids` is set on the exchange move(s), then `_action_assign()` happens. If there's a `move_orig_ids`, it will use the available move lines from the move_orig_ids and so assign the corresponding serial number. Additionally, the buttons to generate/import serials/lots are invisible because `origin_returned_move_id` is set on the same exchange move(s). Again, the exchange move(s) should be considered 'new' move thus having no origin. task 4893860: Additionally (again), it fixes an issue where on-hand quantities and forecasted quantities of the exchanged product were incorrect after validating the exchange. task 4778066: Finally, this PR also hides the 'Return for Exchange' button for pickings that are neither `incoming` nor `outgoing`. tasks 4748294 (& 4778066 & 4893860) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#216111
This fix ensures that when an individual customer is linked to a company, their assigned pricelist stays synchronized with the company's pricelist. It prevents quotations from unexpectedly switching to a different pricelist, helping sales teams keep pricing accurate and predictable.
Original PR description
Versions -------- - 18.0+ Steps ----- 1. Create an individual partner; 2. assign them a pricelist A; 3. create a new company partner for them; 4. create a new pricelist A & sort it on top; 5. open a…
Versions
--------
- 18.0+
Steps
-----
1. Create an individual partner;
2. assign them a pricelist A;
3. create a new company partner for them;
4. create a new pricelist A & sort it on top;
5. open a new quotation;
6. set new company as customer;
7. change customer to the individual partner.
Issue
-----
The quotation's pricelist changed from B to A. The pricelist used for the individual should be identical to the one used for their company.
Cause
-----
Commit de302c2d36305 added the `specific_property_product_pricelist` field to `res.partner`, as the way company-dependent fields are managed was changed on the database-level.
Commit 67cf577cd0a0 added the `_company_dependent_commercial_fields` method to enable syncing company-dependent commercial fields. The base method fetches all fields retrieved via `_commercial_fields`, and selects those whose `company_dependent` attribute is `True`.
In previous versions, the `_company_dependent_commercial_fields` override in `product` adds `property_product_pricelist`, as this field does not have the `company_dependent` attribute set, but it behaves as a company-dependent field. Starting from 18.0, the override adds `specific_property_product_pricelist` instead, which does have the `company_dependent` property set.
As a consequence, when `_company_dependent_commercial_sync` gets called, it does not sync the `specific_property_product_pricelist` as it's not included in the `_commercial_fields` override, nor does it sync when retrieving it from `_company_dependent_commercial_fields`, as it skips the current company, assuming the field was already handled by `_commercial_sync_from_company`: https://github.com/odoo/odoo/blob/c40760244d128cb57e11a233e89a93dd92b8fb56/odoo/addons/base/models/res_partner.py#L667-L668
Solution
--------
- Move `specific_property_product_pricelist` to `_commercial_fields`
- This enables it to sync in `_commercial_sync_from_company`
- Remove the `_company_dependent_commercial_fields` override
- `property_product_pricelist` shouldn't get synced by itself
- `specific_property_product_pricelist` is already included by the base method
opw-4988736
Forward-Port-Of: odoo/odoo#222223Website editors can now resize columns and use grid layouts correctly when editing pages in right-to-left languages such as Arabic. This improves the editing experience for multilingual websites and prevents layout controls from behaving incorrectly in RTL mode.
Original PR description
**[FIX] web_editor, mass_mailing: fix resizing columns in rtl mode** This commit fixes issues in edit mode when resizing columns or using grid mode on websites displayed in a right-to-left (RTL) language like Arabic. task-4815296 Forward-Port-Of: odoo/odoo#224313 Forward-Port-Of: odoo/odoo#217338
This fix prevents completed manufacturing orders from failing during unbuild when component tracking rules were changed after production. Businesses can now reverse eligible production without being incorrectly asked for lot or serial numbers that did not exist at the time.
Original PR description
**Description of the issue/feature this PR addresses:** This PR fixes a regression when using the _Unbuild_ feature on a previously completed `mrp.production` whose components did not originally…
**Description of the issue/feature this PR addresses:** This PR fixes a regression when using the _Unbuild_ feature on a previously completed `mrp.production` whose components did not originally require tracking (no lot/serial), but have been updated later to require it. When unbuilding such a product, `stock.move.line._action_done` currently raises a UserError requiring a lot/serial number, because the component moves created by `mrp.unbuild`: - do not have a picking_type_id, - are not inventory moves, - are not scrap moves, - and lack lot/serial information. However, in this context, the lot requirement is misleading, as the original `mrp.production` did not generate tracked components. It is valid to restore untracked components even if they are now tracked. **Current behavior before PR:** - Create an `mrp.production` for a product with untracked components. - Complete the production. - Later, enable lot tracking on one or more of the original components. - Attempt to unbuild the production. - ❌ Error is raised: “You need to supply a Lot/Serial Number for product…” **Desired behavior after PR is merged:** - The unbuild operation proceeds without error. - The untracked components are restored correctly. - Behavior remains unchanged for tracked components that did require lot info during the original MO. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#222612 Forward-Port-Of: odoo/odoo#217282
Point of Sale now shows a clear Access Denied pop-up when a user lacks permission to load sample product categories, instead of failing with a technical error. This helps administrators and users understand the permission issue and avoids a disruptive crash when opening a POS session.
Original PR description
Currently, an error occurs when a new user with the Member role and Administrator rights for the Point of Sale app attempts to open POS Category. Steps to reproduce: --- - Install the `point_of_sale`…
Currently, an error occurs when a new user with the Member role and Administrator rights for the Point of Sale app attempts to open POS Category.
Steps to reproduce:
---
- Install the `point_of_sale` module (without demo data).
- Create a new User and give Administrator rights for POS & Accounting
- Now log in with a new user in a different browser
- Open the pos session(Clothes or bar)
Traceback:
---
```py
AccessError: You are not allowed to modify 'Product Category' (product.category) records.
This operation is allowed for the following groups:
- Products/Admin en Products / Create
Contact your administrator to request access if necessary.
ParseError:while parsing /home/odoo/src/odoo/saas-18.3/addons/point_of_sale/data/scenarios/clothes_category_data.xml:5, somewhere inside <record id="product_category_clothes" model="product.category">
<field name="name">Clothes</field>
</record>
```
This commit prevents the error by displaying an "Access Denied" pop-up when a user with the "Member" role and Administrator rights attempts to open the POS Category.
sentry-6683335275, 6679363278, 6823710988
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prExpense line analytic distributions are now retained when users click Save & Close. This prevents selected cost allocations from being lost and helps keep expense reporting accurate.
Original PR description
**Issue** When creating an expense line, if the user selects an analytic distribution and clicks "Save & Close", the selected distribution is not saved. However, closing the popup with the "X" icon…
**Issue** When creating an expense line, if the user selects an analytic distribution and clicks "Save & Close", the selected distribution is not saved. However, closing the popup with the "X" icon does trigger the save. **Steps to Reproduce** 1. Go to Expenses > Expense Reports 2. Select an employee (e.g., Ronnie Hart) 3. Add a new expense line 4. Set an Analytic Distribution 5. Click Save & Close 6. Observe that the analytic distribution is not retained **Root Cause** The save behavior is triggered by a window click event, but clicks inside modals (like the analytic distribution popup) do not propagate as expected. Because everything within the popup is modal, the click does not satisfy the criteria to trigger saving the data. **Fix** Refine the modal detection logic in the `onWindowClick` handler. Specifically, allow modals that contain `this.widgetRef.el` to trigger the closing (and thus saving) behavior, ensuring that selections made in such modals are not lost when clicking Save & Close. Opw-4765799 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#213996
Saudi Arabia point-of-sale receipt QR codes now show the local KSA time instead of UTC. This helps scanned e-invoice details match the printed receipt and supports ZATCA compliance expectations.
Original PR description
**Problem:** If you have an SA company, and try to scan the QR code generated on the receipt, the time will be the UTC time instead of the KSA time, as we are in SA. **Steps to reproduce:** - Change your company to SA and install l10n_sa_pos - Make a purchase with a customer from SA - Scan the QR code from the receipt using an app such as E-invoice QR reader - The invoice date will be the UTC time, or 3 hours less than it should **Why the fix:** Before this fix, the time was always displayed as UTC. It could have been correct if it also displayed a 'Z' in the end, to indicate that it is not local KSA time. We now directly change it using the KSA time, to respect the ZATCA guidelines. The time is now the same on the printed invoice and in the app when scanning the QR code. opw-4769521 Forward-Port-Of: odoo/odoo#223348 Forward-Port-Of: odoo/odoo#216640
Fixes an Accounting issue where deleting a generated bank statement line could fail when the related journal entry used multiple reconciliation models. This helps users correct bank reconciliations without encountering a blocking error.
Original PR description
Currently, an error occurs when trying to remove a move line from a bank statement line if the journal entry contains lines with different reconciliation models. **Steps to Reproduce:** 1. Install…
Currently, an error occurs when trying to remove a move line from a bank statement line if the journal entry contains lines with different reconciliation models. **Steps to Reproduce:** 1. Install the Accounting module. 2. Duplicate the "Internal Transfer" reconciliation model. 3. Accounting dashboard > click on Bank > create new record. 4. Add the invoice, apply "Internal Transfer" reconciliation model. 5. Edit the balance of the line to lower, apply duplicated model(Internal Transfers (copy)). 6. Try deleting the generated Liquidity Transfer line. (Refer [this video](https://drive.google.com/file/d/1cz80Q5325x3rqYUlGahVzFIXH2iqrbRF/view?usp=drive_link) for steps to reproduce.) **Error:** ValueError - Expected singleton: account.reconcile.model(8, 6) **Cause:** In the method `delete_reconciled_line` at [1], the `reco_model_id` is fetched from `self.move_id.line_ids.reconcile_model_id`. If the move contains lines linked to multiple reconcile models, this causes a singleton error during subsequent processing. **Fix:** This commit uses the already computed `move_lines_to_remove` to retrieve `reconcile_model_id` instead of accessing it from `self.move_id.line_ids`, which may result in a multi-recordset. [1] - https://github.com/odoo/enterprise/blob/89aa58029a79928788277489414f8192aa5353e4/account_accountant/models/account_bank_statement.py#L1041-L1049 sentry-6781788260
Fixed an issue where enabling Shiprocket delivery could prevent users from opening the Google Merchant Center data source link. The system now avoids validating incomplete temporary customer records, preventing an unnecessary error and keeping the export flow available.
Original PR description
Steps to Reproduce: 1. Go to Delivery Methods. 2. Enable and configure Shiprocket, then publish it. 3. Go to Settings. 4. Search for Google Merchant Center Data Source. 5. Enable the checkbox. 6.…
Steps to Reproduce:
1. Go to Delivery Methods.
2. Enable and configure Shiprocket, then publish it.
3. Go to Settings.
4. Search for Google Merchant Center Data Source.
5. Enable the checkbox.
6. Click on the Copy File Link button.
7. Open the copied link in a new browser tab.
Observation:
A traceback occurs, stating that some of the customer’s fields are missing.
Issue:
In the `_prepare_best_delivery_by_country` method, we create a temporary partner
record. Later, in `_check_required_value`, we validate the Street, Email, Pincode,
and Phone Number fields for that partner. However, these fields are not set
in the temporary record, which causes the validation to fail.
Solution:
Added a check on `recipient._origin` to ensure that required field validations are only
applied on actual partner records. This prevents raising validation
errors when using temporary partner records in Shiprocket flow.
Added `@mute_logger('odoo.tools.translate')` to suppress translation warnings in
test. In our test case, we are calling `_check_required_value` directly, which
internally calls `_get_lang`. The `_get_lang` method attempts to fetch the
language from `http.request`, but since `http.request` is not available in test
context, it fails to retrieve the language and logs a warning.
https://github.com/odoo/odoo/blob/642dde9546417115cbce5b826cdf996970dfbb48/odoo/tools/translate.py#L514C5-L517C1
opw-4972889Vendor bills without taxes are no longer included in GSTR2B reconciliation. This prevents bills that were likely not filed by vendors from appearing in the reconciliation process, improving the accuracy of Indian GST reporting.
Original PR description
Before this commit- We included the Vendor bills without taxes for GSTR2B reconciliation After this commit- We exclude the Vendor bills without taxes for GSTR2B Because if no tax is there on the bill it means wasn't filed by the Vendor as well task-5023013 Forward-Port-Of: odoo/enterprise#93028 Forward-Port-Of: odoo/enterprise#92866
Fixed an issue where returning a rental order paid through Point of Sale could incorrectly increase the delivered quantity. This keeps rental order quantities accurate after returns, reducing billing and inventory confusion for users.
Original PR description
**Issue:** Before this commit, the qty_delivered was wrong when using the Return button when a Picking was made in PoS **Cause:** The `_compute_qty_delivered` method in `pos_sale` adds `pos_line.qty`…
**Issue:** Before this commit, the qty_delivered was wrong when using the Return button when a Picking was made in PoS **Cause:** The `_compute_qty_delivered` method in `pos_sale` adds `pos_line.qty` to the related `sale_line` each time it runs When there is no PoS order, the method add 0 to the line, so the expected behavior work But when you have a PoS Picking, a quantity was added to `qty_delivered` each time the `rental.order.wizard` is used The issue also occured earlier when the `flush_all()` is called in `_process_order()` **Fix:** There is already a `_compute_qty_delivered` method in `pos_sale_stock_renting` that override the `qty_delivered` But it's restricted by `_are_rental_pickings_enabled()` That's not necessary because we don't use any `stock.picking` in the `_compute_qty_delivered` function, only `stock.move` so we removed that verification To make the code working, we also need to extend the _get_outgoing_incoming_moves() results Because there were also block by a `_are_rental_pickings_enabled()` condition even if there is only moves here We need all the moves to calculate `qty_delivered` properly **Steps to reproduce:** - Create a New Product "Rental PoS" that Can be Rented - Create and confirm a New Rental Order, with any customer and your product - Open a PoS Session - Click on Quotation/Order - Choose the last Order and Settle the order - Click on Yes (to confirm import to PoS) - Click on Payment, select Cash and Pay - Go in the Backend, and to your RO - Click Return and Validate - Before the fix, the Delivered should be 2.0 opw-4877019 Forward-Port-Of: odoo/enterprise#93279 Forward-Port-Of: odoo/enterprise#90510
This fix places detraction information in the legally expected columns of the Peruvian TXT purchase report. It helps businesses generate SUNAT-compliant reports and avoid filing issues caused by misplaced data.
Original PR description
According to the Annex N.°8 of RS 040-2022/SUNAT (page 20 of https://www.sunat.gob.pe/legislacion/superin/2022/anexo-040-2022.pdf), detraction informations are considered as additional informations and should be displayed in columns 42 and 43. This commit moves detraction infos from columns 38, 39 to columns 42, 43. opw-4860530 Forward-Port-Of: odoo/enterprise#93044
The Luxembourg annual VAT declaration now correctly includes 0% custom tax rates in exported XML files, preventing rejected submissions. Employee average fields are also handled as decimal values, improving accuracy for the annual report.
Original PR description
**PROBLEM** 1. In Annexes D and E you can declare custom tax rate. If the custom rate is 0%, this percentage will not be exported into the xml. When submitting the xml to the ECDF, the submission…
**PROBLEM** 1. In Annexes D and E you can declare custom tax rate. If the custom rate is 0%, this percentage will not be exported into the xml. When submitting the xml to the ECDF, the submission will be rejected because the xml doesn't state how much % is the custom tax rate. 2. In Section V, code 110, 108 and 109 should be decimal since they are averages. **STEP TO REPRODUCE** 1. install the l10n_lu module and select the demo lu company. 2. In accouting app, goes to the annual tax report (Accounting/Reporting/Tax Report, select Annual VAT Declaration). 3. Goes to Appendix D or E, and fill code 128, 136, 144, and 162, and leave the custom rate(%) above each of them at 0%. 4. Export to xml (using the little cog thingy). 5. Notice the code 396, 394 149, and 153 doesn't appear in the xml. **CAUSE** 1. Field containing zero are filtered out the xml by default, which is the case of the custom rate (%) fields. 2. Average number of employee during the year is declared in the report data file to be of the integer type. **FIX** 1. Adding custom rate fields to the mandatory fields. 2. Changing type of average employee fields to float. opw-4978365 Forward-Port-Of: odoo/enterprise#91938
The tax return wizard now uses the company's accounting opening date to determine which returns to prepare. This ensures earlier-year returns, such as 2024, are correctly triggered instead of only processing later periods like 2025.
Original PR description
Previously, the wizard gave the impression that tax returns for both 2024 and 2025 would be generated, but only 2025 returns were processed. This fix ensures that 2024 tax returns are properly triggered when using the wizard. Solution: - use account_opening_date to get the start date of a tax return report task-5006247