Thursday, July 9, 2026
18 changes · 18.0
Enhancements to existing features
This change adds validation for the contact email in the registration wizard before the process continues. It helps users catch invalid email addresses right away, avoiding errors later in the flow and making registration smoother.
Original PR description
No verification was done on the contact email in the wizard but if the email is invalid the user would have an error later task-6344324
Resolved issues and error corrections
Error details for French reports with accepted or rejected statuses now render properly instead of showing incorrectly or failing to display. This helps users understand report processing results without confusion.
Original PR description
A mismatch between error titles and status logs was introduced in 18.0. Markup wasn't added to the status logs, leading to a type mismatch (Markup + str) when displaying errors for 'accepted' or 'rejected' statuses. As a result, the logs were not interpreted as HTML. This commit ensures Markup is applied to each element to guarantee coherence and proper rendering. backport of 5113752 task-6053842
Miscellaneous changes
all_sm is referenced by two branches of the outer UNION, so PostgreSQL materializes it (a CTE used more than once is an optimization fence). Every query on report_stock_quantity therefore builds the forecast for all products/warehouses first and filters afterwards, so predicates like product_id can't reach the stock_move scan. This hurts single-product lookups such as _read_group() in mrp_report_bom_structure._get_stock_availability(), called repeatedly per component while rendering a BoM rep
Original PR description
all_sm is referenced by two branches of the outer UNION, so PostgreSQL materializes it (a CTE used more than once is an optimization fence). Every query on report_stock_quantity therefore builds the…
all_sm is referenced by two branches of the outer UNION, so PostgreSQL materializes it (a CTE used more than once is an optimization fence). Every query on report_stock_quantity therefore builds the forecast for all products/warehouses first and filters afterwards, so predicates like product_id can't reach the stock_move scan.
This hurts single-product lookups such as _read_group() in mrp_report_bom_structure._get_stock_availability(), called repeatedly per component while rendering a BoM report: each call does a full stock_move scan plus two sorts that spill to disk.
Marking all_sm NOT MATERIALIZED lets the planner inline it and push the product_id/warehouse_id filter down to an index scan, removing the full scan, the GENERATE_SERIES expansion and the on-disk sorts. warehouse_cte stays materialized (small and cheap).
Query generated from the _read_group:
```SQL
SELECT MIN("report_stock_quantity"."date")
FROM "report_stock_quantity"
WHERE (
(
(
(
("report_stock_quantity"."state" = 'forecast')
AND ("report_stock_quantity"."date" >= '2026-07-08')
)
AND ("report_stock_quantity"."product_id" = 15743)
)
AND ("report_stock_quantity"."product_qty" >= 1.0)
)
AND ("report_stock_quantity"."warehouse_id" = 4)
)
AND ("report_stock_quantity"."company_id" IN (1))
```
Before:
800 ms by _read_group in _get_stock_availability().
For opening a BoM overview with hundred of components takes 17 sec.
After:
25 ms by _read_group in _get_stock_availability().
Same BoM overview drops to 3 sec to open.
cc @Aurelienvd
I can send you EXPLAIN ANALYZE, but I prefer to not putting them here to avoid data leakage.Sendcloud shipping labels now correctly keep address numbers that include a dot, such as 12.345. This prevents incorrect house numbers being sent to Sendcloud and helps ensure delivery labels match the customer's full address.
Original PR description
Issue ----- Labels have unexpected format when the delivery address has a dot (`.`) in the number. Steps to reproduce ----- - Set up Sendcloud (carrier shouldn't matter) - Enable logs - Create a customer (with valid address, phone and email) - Address must contain a dot, eg Grand Place 12.345 - Deliver a product to the customer - Add sendcloud as delivery method - Go to the logs - Open the "sendcloud request parcels" log > house_number is 12 Cause ----- The `house_number` field is populated using `_get_house_number`, where the regex used to extract the number from the address line does not accept the `.` character. https://github.com/odoo/enterprise/blob/f93882555864a1f0a2a3e3863780096c78923bfa/delivery_sendcloud/models/sendcloud_service.py#L323 ----- Ticket: opw-6295904
Changing a project's visibility no longer triggers an unnecessary warning when the project contains shortcut documents. This prevents confusion for users managing project documents and keeps visibility updates smoother.
Original PR description
In 18.0 versions, when there is a shortcut document linked to a project, a warning appears as the access rights of a shortcut document is changed. This fix filters out the shortcut documents beforehand. To reproduce on runbot: 1. Go to a project with documents 2. Go to documents 3. Create shortcut of a document 4. Try to change visibility of project, warning occurs opw-6353516
This update makes Odoo’s stdnum integration accept additional options when creating a SOAP client. It improves compatibility with external services that require extra connection settings, reducing the chance of integration errors.
Original PR description
Added support for additional keyword arguments in stdnum's new_get_soap_client function. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
A domain in the rule "Attendance Officer: Restrict Overtime to managed employees" is overly complicated and can be simplified to only one domain leaf. opw-6295888
Original PR description
A domain in the rule "Attendance Officer: Restrict Overtime to managed employees" is overly complicated and can be simplified to only one domain leaf. opw-6295888
### Purpose of this PR: * Prevent the powerbox from opening inside a static file box. * Paste content as plain text inside a static file box, since formatting is not supported there. * Prevent the cursor from leaving an empty file name box when pressing `ArrowLeft` or `ArrowRight`. * o_we_preview_favicon had a fixed height larger than its content, causing the link popover to overflow and show an unwanted scrollbar when the file input was empty as there was nothing to preview. Reduced the h
Original PR description
### Purpose of this PR: * Prevent the powerbox from opening inside a static file box. * Paste content as plain text inside a static file box, since formatting is not supported there. * Prevent the cursor from leaving an empty file name box when pressing `ArrowLeft` or `ArrowRight`. * o_we_preview_favicon had a fixed height larger than its content, causing the link popover to overflow and show an unwanted scrollbar when the file input was empty as there was nothing to preview. Reduced the height to match the actual content size. * Shorthands (such as `#` for headings or `*` for lists) should not trigger when editing inside a file box. task-6283420 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
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
Original PR description
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
In python-stdnum 2.0+, the upstream issue regarding the zeep Transport class timeout handling has been resolved arthurdejong/python-stdnum@6cbb9bc09c25fbda7a032521bc57b44e0ce18ec4), and the method signature for `get_soap_client` was updated to include the `verify` parameter. Applying our legacy monkey patch on python-stdnum >= 2.0 causes signature mismatch issues and is no longer necessary. This commit: - Restricts the `get_soap_client` monkey patch to run only for `python-stdnum < 2.0`.
Original PR description
In python-stdnum 2.0+, the upstream issue regarding the zeep Transport class timeout handling has been resolved arthurdejong/python-stdnum@6cbb9bc09c25fbda7a032521bc57b44e0ce18ec4), and the method signature for `get_soap_client` was updated to include the `verify` parameter. Applying our legacy monkey patch on python-stdnum >= 2.0 causes signature mismatch issues and is no longer necessary. This commit: - Restricts the `get_soap_client` monkey patch to run only for `python-stdnum < 2.0`. - Updates `requirements.txt` to use python-stdnum 2.2 for Python 3.14+ to ensure compatibility with Ubuntu Resolute. Forward-Port-Of: odoo/odoo#275046
When having multiple companies selected at the same time, _get_conversion_rate returns: File "/data/build/odoo/odoo/orm/fields_misc.py", line 114, in __get__ raise ValueError("Expected singleton: %s" % record) 1 - Create a new company with currency EUR. 2 - Create a branch company underneath the main company. 3 - In Accounting, install fiscal localization, e.g. Belgian Companies on the company configuration settings. 4 - Select an account like 600000 Raw Materials, and enable All
Original PR description
When having multiple companies selected at the same time, _get_conversion_rate returns: File "/data/build/odoo/odoo/orm/fields_misc.py", line 114, in __get__ raise ValueError("Expected singleton: %s"…
When having multiple companies selected at the same time, _get_conversion_rate returns:
File "/data/build/odoo/odoo/orm/fields_misc.py", line 114, in __get__
raise ValueError("Expected singleton: %s" % record)
1 - Create a new company with currency EUR.
2 - Create a branch company underneath the main company.
3 - In Accounting, install fiscal localization, e.g. Belgian Companies on the company configuration settings.
4 - Select an account like 600000 Raw Materials, and enable Allow Reconciliation on this account. The exact account isn't important, only that we can make credits / debits to it to be reconciled.
5 - With only the top level company selected, make a debit of 100 USD, e.g. Vendor Bill, set in currency USD to the account 600000.
6 - Now with only the branch level company selected, make a credit of 100EUR, e.g. Customers Invoices, set in currency EUR to the same account with an amount equal to the credit in step 5. (if 1USD == 1EUR, 1-1), so that there is no residual amount, i.e. credit == debit.
7 - Now select both the top level company and the sub branch company in the company context.
8- In Journal Items, reconcile the unreconciled journal items for the Account 600000.
opw-6290703
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-prSteps to reproduce ------------------- - Install sale_project, accountant and project_timesheet_forecast_sale modules; - Activate analytic accounting in the settings; - Add an outstanding account to the bank journal’s manual outgoing payment method; - Create a new billable project; - Open the top menu, add vendor bills and open it; - Create a new bill from there, it should use the project’s analytic distribution; - Confirm it and create a payment; - Open the payment’s journal entry, i
Original PR description
Steps to reproduce ------------------- - Install sale_project, accountant and project_timesheet_forecast_sale modules; - Activate analytic accounting in the settings; - Add an outstanding account to…
Steps to reproduce ------------------- - Install sale_project, accountant and project_timesheet_forecast_sale modules; - Activate analytic accounting in the settings; - Add an outstanding account to the bank journal’s manual outgoing payment method; - Create a new billable project; - Open the top menu, add vendor bills and open it; - Create a new bill from there, it should use the project’s analytic distribution; - Confirm it and create a payment; - Open the payment’s journal entry, it is using the analytic distribution too; Why is it happening -------------------- When opening a vendor bill from the project, the project_id is added to the account.move's context to use the correct analytic distribution when we create a bill. If we create a payment after accessing the bill from this route, the context is transfered to account.payment.register, and then to the payment's entry lines in the `_create_payments` method. Due to the `_compute_analytic_distribution` method, the project's distribution is used on the payment's entry. We propose to filter out payment lines in this compute method. opw-6329475 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Steps: 1. Create an analytic model filtered by partner. 2. Create an analytic model filtered by product. 3. Create a project with an analytic distribution. (Make sure the distributions use different plans) 4. Create an SO for a product within the project that both the models apply to. 5. Confirm the SO. 6. Notice the analytic distribution for the project account is at 200%. When an SOL is created, the analytic distribution from each model is added as a separte line The analytic accoun
Original PR description
Steps: 1. Create an analytic model filtered by partner. 2. Create an analytic model filtered by product. 3. Create a project with an analytic distribution. (Make sure the distributions use different plans) 4. Create an SO for a product within the project that both the models apply to. 5. Confirm the SO. 6. Notice the analytic distribution for the project account is at 200%. When an SOL is created, the analytic distribution from each model is added as a separte line The analytic account for the project is added to each analytic distribution line. This can easily cause the account to have >100% distribution for a given SOL. This is unintuitive and confusing behaviour. This PR changes the behaviour to only create one line for all the distributions from analytic models. This should prevent this behaviour while keeping the functionality of applying the project distribution to each line. opw-6250908 / opw-6304033
**Steps to reproduce:** - activate subcontracting in the settings - create a storable product 'comp' with categ avco auto - confirm a PO and validate receipt for 1 comp at 10 - create a storable product 'prod' with categ avco auto - in the purchase tab set a vendor with a price of 30 - create a subcontracting bom with the same vendor as as the subcontractor and 1 quantity of our comp products in the components - confirm a PO for 1 prod with the subcontractor as the vendor - on
Original PR description
**Steps to reproduce:** - activate subcontracting in the settings - create a storable product 'comp' with categ avco auto - confirm a PO and validate receipt for 1 comp at 10 - create a storable…
**Steps to reproduce:** - activate subcontracting in the settings - create a storable product 'comp' with categ avco auto - confirm a PO and validate receipt for 1 comp at 10 - create a storable product 'prod' with categ avco auto - in the purchase tab set a vendor with a price of 30 - create a subcontracting bom with the same vendor as as the subcontractor and 1 quantity of our comp products in the components - confirm a PO for 1 prod with the subcontractor as the vendor - on the receipt, click on 'record component and change the 'quantity' to 2 then click on record production (you might need to click anywhere on the form to have the 'record production' appear) - validate the receipt - create and confirm the Bill for the 2 quantity - open valuation view **Current behavior:** an extra stock valuation layer was created with no quantity, no reference and a total value of 20 **Expected behavior:** This extra layer is not needed, the two svl for the receipt are made with a quantity of 2 and so is the bill, so the amls created are as follow : - svl comp : - credit 20 stock valuation - debit 20 cost of production - svl prod : - debit 80 stock valuation - credit 60 stock interim received - credit 20 cost of production - amls from the bill : - 60 stock interim received - 60 account payable After all of this the result is : debit 60 in stock valuation credit 60 account payable which is what we want The extra layer is not needed and creates unbalance, its amls are : credit 20 stock interim received debit 20 stock valuation **Cause of the issue:** This extra svl comes from the _apply_price_difference() method https://github.com/odoo/odoo/blob/deeecf7cd02e7383b591835b0c6495e3ddead0ff/addons/purchase_stock/models/account_invoice.py#L131 This method creates svl and/or amls to compensate difference between the bill price and the PO price (see https://github.com/odoo/odoo/pull/126536 for more details) To check if there is a difference, we compute the layer_price_unit https://github.com/odoo/odoo/blob/deeecf7cd02e7383b591835b0c6495e3ddead0ff/addons/purchase_stock/models/account_move_line.py#L140 and pass it as parameter to _prepare_pdiff_vals() https://github.com/odoo/odoo/blob/deeecf7cd02e7383b591835b0c6495e3ddead0ff/addons/purchase_stock/models/account_move_line.py#L159 were it will be compared to the aml price https://github.com/odoo/odoo/blob/deeecf7cd02e7383b591835b0c6495e3ddead0ff/addons/purchase_stock/models/account_move_line.py#L239-L244 In the case of a subcontracted product, price unit of the bill (30 in our case) should be the price unit of the svl of the subcontracted product (40) - the price of the comps for one unit of the subcontracted product (10). That's why when we call _get_layer_price_unit, there is a mrp_subcontracting_purchase override to remove the component price from the svl of the subcontracted product. https://github.com/odoo/odoo/blob/deeecf7cd02e7383b591835b0c6495e3ddead0ff/addons/mrp_subcontracting_purchase/models/stock_valuation_layer.py#L18-L20 To get the price of the components for on unit of the subcontracted product, we divide the value of the components svls by production.product_uom_qty, but that is not necessarily the same number as the quantity of the svl of the subcontracted product (in most cases yes but not in the case of our steps to reproduce for instance). In terms of account move lines the quantity of the svl is the one that maters so that's the one that we should take into account also in this computation. opw-6191829
In the send wizard, don't mark attachments as "unsupported" if the sending method is not ubl dependent Also fix a typo in "Unspported" no-task
Original PR description
In the send wizard, don't mark attachments as "unsupported" if the sending method is not ubl dependent Also fix a typo in "Unspported" no-task
When a push subscription is renewed by the browser (typically every few days), the pushsubscriptionchange event fires and the service worker attempts to re-register the new subscription endpoint via register_devices(). However, the VAPID public key was missing from the request kwargs. The server-side register_devices() always validates the VAPID key first and raises InvalidVapidError when it is absent. This caused the renewed subscription to never be saved in the database, silently breaking p
Original PR description
When a push subscription is renewed by the browser (typically every few days), the pushsubscriptionchange event fires and the service worker attempts to re-register the new subscription endpoint via…
When a push subscription is renewed by the browser (typically every few days), the pushsubscriptionchange event fires and the service worker attempts to re-register the new subscription endpoint via register_devices(). However, the VAPID public key was missing from the request kwargs. The server-side register_devices() always validates the VAPID key first and raises InvalidVapidError when it is absent. This caused the renewed subscription to never be saved in the database, silently breaking push notifications after the first subscription renewal. Fix by extracting the applicationServerKey from the new subscription's options and encoding it as a base64url string (without padding) — matching the existing logic in webclient.js _arrayBufferToBase64(). Description of the issue/feature this PR addresses: Current behavior before PR: Subscriptions don't get renewed causing push notifications to stop eventually. Desired behavior after PR is merged: Subscriptions get renewed successfully. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Step to reproduce: - install pos_discount and l10n_es_edi_tbai_pos with demo data - start pos, add a product and a discount of 10% - fulfill the order - go to backend and open that order - from "TicketBai" page, open the "TicketBAI Post File" xml file Observation: - `ImporteUnitario` and `ImporteTotal` were exported as positive values for discount lines. cause: - Commit [1] assumed tax details are always positive. - This is not generally true, in case we have price_unit < 0 - Th
Original PR description
Step to reproduce: - install pos_discount and l10n_es_edi_tbai_pos with demo data - start pos, add a product and a discount of 10% - fulfill the order - go to backend and open that order - from…
Step to reproduce: - install pos_discount and l10n_es_edi_tbai_pos with demo data - start pos, add a product and a discount of 10% - fulfill the order - go to backend and open that order - from "TicketBai" page, open the "TicketBAI Post File" xml file Observation: - `ImporteUnitario` and `ImporteTotal` were exported as positive values for discount lines. cause: - Commit [1] assumed tax details are always positive. - This is not generally true, in case we have price_unit < 0 - The logic relied on `is_refund`, which depends on `qty * price`. - `_l10n_es_tbai_get_values` then multiplied values by `-1` again for refunds. https://github.com/odoo/odoo/blob/e751fa1e010dbda63903d598048ef415709b4af4/addons/l10n_es_edi_tbai_pos/models/pos_order.py#L177-L181 - For discount lines, `is_refund = True` and `price = -10`, resulting in `-10 * -1 = 10`. Fix: - Ensure tax detail values are always returned as positive values, matching the assumption introduced in commit [1] [1] https://github.com/odoo/odoo/commit/03d55104e49aa65aa4c6475e199747fe9132e754 opw-6226003 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
**Steps to reproduce:** - Install the `l10n_it_edi` module and switch to an IT Company. - Create and confirm an invoice for a non-Italian customer. - Send the invoice, making sure that only `by Email` is enabled. - Attempt to send the invoice again. **Issue:** - In `18.0`, the XML file is not generated when re-sending an invoice that was previously sent only by email. - Starting from `19.0`, attempting to `Send to Tax Agency` raises an error: `UnboundLocalError: cannot access local
Original PR description
**Steps to reproduce:** - Install the `l10n_it_edi` module and switch to an IT Company. - Create and confirm an invoice for a non-Italian customer. - Send the invoice, making sure that only `by…
**Steps to reproduce:** - Install the `l10n_it_edi` module and switch to an IT Company. - Create and confirm an invoice for a non-Italian customer. - Send the invoice, making sure that only `by Email` is enabled. - Attempt to send the invoice again. **Issue:** - In `18.0`, the XML file is not generated when re-sending an invoice that was previously sent only by email. - Starting from `19.0`, attempting to `Send to Tax Agency` raises an error: `UnboundLocalError: cannot access local variable 'attachment_name' where it is not associated with a value` **Root cause:** At [1], `_get_alerts` method does not check whether the invoice was previously sent only by email. As a result, the warning banner is not displayed, and allows the user to `Send to Tax Agency`. **Fix:** Restore the expected behavior by preventing `Send to Tax Agency` when the invoice was previously sent only by email. Instead, display the appropriate warning message instructing the user to delete the PDF attachment before sending to the Tax Agency, matching the behavior in `17.0` (confirmed with PO). [1]: https://github.com/odoo/odoo/blob/c0d8d36481e106f0209521bbb127cb3b1ad1059a/addons/l10n_it_edi/models/account_move_send.py#L27-L33 opw-6293519