Daily updates from Odoo
Friday, July 17, 2026
354 changes
9 changes
Enhancements to existing features
Move French e-invoicing technical statuses out of the main invoice UI and into the chatter. The PPF status is kept available in debug mode for troubleshooting, while regular users get a concise chatter summary with the PA status, PPF status, and any returned errors. Duplicate Lifecycle XML attachments are no longer posted in the chatter. Task-6273270 Forward-Port-Of: odoo/odoo#276770 Forward-Port-Of: odoo/odoo#269465
Original PR description
Move French e-invoicing technical statuses out of the main invoice UI and into the chatter. The PPF status is kept available in debug mode for troubleshooting, while regular users get a concise chatter summary with the PA status, PPF status, and any returned errors. Duplicate Lifecycle XML attachments are no longer posted in the chatter. Task-6273270 Forward-Port-Of: odoo/odoo#276770 Forward-Port-Of: odoo/odoo#269465
Resolved issues and error corrections
When editing a partner, changing fields like `routing_identifier` triggers the onchange method `_onchange_verify_peppol_status()`, which in turn calls `button_account_peppol_check_partner_endpoint()`. Before: Calling `self.invalidate_recordset(...)` directly on the virtual record cleare the cache, reverting the user's unsaved input in the form back to the database value. After: Calling `self._origin.invalidate_recordset(...)` instead invalidates the database record cache, preserving the
Original PR description
When editing a partner, changing fields like `routing_identifier` triggers the onchange method `_onchange_verify_peppol_status()`, which in turn calls `button_account_peppol_check_partner_endpoint()`. Before: Calling `self.invalidate_recordset(...)` directly on the virtual record cleare the cache, reverting the user's unsaved input in the form back to the database value. After: Calling `self._origin.invalidate_recordset(...)` instead invalidates the database record cache, preserving the unsaved changes in the UI. > This fix was discussed with and suggested by @clbr-odoo no-task
**Description of the problem** The age verification popup snippet (`s_age_verification_popup`) does not work properly when dropped into the `#product_details` element of a product page. In particular, the popup is rendered below the blurred background and cannot be interacted with (it should be rendered above the blur instead). **How to reproduce** 1. Open a product page. 2. Drop `s_age_verification_popup` into the product details. 3. The popup is rendered below the blurred background.
Original PR description
**Description of the problem** The age verification popup snippet (`s_age_verification_popup`) does not work properly when dropped into the `#product_details` element of a product page. In…
**Description of the problem** The age verification popup snippet (`s_age_verification_popup`) does not work properly when dropped into the `#product_details` element of a product page. In particular, the popup is rendered below the blurred background and cannot be interacted with (it should be rendered above the blur instead). **How to reproduce** 1. Open a product page. 2. Drop `s_age_verification_popup` into the product details. 3. The popup is rendered below the blurred background. **Why the problem happens** The `s_age_verification_popup` snippet applies the blur effect to the `#wrapwrap` element. The popup is expected to be rendered above the blur thanks to its `z-index`. However, `z-index` only applies within the same stacking context. A child element cannot be rendered above elements outside its parent's stacking context, regardless of how high its own `z-index` is. Since `#product_details` has a defined `z-index`, it creates a stacking context. As a result, the popup, which is a child of `#product_details`, is rendered below the blur element. **Fix** This commit adds an SCSS rule to `#product_details` so that it no longer creates a stacking context when it contains an open age verification popup. To prevent the stacking context from being created, `z-index` is set to `auto` and `position` to `relative`. task-6358990 Forward-Port-Of: odoo/odoo#275368
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 Forward-Port-Of: odoo/odoo#276085 Forward-Port-Of: odoo/odoo#275217
The `retry` button shown after a failed initial fetch did nothing when clicked (it goes through `thread.fetchMoreMessages` which is for load older and load newer). This change routes the click through `fetchMessages` when the failure is an initial one. task-6223358 backport of #272153 Forward-Port-Of: odoo/odoo#276444 Forward-Port-Of: odoo/odoo#275300
Original PR description
The `retry` button shown after a failed initial fetch did nothing when clicked (it goes through `thread.fetchMoreMessages` which is for load older and load newer). This change routes the click through `fetchMessages` when the failure is an initial one. task-6223358 backport of #272153 Forward-Port-Of: odoo/odoo#276444 Forward-Port-Of: odoo/odoo#275300
This fixes two bugs in the web push subscription flow: - register_devices() compared partner records with 'is not' instead of '!='. Records loaded via sudo() live in a different environment than self.env.user, so 'is not' was always True and the ownership guard never behaved as intended. Use '!=', which compares record identity by model and id as Odoo's ORM intends. - webclient.js sent the previous subscription endpoint under the snake_case key 'previous_endpoint', while the server reads i
Original PR description
This fixes two bugs in the web push subscription flow:
- register_devices() compared partner records with 'is not' instead of '!='. Records loaded via sudo() live in a different environment than self.env.user, so 'is not' was always True and the ownership guard never behaved as intended. Use '!=', which compares record identity by model and id as Odoo's ORM intends.
- webclient.js sent the previous subscription endpoint under the snake_case key 'previous_endpoint', while the server reads it as 'previousEndpoint' (kw.get('previousEndpoint', endpoint)). The mismatch meant the lookup always fell back to the new endpoint, so a refreshed subscription created a duplicate device instead of updating the existing one. Send the camelCase key to match the server.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#276082there is a typo in https://github.com/odoo/odoo/pull/275945 leading to a traceback. opw-6398050 Forward-Port-Of: odoo/odoo#277223
Original PR description
there is a typo in https://github.com/odoo/odoo/pull/275945 leading to a traceback. opw-6398050 Forward-Port-Of: odoo/odoo#277223
The partner credit limit warning on quotations and customer invoices depends on which company the user is currently working in, instead of the company of the document itself. Steps to reproduce: - Enable Sale Credit Limit in the settings of My Company (San Francisco) - Set a Credit Limit of 100 on a customer, e.g. Deco Addict - Create a draft quotation of 500 for that customer => The credit limit warning banner is displayed, as expected - Switch the active company to any other company, fo
Original PR description
The partner credit limit warning on quotations and customer invoices depends on which company the user is currently working in, instead of the company of the document itself. Steps to reproduce: -…
The partner credit limit warning on quotations and customer invoices depends on which company the user is currently working in, instead of the company of the document itself. Steps to reproduce: - Enable Sale Credit Limit in the settings of My Company (San Francisco) - Set a Credit Limit of 100 on a customer, e.g. Deco Addict - Create a draft quotation of 500 for that customer => The credit limit warning banner is displayed, as expected - Switch the active company to any other company, for example My Company (Chicago), keeping access to both companies - Open the same quotation again => The warning banner is gone, although neither the quotation nor the customer changed The credit fields used to build the warning are evaluated against the user's active company: credit_limit is a company-dependent field, and credit / credit_to_invoice are computed on the receivables of the current company. When the active company is not the document's company, the warning is checked against the wrong ledger and the wrong limit, so it can disappear on an over-limit customer or show up for a healthy one. Both computes already contain the line that was meant to handle this, but the result of with_company() was discarded, making it a no-op. Assign it, as every other compute in these files already does, so the warning is always evaluated in the document's company. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276917 Forward-Port-Of: odoo/odoo#276308
Problem: When importing XML files to generate vendor bills, the system uses UBL parser and assumes that the PriceAmount node needs to be divided by the BaseQuantity node to obtain the exact price unit. However, in Colombia, the DIAN treats the PriceAmount node as the exact price unit. This was not flagged in the system so the parser incorrectly divides the PriceAmount by BaseQuantity, resulting in negative discounts to be added to match the subtotal. Solution: Extract the basis_qty logic into
Original PR description
Problem: When importing XML files to generate vendor bills, the system uses UBL parser and assumes that the PriceAmount node needs to be divided by the BaseQuantity node to obtain the exact price…
Problem: When importing XML files to generate vendor bills, the system uses UBL parser and assumes that the PriceAmount node needs to be divided by the BaseQuantity node to obtain the exact price unit. However, in Colombia, the DIAN treats the PriceAmount node as the exact price unit. This was not flagged in the system so the parser incorrectly divides the PriceAmount by BaseQuantity, resulting in negative discounts to be added to match the subtotal. Solution: Extract the basis_qty logic into a helper method so other localizations can override when needed. Current behavior: When importing a Colombian XML with a product that has a BaseQuantity greater than 1, the PriceAmount gets incorrectly divided, resulting in negative discounts on the vendor bill. Expected Behavior: When importing a Colombian XML with a product that has a BaseQuantity greater than 1, the PriceAmount gets parses as the exact unit price with no negative discounts applied. Task [link](https://www.odoo.com/odoo/project.task/6215466) task-6215466 Forward-Port-Of: odoo/odoo#276763 Forward-Port-Of: odoo/odoo#273129
9 changes
Enhancements to existing features
When a barcode scan matches a specific product variant, the product configurator now behaves the same as when the user searches by barcode: - The `always`-mode attribute (e.g. Color) is preselected from the matched variant instead of being hidden or left blank. - The `no_variant`-mode attributes (e.g. Size) are shown for user input. opw-6220883 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276800 Forward-Port-Of: odo
Original PR description
When a barcode scan matches a specific product variant, the product configurator now behaves the same as when the user searches by barcode: - The `always`-mode attribute (e.g. Color) is preselected from the matched variant instead of being hidden or left blank. - The `no_variant`-mode attributes (e.g. Size) are shown for user input. opw-6220883 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276800 Forward-Port-Of: odoo/odoo#265189
Resolved issues and error corrections
Currently, when adding a line discount it does not reflect on the receipt. Steps to reproduce: ------------------- * Add items to the order * For one of them use a line discount (% button) * Pay the order * Generate the receipt > The mention x% discount off on y is not displayed Why the fix: ------------ On the product screen the mention is shown and the value is computed here: https://github.com/odoo/odoo/blob/fc2230fb44dc421fe280d49da9b2d8134e1a0702/addons/point_of_sale/static/s
Original PR description
Currently, when adding a line discount it does not reflect on the receipt. Steps to reproduce: ------------------- * Add items to the order * For one of them use a line discount (% button) * Pay the order * Generate the receipt > The mention x% discount off on y is not displayed Why the fix: ------------ On the product screen the mention is shown and the value is computed here: https://github.com/odoo/odoo/blob/fc2230fb44dc421fe280d49da9b2d8134e1a0702/addons/point_of_sale/static/src/app/models/accounting/pos_order_line_accounting.js#L54-L66 We can reuse this fonction for the frontend, in the backend we translate it. We don't show this mention for the chosen products of a combo product. opw-6290821 Forward-Port-Of: odoo/odoo#270734
Before this commit, in the test `test_ticket_price_with_currency_conversion` the exchange rate was not correctly assigned because `rate_ids` was not initialized when running tests on app install. As a result the the rate was left empty and the currency conversion was not applied. This commit creates the rate_ids in the test to make sure the rates are correctly applied. Runbot error: https://runbot.odoo.com/odoo/error/242431 --- I confirm I have signed the CLA and read the PR guidelines
Original PR description
Before this commit, in the test `test_ticket_price_with_currency_conversion` the exchange rate was not correctly assigned because `rate_ids` was not initialized when running tests on app install. As a result the the rate was left empty and the currency conversion was not applied. This commit creates the rate_ids in the test to make sure the rates are correctly applied. Runbot error: https://runbot.odoo.com/odoo/error/242431 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276218 Forward-Port-Of: odoo/odoo#265503
Steps to reproduce: - Create a promotions program with a rule granting 1 point per currency unit spent (minimum 2 items and 50.00 spent) and a reward "10% discount on the cheapest product" for 1 point - Open a PoS session and add 2 products to trigger the program - Keep adding products to the order Issue: A new "10% on the cheapest product" line is added for every product added. In eCommerce and Sales the discount is only applied once. Cause: The rule grants far more points than the r
Original PR description
Steps to reproduce: - Create a promotions program with a rule granting 1 point per currency unit spent (minimum 2 items and 50.00 spent) and a reward "10% discount on the cheapest product" for 1…
Steps to reproduce: - Create a promotions program with a rule granting 1 point per currency unit spent (minimum 2 items and 50.00 spent) and a reward "10% discount on the cheapest product" for 1 point - Open a PoS session and add 2 products to trigger the program - Keep adding products to the order Issue: A new "10% on the cheapest product" line is added for every product added. In eCommerce and Sales the discount is only applied once. Cause: The rule grants far more points than the reward costs, so the reward remains claimable after being applied. The auto-claim loop of `updateRewards` therefore re-applies it on every order change, stacking one discount line per change. The already-applied check in `getClaimableRewards` only covered 'coupons' programs, while `_get_claimable_rewards` in sale_loyalty also skips discount rewards already present on the order lines. Solution: When auto-claiming, skip discount rewards that are already applied on the order, unless they belong to a payment program (ewallet, gift card). The reward can still be claimed manually several times through the Reward button by spending more points. opw-6380421 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276300
Steps to reproduce: - Create a partner without pincode - Create a picking and Challan for that partner - Click print Will result in the following traceback- ```py Traceback (most recent call last): File "<1103>", line 710, in template_l10n_in_ewaybill_report_ewaybill_1103 File "<1103>", line 692, in template_l10n_in_ewaybill_report_ewaybill_1103_content File "<1103>", line 674, in template_l10n_in_ewaybill_report_ewaybill_1103_t_call_0 File "<1103>", line 86, in template_l10n
Original PR description
Steps to reproduce: - Create a partner without pincode - Create a picking and Challan for that partner - Click print Will result in the following traceback- ```py Traceback (most recent call last):…
Steps to reproduce:
- Create a partner without pincode
- Create a picking and Challan for that partner
- Click print
Will result in the following traceback-
```py
Traceback (most recent call last):
File "<1103>", line 710, in template_l10n_in_ewaybill_report_ewaybill_1103
File "<1103>", line 692, in template_l10n_in_ewaybill_report_ewaybill_1103_content
File "<1103>", line 674, in template_l10n_in_ewaybill_report_ewaybill_1103_t_call_0
File "<1103>", line 86, in template_l10n_in_ewaybill_report_ewaybill_1103_t_call_1
File "/home/odoo/odoo18/community/addons/l10n_in_ewaybill/models/l10n_in_ewaybill.py", line 727, in _ewaybill_generate_direct_json
**self._prepare_ewaybill_base_json_payload(),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo18/community/addons/l10n_in_ewaybill_stock/models/l10n_in_ewaybill.py", line 289, in _prepare_ewaybill_base_json_payload
ewaybill_json = super()._prepare_ewaybill_base_json_payload()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo18/community/addons/l10n_in_ewaybill/models/l10n_in_ewaybill.py", line 669, in _prepare_ewaybill_base_json_payload
**prepare_details(
^^^^^^^^^^^^^^^^
File "/home/odoo/odoo18/community/addons/l10n_in_ewaybill/models/l10n_in_ewaybill.py", line 641, in prepare_details
f"{place}{key}": fun(partner, place) if key == "StateCode" else fun(partner)
^^^^^^^^^^^^
File "/home/odoo/odoo18/community/addons/l10n_in_ewaybill/models/l10n_in_ewaybill.py", line 674, in <lambda>
"Pincode": lambda p: int(p.zip) if p.country_id.code == "IN" else 999999,
^^^^^^^^^^
ValueError: invalid literal for int() with base 10: ''
```
In this commit, we resolve the traceback
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#276180Klipy requires attribution with "Search KLIPY" as the default placeholder [1]. This commit complies these guidelines. [1]: https://docs.klipy.com/attribution Forward-Port-Of: odoo/odoo#276514 Forward-Port-Of: odoo/odoo#275677
Original PR description
Klipy requires attribution with "Search KLIPY" as the default placeholder [1]. This commit complies these guidelines. [1]: https://docs.klipy.com/attribution Forward-Port-Of: odoo/odoo#276514 Forward-Port-Of: odoo/odoo#275677
Problem: When importing XML files to generate vendor bills, the system uses UBL parser and assumes that the PriceAmount node needs to be divided by the BaseQuantity node to obtain the exact price unit. However, in Colombia, the DIAN treats the PriceAmount node as the exact price unit. This was not flagged in the system so the parser incorrectly divides the PriceAmount by BaseQuantity, resulting in negative discounts to be added to match the subtotal. Solution: Extract the basis_qty logic into
Original PR description
Problem: When importing XML files to generate vendor bills, the system uses UBL parser and assumes that the PriceAmount node needs to be divided by the BaseQuantity node to obtain the exact price…
Problem: When importing XML files to generate vendor bills, the system uses UBL parser and assumes that the PriceAmount node needs to be divided by the BaseQuantity node to obtain the exact price unit. However, in Colombia, the DIAN treats the PriceAmount node as the exact price unit. This was not flagged in the system so the parser incorrectly divides the PriceAmount by BaseQuantity, resulting in negative discounts to be added to match the subtotal. Solution: Extract the basis_qty logic into a helper method so other localizations can override when needed. Current behavior: When importing a Colombian XML with a product that has a BaseQuantity greater than 1, the PriceAmount gets incorrectly divided, resulting in negative discounts on the vendor bill. Expected Behavior: When importing a Colombian XML with a product that has a BaseQuantity greater than 1, the PriceAmount gets parses as the exact unit price with no negative discounts applied. Task [link](https://www.odoo.com/odoo/project.task/6215466) task-6215466 Forward-Port-Of: odoo/odoo#276763 Forward-Port-Of: odoo/odoo#273129
..., pos_restaurant, pos_self_order, pos_urban_piper --- During the refactor of the receipt printing system, some CSS classes were no longer loaded with preparation tickets. As a result, preparation tickets lost part of their original styling. To restore the expected rendering, ensure all required classes are properly loaded while keeping the loading minimal. Additionally, some receipt data were missing after the refactor and some UI elements could be improved. This commit restores t
Original PR description
..., pos_restaurant, pos_self_order, pos_urban_piper --- During the refactor of the receipt printing system, some CSS classes were no longer loaded with preparation tickets. As a result, preparation tickets lost part of their original styling. To restore the expected rendering, ensure all required classes are properly loaded while keeping the loading minimal. Additionally, some receipt data were missing after the refactor and some UI elements could be improved. This commit restores the missing data and improves the overall UI. It also fixes an issue where customer notes were not printed on the receipt. Templates checked: * point_of_sale.pos_order_change_receipt * point_of_sale.pos_order_change_receipt_line --- Task: https://www.odoo.com/odoo/project/1737/tasks/6133403 Refacto: https://github.com/odoo/odoo/pull/244395 Forward-Port-Of: odoo/odoo#267161
there is a typo in https://github.com/odoo/odoo/pull/275945 leading to a traceback. opw-6398050 Forward-Port-Of: odoo/odoo#277223
Original PR description
there is a typo in https://github.com/odoo/odoo/pull/275945 leading to a traceback. opw-6398050 Forward-Port-Of: odoo/odoo#277223
6 changes
Resolved issues and error corrections
The `account_journal_type.bank` KPI (surfaced by the Databases app as `account_journal_type_bank`, tooltip "Draft entries in journal Bank") is meant to report journal entries in bank journals that still need attention: drafts, posted-but-unchecked entries, and posted bank entries that are not yet reconciled. The "not yet reconciled" case was implemented as: ```LEFT JOIN account_bank_statement_line st_line ON move.statement_line_id = st_line.id AND (st_line.id IS NULL O
Original PR description
The `account_journal_type.bank` KPI (surfaced by the Databases app as `account_journal_type_bank`, tooltip "Draft entries in journal Bank") is meant to report journal entries in bank journals that…
The `account_journal_type.bank` KPI (surfaced by the Databases app as `account_journal_type_bank`, tooltip "Draft entries in journal Bank") is meant to report journal entries in bank journals that still need attention: drafts, posted-but-unchecked entries, and posted bank entries that are not yet reconciled.
The "not yet reconciled" case was implemented as:
```LEFT JOIN account_bank_statement_line st_line
ON move.statement_line_id = st_line.id
AND (st_line.id IS NULL OR NOT st_line.is_reconciled)```
`st_line.id IS NULL` does not only match unreconciled bank transactions, it also matches any posted move booked directly in a bank journal that never originated from an imported bank statement line (manual entries, bank fees, opening balances, ...). Those moves have no `statement_line_id` by design and can never be reconciled, so they were counted as "pending" forever, permanently inflating the KPI for any database with such entries.
Only require an actual bank statement line before flagging it as unreconciled, matching the logic already used for the "to reconcile" count on the journal dashboard (account_journal_dashboard.py).
opw-6199785
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr`qty_available` is by default not computed with sudo, while the `total_value` is. This means that in `_update_standard_price`, the customer configuration can have a negative impact on the standard_price compute. For example, if a user create a custom rule so that specific users have access to specific warehouses only, compute `total_value / qty_available` can actually mean `global_total_value / partial_qty_available`, which creates an aberrant standard price. To fix this issue, the _update
Original PR description
`qty_available` is by default not computed with sudo, while the `total_value` is. This means that in `_update_standard_price`, the customer configuration can have a negative impact on the…
`qty_available` is by default not computed with sudo, while the `total_value` is. This means that in `_update_standard_price`, the customer configuration can have a negative impact on the standard_price compute.
For example, if a user create a custom rule so that specific users have access to specific warehouses only, compute `total_value / qty_available` can actually mean `global_total_value / partial_qty_available`, which creates an aberrant standard price.
To fix this issue, the _update_standard_price must be done in sudo.
OPW-6243363
---
## Test result without fix
```
2026-06-17 12:24:50,810 47572 ERROR oes_test_19 odoo.addons.stock_account.tests.test_stockvaluation: FAIL: TestStockValuation.test_update_standard_price_with_limited_access_users
Traceback (most recent call last):
File "/home/odoo/Odoo/src/19.0/odoo/addons/stock_account/tests/test_stockvaluation.py", line 3624, in test_update_standard_price_with_limited_access_users
self.assertEqual(product.standard_price, 1.0)
AssertionError: 12.11111111111111 != 1.0
```
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#270559Before this commit, in the test `test_ticket_price_with_currency_conversion` the exchange rate was not correctly assigned because `rate_ids` was not initialized when running tests on app install. As a result the the rate was left empty and the currency conversion was not applied. This commit creates the rate_ids in the test to make sure the rates are correctly applied. Runbot error: https://runbot.odoo.com/odoo/error/242431 --- I confirm I have signed the CLA and read the PR guidelines
Original PR description
Before this commit, in the test `test_ticket_price_with_currency_conversion` the exchange rate was not correctly assigned because `rate_ids` was not initialized when running tests on app install. As a result the the rate was left empty and the currency conversion was not applied. This commit creates the rate_ids in the test to make sure the rates are correctly applied. Runbot error: https://runbot.odoo.com/odoo/error/242431 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276218 Forward-Port-Of: odoo/odoo#265503
Only admin users have read access to the `payment.provider` model. Opening the PoS payment method form as a non-admin would raise an access error because the `online_payment_provider_ids` many2many field tries to fetch `payment.provider` records on form load. Grant read-only access on `payment.provider` to `group_pos_manager` so POS admins can use the field. Restrict the field's group in the form view to `point_of_sale.group_pos_manager,base.group_system` so it is not rendered for users witho
Original PR description
Only admin users have read access to the `payment.provider` model. Opening the PoS payment method form as a non-admin would raise an access error because the `online_payment_provider_ids` many2many field tries to fetch `payment.provider` records on form load. Grant read-only access on `payment.provider` to `group_pos_manager` so POS admins can use the field. Restrict the field's group in the form view to `point_of_sale.group_pos_manager,base.group_system` so it is not rendered for users without either role. opw-6208656 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276678 Forward-Port-Of: odoo/odoo#263837
Steps to reproduce: - Create a partner without pincode - Create a picking and Challan for that partner - Click print Will result in the following traceback- ```py Traceback (most recent call last): File "<1103>", line 710, in template_l10n_in_ewaybill_report_ewaybill_1103 File "<1103>", line 692, in template_l10n_in_ewaybill_report_ewaybill_1103_content File "<1103>", line 674, in template_l10n_in_ewaybill_report_ewaybill_1103_t_call_0 File "<1103>", line 86, in template_l10n
Original PR description
Steps to reproduce: - Create a partner without pincode - Create a picking and Challan for that partner - Click print Will result in the following traceback- ```py Traceback (most recent call last):…
Steps to reproduce:
- Create a partner without pincode
- Create a picking and Challan for that partner
- Click print
Will result in the following traceback-
```py
Traceback (most recent call last):
File "<1103>", line 710, in template_l10n_in_ewaybill_report_ewaybill_1103
File "<1103>", line 692, in template_l10n_in_ewaybill_report_ewaybill_1103_content
File "<1103>", line 674, in template_l10n_in_ewaybill_report_ewaybill_1103_t_call_0
File "<1103>", line 86, in template_l10n_in_ewaybill_report_ewaybill_1103_t_call_1
File "/home/odoo/odoo18/community/addons/l10n_in_ewaybill/models/l10n_in_ewaybill.py", line 727, in _ewaybill_generate_direct_json
**self._prepare_ewaybill_base_json_payload(),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo18/community/addons/l10n_in_ewaybill_stock/models/l10n_in_ewaybill.py", line 289, in _prepare_ewaybill_base_json_payload
ewaybill_json = super()._prepare_ewaybill_base_json_payload()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo18/community/addons/l10n_in_ewaybill/models/l10n_in_ewaybill.py", line 669, in _prepare_ewaybill_base_json_payload
**prepare_details(
^^^^^^^^^^^^^^^^
File "/home/odoo/odoo18/community/addons/l10n_in_ewaybill/models/l10n_in_ewaybill.py", line 641, in prepare_details
f"{place}{key}": fun(partner, place) if key == "StateCode" else fun(partner)
^^^^^^^^^^^^
File "/home/odoo/odoo18/community/addons/l10n_in_ewaybill/models/l10n_in_ewaybill.py", line 674, in <lambda>
"Pincode": lambda p: int(p.zip) if p.country_id.code == "IN" else 999999,
^^^^^^^^^^
ValueError: invalid literal for int() with base 10: ''
```
In this commit, we resolve the traceback
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#276180there is a typo in https://github.com/odoo/odoo/pull/275945 leading to a traceback. opw-6398050 Forward-Port-Of: odoo/odoo#277223
Original PR description
there is a typo in https://github.com/odoo/odoo/pull/275945 leading to a traceback. opw-6398050 Forward-Port-Of: odoo/odoo#277223
20 changes
Enhancements to existing features
Test lints should be deterministic so retrying them doesn't make sense. While lints are not the slowest, pylint can take 10~15mn in later branches, and with retrying that's half an hour (and some) to get a failure, which is all that in wasted staging and build time. Forward-Port-Of: odoo/odoo#276600
Original PR description
Test lints should be deterministic so retrying them doesn't make sense. While lints are not the slowest, pylint can take 10~15mn in later branches, and with retrying that's half an hour (and some) to get a failure, which is all that in wasted staging and build time. Forward-Port-Of: odoo/odoo#276600
The Nilvera client returns base64-encoded PDF content from the GET pdf endpoint. A previous change https://github.com/odoo-dev/odoo/commit/d2c2dc0bc33c369369ba748a513fad0c6edcaf6a incorrectly stored this base64 string directly into the `ir.attachment.raw` field without decoding. Because the field is binary, the base64 ASCII text was UTF-8 encoded on the disk, leaving the files unreadable in browsers. This commit introduces a migration script to fix this. For newly downloaded attachment
Original PR description
The Nilvera client returns base64-encoded PDF content from the GET pdf endpoint. A previous change https://github.com/odoo-dev/odoo/commit/d2c2dc0bc33c369369ba748a513fad0c6edcaf6a incorrectly stored this base64 string directly into the `ir.attachment.raw` field without decoding. Because the field is binary, the base64 ASCII text was UTF-8 encoded on the disk, leaving the files unreadable in browsers. This commit introduces a migration script to fix this. For newly downloaded attachments, 35fd6ce handles the decoding. task-6377121 Forward-Port-Of: odoo/odoo#275376
Resolved issues and error corrections
The cron to send e-invoices might be stuck in an infinite loop if the error received is considered as "Networking error". zeep.exceptions.Fault hinerits from zeep.exceptions.Error so it will be catch as a "Networking error" and will retry to send the invoice in the next cron run. This PR proposes a way to set those documents sent to "error" state if a fault exception is detected. opw-6085114 Forward-Port-Of: odoo/odoo#276225
Original PR description
The cron to send e-invoices might be stuck in an infinite loop if the error received is considered as "Networking error". zeep.exceptions.Fault hinerits from zeep.exceptions.Error so it will be catch as a "Networking error" and will retry to send the invoice in the next cron run. This PR proposes a way to set those documents sent to "error" state if a fault exception is detected. opw-6085114 Forward-Port-Of: odoo/odoo#276225
Steps to reproduce: - Create a partner without pincode - Create a picking and Challan for that partner - Click print Will result in the following traceback- ```py Traceback (most recent call last): File "<1103>", line 710, in template_l10n_in_ewaybill_report_ewaybill_1103 File "<1103>", line 692, in template_l10n_in_ewaybill_report_ewaybill_1103_content File "<1103>", line 674, in template_l10n_in_ewaybill_report_ewaybill_1103_t_call_0 File "<1103>", line 86, in template_l10n
Original PR description
Steps to reproduce: - Create a partner without pincode - Create a picking and Challan for that partner - Click print Will result in the following traceback- ```py Traceback (most recent call last):…
Steps to reproduce:
- Create a partner without pincode
- Create a picking and Challan for that partner
- Click print
Will result in the following traceback-
```py
Traceback (most recent call last):
File "<1103>", line 710, in template_l10n_in_ewaybill_report_ewaybill_1103
File "<1103>", line 692, in template_l10n_in_ewaybill_report_ewaybill_1103_content
File "<1103>", line 674, in template_l10n_in_ewaybill_report_ewaybill_1103_t_call_0
File "<1103>", line 86, in template_l10n_in_ewaybill_report_ewaybill_1103_t_call_1
File "/home/odoo/odoo18/community/addons/l10n_in_ewaybill/models/l10n_in_ewaybill.py", line 727, in _ewaybill_generate_direct_json
**self._prepare_ewaybill_base_json_payload(),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo18/community/addons/l10n_in_ewaybill_stock/models/l10n_in_ewaybill.py", line 289, in _prepare_ewaybill_base_json_payload
ewaybill_json = super()._prepare_ewaybill_base_json_payload()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo18/community/addons/l10n_in_ewaybill/models/l10n_in_ewaybill.py", line 669, in _prepare_ewaybill_base_json_payload
**prepare_details(
^^^^^^^^^^^^^^^^
File "/home/odoo/odoo18/community/addons/l10n_in_ewaybill/models/l10n_in_ewaybill.py", line 641, in prepare_details
f"{place}{key}": fun(partner, place) if key == "StateCode" else fun(partner)
^^^^^^^^^^^^
File "/home/odoo/odoo18/community/addons/l10n_in_ewaybill/models/l10n_in_ewaybill.py", line 674, in <lambda>
"Pincode": lambda p: int(p.zip) if p.country_id.code == "IN" else 999999,
^^^^^^^^^^
ValueError: invalid literal for int() with base 10: ''
```
In this commit, we resolve the traceback
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#276180Before this commit: - When importing a **UBL** or **Factur-X (CII)** invoice, Odoo determines whether the document should be imported as an invoice or a credit note based on the `TaxExclusiveAmount (UBL)` /` TaxBasisTotalAmount (Factur-X)`. - In some rare cases, a valid invoice can contain a negative `TaxExclusiveAmount` / `TaxBasisTotalAmount` while still having a positive `TaxInclusiveAmount` / `GrandTotalAmount`. - In such situations, Odoo incorrectly imports the document as a credit note
Original PR description
Before this commit: - When importing a **UBL** or **Factur-X (CII)** invoice, Odoo determines whether the document should be imported as an invoice or a credit note based on the `TaxExclusiveAmount…
Before this commit: - When importing a **UBL** or **Factur-X (CII)** invoice, Odoo determines whether the document should be imported as an invoice or a credit note based on the `TaxExclusiveAmount (UBL)` /` TaxBasisTotalAmount (Factur-X)`. - In some rare cases, a valid invoice can contain a negative `TaxExclusiveAmount` / `TaxBasisTotalAmount` while still having a positive `TaxInclusiveAmount` / `GrandTotalAmount`. - In such situations, Odoo incorrectly imports the document as a credit note. Technical reason: - The method `_get_import_document_amount_sign()` uses `TaxExclusiveAmount` / `TaxBasisTotalAmount `to determine whether the imported document is an invoice or a refund. After this commit: - **UBL** now uses `TaxInclusiveAmount` instead of `TaxExclusiveAmount`, and **Factur-X** now uses `GrandTotalAmount `instead of `TaxBasisTotalAmount` to determine whether the document should be imported as an invoice or a credit note. - Prevent valid invoices with negative `TaxExclusiveAmount` / `TaxBasisTotalAmount` from being incorrectly converted into credit notes. Task-6321262 Forward-Port-Of: odoo/odoo#276307 Forward-Port-Of: odoo/odoo#271829
From versions 18.3 to 19.3, company_id and siret refer to the same field. This caused an issue when connecting to the PDP using siret or siren could overwrite company_id. task-6327304 Forward-Port-Of: odoo/odoo#275276
Original PR description
From versions 18.3 to 19.3, company_id and siret refer to the same field. This caused an issue when connecting to the PDP using siret or siren could overwrite company_id. task-6327304 Forward-Port-Of: odoo/odoo#275276
_reset_inventory() counter balances the stock implied by the move history when a product becomes storable, to reset the valuation of goods received while untracked. It assumed the product had no quants. But unticking Track Inventory does not clear the existing quants, so toggling it off then on again counter balances stock that is still on hand. The quants then desynchronize from their moves and the historical stock and valuation reports show quantities before the product ever existed. Onl
Original PR description
_reset_inventory() counter balances the stock implied by the move history when a product becomes storable, to reset the valuation of goods received while untracked. It assumed the product had no quants. But unticking Track Inventory does not clear the existing quants, so toggling it off then on again counter balances stock that is still on hand. The quants then desynchronize from their moves and the historical stock and valuation reports show quantities before the product ever existed. Only counter balance the part of the move history that is not already on hand. Steps to reproduce: - Create a storable product tracked by lots, 10 units on hand - Untick then re-tick "Track Inventory" on the product - Inventory > Reporting > Inventory at Date, pick a date before the product existed > The report shows 10 units on hand although there was no stock at that date. opw-6373051 Forward-Port-Of: odoo/odoo#275565
Steps to reproduce: - Create a promotions program with a rule granting 1 point per currency unit spent (minimum 2 items and 50.00 spent) and a reward "10% discount on the cheapest product" for 1 point - Open a PoS session and add 2 products to trigger the program - Keep adding products to the order Issue: A new "10% on the cheapest product" line is added for every product added. In eCommerce and Sales the discount is only applied once. Cause: The rule grants far more points than the r
Original PR description
Steps to reproduce: - Create a promotions program with a rule granting 1 point per currency unit spent (minimum 2 items and 50.00 spent) and a reward "10% discount on the cheapest product" for 1…
Steps to reproduce: - Create a promotions program with a rule granting 1 point per currency unit spent (minimum 2 items and 50.00 spent) and a reward "10% discount on the cheapest product" for 1 point - Open a PoS session and add 2 products to trigger the program - Keep adding products to the order Issue: A new "10% on the cheapest product" line is added for every product added. In eCommerce and Sales the discount is only applied once. Cause: The rule grants far more points than the reward costs, so the reward remains claimable after being applied. The auto-claim loop of `updateRewards` therefore re-applies it on every order change, stacking one discount line per change. The already-applied check in `getClaimableRewards` only covered 'coupons' programs, while `_get_claimable_rewards` in sale_loyalty also skips discount rewards already present on the order lines. Solution: When auto-claiming, skip discount rewards that are already applied on the order, unless they belong to a payment program (ewallet, gift card). The reward can still be claimed manually several times through the Reward button by spending more points. opw-6380421 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276300
Steps to reproduce: 1. install mail 2. Send a voice message to anyone from the discuss app 3. Open the ui in mobile and see the voice messege duration Issue: - time is showing in two lines Solution: - Adjust the spacing of the voice player controls for small screens using responsive Bootstrap utility classes and prevent the duration text from shrinking, ensuring it remains on a single line while preserving the existing layout on larger screens. <table width="100%"> <tr> <th
Original PR description
Steps to reproduce:
1. install mail
2. Send a voice message to anyone from the discuss app
3. Open the ui in mobile and see the voice messege duration
Issue:
- time is showing in two lines
Solution:
- Adjust the spacing of the voice player controls for small screens using responsive Bootstrap utility classes and prevent the duration text from shrinking, ensuring it remains on a single line while preserving the existing layout on larger screens.
<table width="100%">
<tr>
<th>Before</th>
<th>After</th>
</tr>
<tr>
<td align="center">
<img alt="After" src="https://github.com/user-attachments/assets/98f41d1a-9082-4d5c-a34b-c9181e643e0a">
</td>
<td align="center">
<img alt="Before" src="https://github.com/user-attachments/assets/0f879988-b2e6-46c9-ba5a-0f935fde40ec">
</td>
</tr>
</table>
opw-6328609
Forward-Port-Of: odoo/odoo#271768When sending a batch of invoice, we first generate an unique PDF file regrouping every invoice. When at least one invoice needs more than one page to be rendered entirely, the number of pages will be greater than the number of invoices. In this case, we use <hX> tags to split invoices: https://github.com/odoo/odoo/blob/5ba945cdba6be3ff8838e56784ade16c6200de84/odoo/addons/base/models/ir_actions_report.py#L916-L923 This tag is added for PDF files, using the invoice's title: https://g
Original PR description
When sending a batch of invoice, we first generate an unique PDF file regrouping every invoice. When at least one invoice needs more than one page to be rendered entirely, the number of pages will be…
When sending a batch of invoice, we first generate an unique PDF file regrouping every invoice. When at least one invoice needs more than one page to be rendered entirely, the number of pages will be greater than the number of invoices. In this case, we use <hX> tags to split invoices: https://github.com/odoo/odoo/blob/5ba945cdba6be3ff8838e56784ade16c6200de84/odoo/addons/base/models/ir_actions_report.py#L916-L923 This tag is added for PDF files, using the invoice's title: https://github.com/odoo/odoo/blob/5ba945cdba6be3ff8838e56784ade16c6200de84/addons/web/views/report_templates.xml#L627 In some localization modules, this title shouldn't be displayed and it is removed. Example for Chile: https://github.com/odoo/odoo/blob/5ba945cdba6be3ff8838e56784ade16c6200de84/addons/l10n_cl/views/report_invoice.xml#L149 In this case, no `<h3>` tag will be added and an error is raised as we cannot separate invoices. We propose to add an empty `<h3>` tag if the document title is not defined. opw-6281187 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#269100
Update the XPath of the empty header of the 'Pay Now' button column to the last header of the table. Use a deterministic XPath expression for the 'Pay Now' button column cell. as the last cell of the table. Forward-Port-Of: odoo/odoo#272710
Original PR description
Update the XPath of the empty header of the 'Pay Now' button column to the last header of the table. Use a deterministic XPath expression for the 'Pay Now' button column cell. as the last cell of the table. Forward-Port-Of: odoo/odoo#272710
Registration token now needs to be valid for much longer than 1 hour due to manual verifications Forward-Port-Of: odoo/odoo#276550
Original PR description
Registration token now needs to be valid for much longer than 1 hour due to manual verifications Forward-Port-Of: odoo/odoo#276550
`forEach` is a synchronous operation, so it doesn't support promises. We refactor its usage to use `for` loops. task-4794299 Forward-Port-Of: odoo/odoo#276590 Forward-Port-Of: odoo/odoo#275262
Original PR description
`forEach` is a synchronous operation, so it doesn't support promises. We refactor its usage to use `for` loops. task-4794299 Forward-Port-Of: odoo/odoo#276590 Forward-Port-Of: odoo/odoo#275262
In Multiwebsite settings, when the public user interactions needs email generation (appointment or event flow), the email links are generated with a base url that does not corresponds to the one from which the request started. - Have website A and website B - Create an event and assign it to website B - As public user, access the event and register to it - Check the generated email Issue: button links in the email will redirect to the wrong website, so users will encounter an issue when
Original PR description
In Multiwebsite settings, when the public user interactions needs email generation (appointment or event flow), the email links are generated with a base url that does not corresponds to the one from which the request started. - Have website A and website B - Create an event and assign it to website B - As public user, access the event and register to it - Check the generated email Issue: button links in the email will redirect to the wrong website, so users will encounter an issue when managing the event. This occurs because the record `event.registration` has no website_id field and the base url is taken from the company default website (website A) opw-4146760 opw-4336369 Forward-Port-Of: odoo/odoo#276070 Forward-Port-Of: odoo/odoo#274051
Before this commit, in the test `test_ticket_price_with_currency_conversion` the exchange rate was not correctly assigned because `rate_ids` was not initialized when running tests on app install. As a result the the rate was left empty and the currency conversion was not applied. This commit creates the rate_ids in the test to make sure the rates are correctly applied. Runbot error: https://runbot.odoo.com/odoo/error/242431 --- I confirm I have signed the CLA and read the PR guidelines
Original PR description
Before this commit, in the test `test_ticket_price_with_currency_conversion` the exchange rate was not correctly assigned because `rate_ids` was not initialized when running tests on app install. As a result the the rate was left empty and the currency conversion was not applied. This commit creates the rate_ids in the test to make sure the rates are correctly applied. Runbot error: https://runbot.odoo.com/odoo/error/242431 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276218 Forward-Port-Of: odoo/odoo#265503
Steps to reproduce: - Install `project`, open a project and switch to the Gantt view - Start dragging a task while holding Ctrl (copy mode), then switch browser tab with Ctrl+Tab (or Ctrl+Shift+Tab, Ctrl+PgUp/PgDn) - Come back to the Gantt tab and drop the task without holding Ctrl Issue: The task is duplicated instead of rescheduled. Cause: The copy/reschedule behavior is tracked through window keydown/keyup listeners on the Control key. While the document is hidden, the keyup for Co
Original PR description
Steps to reproduce: - Install `project`, open a project and switch to the Gantt view - Start dragging a task while holding Ctrl (copy mode), then switch browser tab with Ctrl+Tab (or Ctrl+Shift+Tab, Ctrl+PgUp/PgDn) - Come back to the Gantt tab and drop the task without holding Ctrl Issue: The task is duplicated instead of rescheduled. Cause: The copy/reschedule behavior is tracked through window keydown/keyup listeners on the Control key. While the document is hidden, the keyup for Control is never received, so the drag sequence resumes with a stale "Ctrl pressed" state and the drop is treated as a copy. Fix: Keyboard and pointer states cannot be reliably tracked while the document is hidden, so cancel any ongoing drag sequence from `makeDraggableHook` as soon as the tab is no longer visible (through the `visibilitychange` event). This applies to every drag and drop instance built on the hook builder. opw-6298440 Forward-Port-Of: odoo/odoo#276859
Only admin users have read access to the `payment.provider` model. Opening the PoS payment method form as a non-admin would raise an access error because the `online_payment_provider_ids` many2many field tries to fetch `payment.provider` records on form load. Grant read-only access on `payment.provider` to `group_pos_manager` so POS admins can use the field. Restrict the field's group in the form view to `point_of_sale.group_pos_manager,base.group_system` so it is not rendered for users witho
Original PR description
Only admin users have read access to the `payment.provider` model. Opening the PoS payment method form as a non-admin would raise an access error because the `online_payment_provider_ids` many2many field tries to fetch `payment.provider` records on form load. Grant read-only access on `payment.provider` to `group_pos_manager` so POS admins can use the field. Restrict the field's group in the form view to `point_of_sale.group_pos_manager,base.group_system` so it is not rendered for users without either role. opw-6208656 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276678 Forward-Port-Of: odoo/odoo#263837
Follow-up to #247929, which limited `forever` recurrences to a 15-year horizon (`calendar.max_recurrence_years`) for *yearly* and *monthly* frequencies but left *weekly* and *daily* on the hard cap `MAX_RECURRENT_EVENT` (720), ignoring the parameter. A weekly `forever` event therefore always materializes 720 occurrences (~14 years) regardless of `calendar.max_recurrence_years`, and a daily one ignores the parameter too. This applies the year limit to weekly and daily as well, accounting for th
Original PR description
Follow-up to #247929, which limited `forever` recurrences to a 15-year horizon (`calendar.max_recurrence_years`) for *yearly* and *monthly* frequencies but left *weekly* and *daily* on the hard cap `MAX_RECURRENT_EVENT` (720), ignoring the parameter. A weekly `forever` event therefore always materializes 720 occurrences (~14 years) regardless of `calendar.max_recurrence_years`, and a daily one ignores the parameter too. This applies the year limit to weekly and daily as well, accounting for the number of selected weekdays and the interval, while still capping at `MAX_RECURRENT_EVENT`. Steps to reproduce: 1. Set `calendar.max_recurrence_years` to e.g. 2. 2. Create a weekly event repeating "forever". 3. Before: 720 occurrences (~14 years). After: ~106 (2 years). task / context: extends #247929. Forward-Port-Of: odoo/odoo#270348
there is a typo in https://github.com/odoo/odoo/pull/275945 leading to a traceback. opw-6398050 Forward-Port-Of: odoo/odoo#277223
Original PR description
there is a typo in https://github.com/odoo/odoo/pull/275945 leading to a traceback. opw-6398050 Forward-Port-Of: odoo/odoo#277223
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.
Forward-Port-Of: odoo/odoo#275457
Forward-Port-Of: odoo/odoo#2750224 changes
Resolved issues and error corrections
Before this commit: In some cases, an element may have a invisible `/n` first child from the html. When inserting content to it, the insert function doesn't check if the first child is visible, and then split the element wrongly. After this commit: we now check if the previous sibling of the current node is invisible, if so, we don't split the parent element. task-6352841 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/o
Original PR description
Before this commit: In some cases, an element may have a invisible `/n` first child from the html. When inserting content to it, the insert function doesn't check if the first child is visible, and then split the element wrongly. After this commit: we now check if the previous sibling of the current node is invisible, if so, we don't split the parent element. task-6352841 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#273958
Before this commit, validating transfers of several companies at once could assign a lot of one company to the move lines of another one, because the search for existing lots used a variable left over from a previous loop (the last move line iterated) instead of the company of the group of lines being checked. Steps to reproduce: - in a multi-company database, create a product tracked by lots - create a lot with the same name for that product in each company - create one receipt per compan
Original PR description
Before this commit, validating transfers of several companies at once could assign a lot of one company to the move lines of another one, because the search for existing lots used a variable left…
Before this commit, validating transfers of several companies at once could assign a lot of one company to the move lines of another one, because the search for existing lots used a variable left over from a previous loop (the last move line iterated) instead of the company of the group of lines being checked. Steps to reproduce: - in a multi-company database, create a product tracked by lots - create a lot with the same name for that product in each company - create one receipt per company with that lot name typed in the detailed operations, select both receipts in the Transfers list view and validate them together The lines of one company are linked to the lot of the other company and the validation is blocked by "Incompatible companies on records". When the lot only exists in one of the companies, the search misses it and the validation fails on the lot uniqueness constraint while recreating a lot that already exists. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276688
there is a typo in https://github.com/odoo/odoo/pull/275945 leading to a traceback. opw-6398050 Forward-Port-Of: odoo/odoo#277223
Original PR description
there is a typo in https://github.com/odoo/odoo/pull/275945 leading to a traceback. opw-6398050 Forward-Port-Of: odoo/odoo#277223
When creating an activity in a custom app made with studio, no image is shown, and instead the alt text is shown with a missing image. This fixes the issue by showing a placeholder icon if no module is found for the activity group. opw-6282451 Previous behavior: <img width="1315" height="568" alt="image" src="https://github.com/user-attachments/assets/1e4fc1d5-3a35-4193-80fc-f6d161776e2e" /> New behavior: <img width="1315" height="568" alt="image" src="https://github.com/user-attac
Original PR description
When creating an activity in a custom app made with studio, no image is shown, and instead the alt text is shown with a missing image. This fixes the issue by showing a placeholder icon if no module is found for the activity group. opw-6282451 Previous behavior: <img width="1315" height="568" alt="image" src="https://github.com/user-attachments/assets/1e4fc1d5-3a35-4193-80fc-f6d161776e2e" /> New behavior: <img width="1315" height="568" alt="image" src="https://github.com/user-attachments/assets/6ed5ca8b-8481-470f-b7c6-dd59c616125e" /> (Original PR: https://github.com/odoo/odoo/pull/268957 Re-based on 17.0 as its the earliest version affected, so it can be forward-ported to future versions) Forward-Port-Of: odoo/odoo#269654
5 changes
Enhancements to existing features
The Nilvera client returns base64-encoded PDF content from the GET pdf endpoint. A previous change https://github.com/odoo-dev/odoo/commit/d2c2dc0bc33c369369ba748a513fad0c6edcaf6a incorrectly stored this base64 string directly into the `ir.attachment.raw` field without decoding. Because the field is binary, the base64 ASCII text was UTF-8 encoded on the disk, leaving the files unreadable in browsers. This commit introduces a migration script to fix this. For newly downloaded attachment
Original PR description
The Nilvera client returns base64-encoded PDF content from the GET pdf endpoint. A previous change https://github.com/odoo-dev/odoo/commit/d2c2dc0bc33c369369ba748a513fad0c6edcaf6a incorrectly stored this base64 string directly into the `ir.attachment.raw` field without decoding. Because the field is binary, the base64 ASCII text was UTF-8 encoded on the disk, leaving the files unreadable in browsers. This commit introduces a migration script to fix this. For newly downloaded attachments, 35fd6ce handles the decoding. task-6377121 Forward-Port-Of: odoo/odoo#275376
Resolved issues and error corrections
there is a typo in https://github.com/odoo/odoo/pull/275945 leading to a traceback. opw-6398050
Original PR description
there is a typo in https://github.com/odoo/odoo/pull/275945 leading to a traceback. opw-6398050
hash_sign has become very used all over the place, so more and more tokens depend on `database.secret`, increasing the impact of that secret needing one day to be rotated or being compromised. To avoid making `database.secret` a single point of failure, we would like `hash_sign` to support a custom secret supplied by caller. task-6391264 Forward-Port-Of: odoo/odoo#276474
Original PR description
hash_sign has become very used all over the place, so more and more tokens depend on `database.secret`, increasing the impact of that secret needing one day to be rotated or being compromised. To avoid making `database.secret` a single point of failure, we would like `hash_sign` to support a custom secret supplied by caller. task-6391264 Forward-Port-Of: odoo/odoo#276474
**Problem:** After partially invoicing a timesheet-delivered sale order line, a second invoice for the remaining delivered quantity can no longer be created. **Steps to reproduce:** 1. Install Sales and Timesheets 2. Sell a service invoiced on delivered timesheets 3. Log a timesheet covering the whole period 4. Create an invoice, reduce its quantity to part of the delivered amount, and post it 5. Create an invoice again for the rest **Current behavior:** "Cannot create an invoice. N
Original PR description
**Problem:** After partially invoicing a timesheet-delivered sale order line, a second invoice for the remaining delivered quantity can no longer be created. **Steps to reproduce:** 1. Install Sales…
**Problem:** After partially invoicing a timesheet-delivered sale order line, a second invoice for the remaining delivered quantity can no longer be created. **Steps to reproduce:** 1. Install Sales and Timesheets 2. Sell a service invoiced on delivered timesheets 3. Log a timesheet covering the whole period 4. Create an invoice, reduce its quantity to part of the delivered amount, and post it 5. Create an invoice again for the rest **Current behavior:** "Cannot create an invoice. No items are available to invoice." is raised, even though qty_delivered - qty_invoiced is still positive. **Expected behavior:** A second invoice is created for the remaining delivered quantity. **Cause of the issue:** Since opw-6001094, `_recompute_qty_to_invoice` runs on every invoice creation, not only when a timesheet period is provided. It re-derives `qty_to_invoice` from un-invoiced timesheets alone. Posting the first invoice already linked every timesheet of the line (the linking ignores the invoiced quantity), so no un-invoiced timesheet is left, and `qty_to_invoice` is forced to 0 — discarding the still-billable remainder. **Fix:** Forcing the quantity to zero is only meaningful when the invoice is restricted to a timesheet period, where a line whose timesheets all fall outside that period genuinely has nothing to invoice. Without a period, the standard delivered-versus-invoiced quantity is the authoritative measure of what remains to bill, so the timesheet-based recomputation must not override it. opw-6295941
When creating an activity in a custom app made with studio, no image is shown, and instead the alt text is shown with a missing image. This fixes the issue by showing a placeholder icon if no module is found for the activity group. opw-6282451 Previous behavior: <img width="1315" height="568" alt="image" src="https://github.com/user-attachments/assets/1e4fc1d5-3a35-4193-80fc-f6d161776e2e" /> New behavior: <img width="1315" height="568" alt="image" src="https://github.com/user-attac
Original PR description
When creating an activity in a custom app made with studio, no image is shown, and instead the alt text is shown with a missing image. This fixes the issue by showing a placeholder icon if no module is found for the activity group. opw-6282451 Previous behavior: <img width="1315" height="568" alt="image" src="https://github.com/user-attachments/assets/1e4fc1d5-3a35-4193-80fc-f6d161776e2e" /> New behavior: <img width="1315" height="568" alt="image" src="https://github.com/user-attachments/assets/6ed5ca8b-8481-470f-b7c6-dd59c616125e" /> (Original PR: https://github.com/odoo/odoo/pull/268957 Re-based on 17.0 as its the earliest version affected, so it can be forward-ported to future versions) Forward-Port-Of: odoo/odoo#269654
1 change
Enhancements to existing features
The Nilvera client returns base64-encoded PDF content from the GET pdf endpoint. A previous change https://github.com/odoo-dev/odoo/commit/d2c2dc0bc33c369369ba748a513fad0c6edcaf6a incorrectly stored this base64 string directly into the `ir.attachment.raw` field without decoding. Because the field is binary, the base64 ASCII text was UTF-8 encoded on the disk, leaving the files unreadable in browsers. This commit introduces a migration script to fix this. For newly downloaded attachment
Original PR description
The Nilvera client returns base64-encoded PDF content from the GET pdf endpoint. A previous change https://github.com/odoo-dev/odoo/commit/d2c2dc0bc33c369369ba748a513fad0c6edcaf6a incorrectly stored this base64 string directly into the `ir.attachment.raw` field without decoding. Because the field is binary, the base64 ASCII text was UTF-8 encoded on the disk, leaving the files unreadable in browsers. This commit introduces a migration script to fix this. For newly downloaded attachments, 35fd6ce handles the decoding. task-6377121