Daily updates from Odoo
Friday, July 31, 2026
16 changes · saas-18.3
Resolved issues and error corrections
Users can now click custom fields in the Documents list view and edit them directly, without first activating another standard field. This makes Studio-added fields behave consistently with built-in fields and reduces friction when updating document metadata.
Original PR description
**Description of the issue/feature this PR addresses:** When adding a custom field (e.g., via Studio) to the Documents list view, clicking the cell directly does not trigger inline edit mode. The…
**Description of the issue/feature this PR addresses:** When adding a custom field (e.g., via Studio) to the Documents list view, clicking the cell directly does not trigger inline edit mode. The user has to first click a standard editable field (like "Owner") to put the row into edit mode before they can modify the custom field. This occurs because we use a hardcoded whitelist (`editableColumns`) of standard fields allowed to trigger edit mode. Custom fields (`x_`) are missing from this static list. This commit resolves the issue by dynamically injecting visible, non-readonly custom fields into the `editableColumns` whitelist. This allows user-created fields to be edited inline as expected. **Steps to reproduce:** - Documents > Studio > List view > Add any field that accepts user input (e.g. Text/char) > save/exit - In the same Documents list view > select a row > click the cell belonging to the newly created field > observe that the row does not enter edit mode - In the same Documents list view > select a row > click a standard editable cell, then click the cell belonging to our newly created field > observe that this then allows us to edit our field **Current behavior before PR:** - Custom fields do not trigger inline edit mode **Desired behavior after PR is merged:** - Custom fields trigger inline edit mode opw-6378102 Forward-Port-Of: odoo/enterprise#125239
The accounting reports now handle cases where a tax was originally part of a tax group but later changed, avoiding a crash when opening the Journal Report. This helps users access audit reporting reliably even after tax configuration changes.
Original PR description
**Steps to reproduce:** - Install account_reports - Create a tax * Tax Computation: Group of Taxes * Definition: [Add a tax] - Create an invoice with that tax - Confirm the invoice - Edit the tax by changing "Tax Computation" to "Percentage" - Go to "Accounting / Reporting / Audit Reports / Journal Report" **Issue:** A KeyError is raised. **Cause:** While generating the data, a group of taxes is found in the journal items. When trying to retrieve its info from the dict listing the groups of taxes, its ID is not found but the system assumes that it's present. opw-6377465 Forward-Port-Of: odoo/enterprise#125291
This fix ensures German POS transaction cancellations succeed even when the original transaction record lacks receipt details. It avoids rejection by the fiscal certification service, reducing interruption risk during cancellation workflows while keeping existing receipt information unchanged when available.
Original PR description
When cancelling active transactions, the schema was forwarded as-is from the listed transaction. ACTIVE transactions can have an empty schema, and Fiskaly rejects the cancellation PUT with:
{
"code": "E_TX_NO_TYPE_DEFINED",
"message": "`schema.raw.process_type` must be defined for
updating or finishing a transaction",
"status_code": 409,
"error": "Conflict"
}
Fall back to a minimal CANCELLATION receipt schema when the transaction has no schema, while preserving any schema that is already present.
opw-6345005
Forward-Port-Of: odoo/enterprise#122130Quotations created from helpdesk repair orders now correctly use the salesperson assigned to the customer. This prevents sales ownership from being left blank, helping teams route follow-up and reporting correctly.
Original PR description
Currently, when a quotation is created from a repair order generated from a helpdesk ticket, the salesperson is not set on the quotation even if the customer has a salesperson assigned. **Steps to…
Currently, when a quotation is created from a repair order generated from a helpdesk ticket, the salesperson is not set on the quotation even if the customer has a salesperson assigned. **Steps to Reproduce:** - Install `helpdesk_repair`. - Go to `Helpdesk` > `Configuration` > `Helpdesk Teams`. - Open a team recod and enable `Repairs`. - Create a `contact/customer` with a `salesperson` assigned. - Go to `Helpdesk`, create a ticket for that `customer`, and select the `helpdesk team` configured above. - Click `Repair`, then click `Create Quotation`. - Open the quotation and check the `Salesperson` field in the `Other Info` tab. **Current behavior:** The Salesperson field on the quotation remains empty. **Expected behavior:** The Salesperson field on the quotation should inherit the salesperson assigned to the selected customer/contact. **Cause of the issue:** When a repair order is created from a helpdesk ticket, default_user_id [1] is passed in the context . This value is propagated when creating the repair order [2] . Later, when creating the quotation from the repair order [3], the same context is reused. Because default_user_id is already present in the context, it overrides the precomputation of user_id from the customer. As a result, user_id is initialized with an empty value and remains unset. **Fix:** This commit ensures that default_user_id is removed from the context before creating the sale order. Without a default value for user_id, the field is correctly precomputed from the selected customer, and the salesperson is properly assigned. [1]- https://github.com/odoo/enterprise/blob/2662932c7ac8ebf3ed5a05d44d7ebfaff869fcbd/helpdesk_repair/models/helpdesk_ticket.py#L52 [2]- https://github.com/odoo/enterprise/blob/2662932c7ac8ebf3ed5a05d44d7ebfaff869fcbd/helpdesk_repair/models/helpdesk_ticket.py#L36-L40 [3]: https://github.com/odoo/odoo/blob/29328b8fccff833c14de317b51f3b4e5a8c40f75/addons/repair/models/repair.py#L357 opw-6344939 Forward-Port-Of: odoo/enterprise#122980
Appointment cancellation emails are now sent in the language of the person who booked the appointment, matching confirmation emails. This avoids confusing customers with cancellation notices in the staff member’s language, while keeping the old behavior for non-appointment events.
Original PR description
**Problem:** When an appointment is cancelled, the cancellation email is always sent in the organizer's (staff member's) language, ignoring the booking customer's language. The booking confirmation,…
**Problem:**
When an appointment is cancelled, the cancellation email is always sent in the organizer's (staff member's) language, ignoring the booking customer's language. The booking confirmation, by contrast, is correctly localized.
**Steps to reproduce:**
1. Set a contact's language to a non-default one (e.g. Romanian).
2. Book an appointment for that contact (they are the booker/attendee).
3. Cancel the appointment.
4. The customer received the confirmation in Romanian but the cancellation email arrives in English.
**Current behavior:**
The cancellation email is rendered in the organizer's language.
**Expected behavior:**
The cancellation email is rendered in the booking customer's language, like the confirmation/invitation email.
**Cause of the issue:**
The cancellation uses `appointment_canceled_mail_template`, whose `lang` is `{{ object.partner_id.lang }}`. On `calendar.event`, `partner_id` is `related='user_id.partner_id'`, i.e. the organizer, not the customer. The template is posted once per event (via `_track_template`), so its single rendering language applies to every recipient, including attendees whose own language differs. The confirmation email is unaffected because it is the per-attendee `attendee_invitation_mail_template` (model `calendar.attendee`), rendered once per attendee in that attendee's language.
**Fix:**
Deriving the language from `appointment_booker_id` makes the cancellation consistent with the other appointment mails, which are meant for the person who booked the meeting. It falls back to `partner_id` when there is no booker (e.g. an event not created through the appointment flow), preserving the previous behavior in that case.
opw-6323179
Forward-Port-Of: odoo/enterprise#124116Steps to reproduce: Use the real testing credentials Create a SEZ partner Create an invoice and ewaybill Select the type of Ewaybill as Export Tax Invoice We get error code-450 which clearly states, `450 For outward-export ewaybill, To GSTIN has to be either URP or SEZ` --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
Steps to reproduce: Use the real testing credentials Create a SEZ partner Create an invoice and ewaybill Select the type of Ewaybill as Export Tax Invoice We get error code-450 which clearly states, `450 For outward-export ewaybill, To GSTIN has to be either URP or SEZ` --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This commit fix the regex used in `street_split` to be more complient. Before: address format was "street_name street_number - street_number2" Now, street_number can be in front of street_name. Format is also less strict, allowing multiple numbers in the street_name without skipping the building number. Linked: https://github.com/odoo/enterprise/pull/121674 task-6317758 Forward-Port-Of: odoo/odoo#271855
Original PR description
This commit fix the regex used in `street_split` to be more complient. Before: address format was "street_name street_number - street_number2" Now, street_number can be in front of street_name. Format is also less strict, allowing multiple numbers in the street_name without skipping the building number. Linked: https://github.com/odoo/enterprise/pull/121674 task-6317758 Forward-Port-Of: odoo/odoo#271855
- `s_three_columns` cards could end up with mismatched heights: `equalizeCardHeights` now measures header height directly instead of deriving it from the already-stretched card container. - Links appeared with a black background: exclude `btn-link` from background-color application. - Background images didn't stretch: pass `preserve_comments` so the DPI-scaling fix comment isn't stripped from the sent email. opw-6044725 --- I confirm I have signed the CLA and read the PR guidelines at w
Original PR description
- `s_three_columns` cards could end up with mismatched heights: `equalizeCardHeights` now measures header height directly instead of deriving it from the already-stretched card container. - Links appeared with a black background: exclude `btn-link` from background-color application. - Background images didn't stretch: pass `preserve_comments` so the DPI-scaling fix comment isn't stripped from the sent email. opw-6044725 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279196 Forward-Port-Of: odoo/odoo#278862
**Steps to reproduce:** 1. Install Sales app and open any sale order 2. In the "Terms and conditions" text area at the bottom, apply the "/Switch direction" command for an empty block **Issue:** The feature doesn't work when you apply it on an empty line **Why this happens:** Before the fix, the switch-direction logic depended on selected text nodes, and effectively ignored empty content nodes. **Fix:** The problem has been fixed in 19.4 as an [IMP] in this commit 9c97fc464ffba5088
Original PR description
**Steps to reproduce:** 1. Install Sales app and open any sale order 2. In the "Terms and conditions" text area at the bottom, apply the "/Switch direction" command for an empty block **Issue:** The feature doesn't work when you apply it on an empty line **Why this happens:** Before the fix, the switch-direction logic depended on selected text nodes, and effectively ignored empty content nodes. **Fix:** The problem has been fixed in 19.4 as an [IMP] in this commit 9c97fc464ffba5088642d7699b3e033c1fcc2564, so this is essentially a backport for that fix. opw-6344750 Forward-Port-Of: odoo/odoo#276536
Steps to reproduce 1. Create two products and assign the same vendor to both in the Purchase tab 2. Create two Sales Orders using the dropship route (one product per order) 3. Generate and merge the corresponding Purchase Orders, then confirm the merged PO 4. Validate the dropship transfer Issue stock.picking.sale_id is a Many2one (https://github.com/odoo/odoo/blob/18c3a034b7d3772baca62d8d86efba2ca15f17b0/addons/sale_stock/models/stock.py#L99) computed from procurement.group.sale_id, an
Original PR description
Steps to reproduce 1. Create two products and assign the same vendor to both in the Purchase tab 2. Create two Sales Orders using the dropship route (one product per order) 3. Generate and merge the…
Steps to reproduce 1. Create two products and assign the same vendor to both in the Purchase tab 2. Create two Sales Orders using the dropship route (one product per order) 3. Generate and merge the corresponding Purchase Orders, then confirm the merged PO 4. Validate the dropship transfer Issue stock.picking.sale_id is a Many2one (https://github.com/odoo/odoo/blob/18c3a034b7d3772baca62d8d86efba2ca15f17b0/addons/sale_stock/models/stock.py#L99) computed from procurement.group.sale_id, and stock.picking.group_id is a stored related on move_ids.group_id (https://github.com/odoo/odoo/blob/18c3a034b7d3772baca62d8d86efba2ca15f17b0/addons/stock/models/stock_picking.py#L186). A single picking can therefore only resolve to one SO. _create_picking (https://github.com/odoo/odoo/blob/18c3a034b7d3772baca62d8d86efba2ca15f17b0/addons/purchase_stock/models/purchase_order.py#L290) builds one picking per PO and _prepare_stock_moves assigns every move the merged PO's group_id (https://github.com/odoo/odoo/blob/18c3a034b7d3772baca62d8d86efba2ca15f17b0/addons/purchase_stock/models/purchase_order_line.py#L307), so when a merged dropship PO carries lines from multiple SOs every move lands in one picking under the PO group. Only one SO gets linked and the others stay "not fully delivered" even after validation. Solution Override PurchaseOrderLine._prepare_stock_moves to set group_id to the SO's procurement_group_id when sale_line_id is set, so each dropship move is created in its originating SO's procurement group. Override PurchaseOrder._create_picking to detect dropship POs whose order lines span more than one SO and create one picking per SO group by calling _prepare_picking and _create_stock_moves per group. picking.group_id then resolves to the SO group via the stored related field, picking.sale_id points to the right SO, and delivery_status updates correctly on validation. opw-6094608 Forward-Port-Of: odoo/odoo#257823
Before this commit, the unread banner could stop showing in a channel until it was left and opened again: - open a channel with unread messages - click "Mark as Read" - read the same channel from another device - receive a new message: the server counter increases, still no banner This happens because an implicit mark as read freezes the local unread state, so the banner stays in place while the user reads. The problem is that it stays frozen even after the banner is gone, and never fol
Original PR description
Before this commit, the unread banner could stop showing in a channel until it was left and opened again: - open a channel with unread messages - click "Mark as Read" - read the same channel from another device - receive a new message: the server counter increases, still no banner This happens because an implicit mark as read freezes the local unread state, so the banner stays in place while the user reads. The problem is that it stays frozen even after the banner is gone, and never follows the server counter again. This commit freezes that state only while something is still unread locally. This also fixes the flaky test "no unread message banner after message is deleted". https://runbot.odoo.com/odoo/error/242776 Forward-Port-Of: odoo/odoo#279195
## Problem `pttExtensionHookService` registers a global `window.addEventListener("message", ...)` handler that reads `data.from` without checking that `data` is defined first: ```js browser.addEventListener("message", ({ data, origin, source }) => { const rtc = env.services["discuss.rtc"]; if ( source !== window || origin !== location.origin || data.from !== "discuss-push-to-talk" || // <- crashes if data is undefined (!rtc && data.type !== "answer-is-
Original PR description
## Problem `pttExtensionHookService` registers a global `window.addEventListener("message", ...)` handler that reads `data.from` without checking that `data` is defined first: ```js…
## Problem
`pttExtensionHookService` registers a global `window.addEventListener("message", ...)`
handler that reads `data.from` without checking that `data` is defined first:
```js
browser.addEventListener("message", ({ data, origin, source }) => {
const rtc = env.services["discuss.rtc"];
if (
source !== window ||
origin !== location.origin ||
data.from !== "discuss-push-to-talk" || // <- crashes if data is undefined
(!rtc && data.type !== "answer-is-enabled")
) {
return;
}
...
```
Any same-window, same-origin `postMessage` sent by an unrelated browser
extension (a common content-script <-> injected-script pattern) can carry
`data === undefined`. The `source !== window` and `origin !== location.origin`
checks only filter out cross-window/cross-origin messages, so a same-origin
message from any other extension reaches this handler and crashes with:
```
TypeError: Cannot read properties of undefined (reading 'from')
```
This surfaces as an uncaught client error on any page with Discuss loaded,
after some time, unrelated to what the user is doing. The Discuss
push-to-talk extension itself does not need to be installed to trigger it,
since the crash happens before checking whether the message actually
originated from that extension.
## Solution
Use optional chaining (`data?.from`) so unrelated same-origin messages with
no `data` are safely ignored instead of crashing.
## Verification
- Reproduced against the live production `web.assets_web.min.js` bundle
(traceback matches exactly).
- Confirmed the bug is still present in the latest `18.0` of both `OCA/OCB`
and `odoo/odoo` (no newer commit touches this file since
`dc58ef1ad904`, which fixes an unrelated issue).
Forward-Port-Of: odoo/odoo#279476Steps to reproduce: Use the real testing credentials Make sure the invoice sequence is not generated on the real testing credentials 1. Create an invoice with overseas partner 2. Create ewaybill Error from the portal: `[372] Invalid or Blank Consignee Ship-to State Code` It is currently a flaw in the government portal because in Government portal there is no option for the Other country (99) for Ship to state code and only option for Other Teritory(97) It is because in reality, i
Original PR description
Steps to reproduce: Use the real testing credentials Make sure the invoice sequence is not generated on the real testing credentials 1. Create an invoice with overseas partner 2. Create ewaybill…
Steps to reproduce: Use the real testing credentials Make sure the invoice sequence is not generated on the real testing credentials 1. Create an invoice with overseas partner 2. Create ewaybill Error from the portal: `[372] Invalid or Blank Consignee Ship-to State Code` It is currently a flaw in the government portal because in Government portal there is no option for the Other country (99) for Ship to state code and only option for Other Teritory(97) It is because in reality, it should the port ship to state code but there cases where goods can be transfered to nearby country i.e. Bangladesh, Nepal where good can taken by road from India In that case the state code should be 97 task-6431082 **Second Commit** - [FIX] l10n_in_ewaybill: import/export GSTIN should be URP Steps to reproduce: Use the real testing credentials Create a SEZ partner Create an invoice and ewaybill Select the type of Ewaybill as Export Tax Invoice We get error code-450 which clearly states, `450 For outward-export ewaybill, To GSTIN has to be either URP or SEZ` --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
### Steps to reproduce: - Create 3 AVCO products: Super Kit, Kit, Comp - Super Kit BoM: 2 x Kit - Kit BoM: 1 x Comp - Create and confirm a purchase order for 1 X Super Kit at 100 - Validate the receipt of 2 Comp - Go to the valuation > Both units of Comp are valued at 100 for a total of 200 ### Cause of the issue: The issue has been introduced by: https://github.com/odoo/odoo/pull/158849/changes/713701a5035d342263e3fef2a2819b5696b6d063 To be more precise, the price unit of each u
Original PR description
### Steps to reproduce: - Create 3 AVCO products: Super Kit, Kit, Comp - Super Kit BoM: 2 x Kit - Kit BoM: 1 x Comp - Create and confirm a purchase order for 1 X Super Kit at 100 - Validate the…
### Steps to reproduce: - Create 3 AVCO products: Super Kit, Kit, Comp - Super Kit BoM: 2 x Kit - Kit BoM: 1 x Comp - Create and confirm a purchase order for 1 X Super Kit at 100 - Validate the receipt of 2 Comp - Go to the valuation > Both units of Comp are valued at 100 for a total of 200 ### Cause of the issue: The issue has been introduced by: https://github.com/odoo/odoo/pull/158849/changes/713701a5035d342263e3fef2a2819b5696b6d063 To be more precise, the price unit of each unit of Comp is expected to be computed by the `_get_price_unit`. This method used to rely on the `product_qty` appropriately: https://github.com/odoo/odoo/pull/158849/changes/713701a5035d342263e3fef2a2819b5696b6d063#diff-687527af1723e60816358020c4d83687479df62ca0cfd71079f0a82afb4b3efeL27 However, backorder adapt the move demand and hence did not provide the appropriate demand in this flow that computation logic was changed to rely on the `bom` and `bom_line` quantities: https://github.com/odoo/odoo/blob/29977a6a80442af49ecefa7fef54f085483d8f77/addons/purchase_mrp/models/stock_move.py#L20-L40 This new computation is not correct in case of nested boms since the `bom_line` only carries the unit demand on the last explosion stage. ### Additional issue: If nested kit boms lead to the creation of 2 moves with the same `cost_share` and `bom_line_id`, these moves will be merged without summing their `cost_share` leading to an under pricing of the kit since its related `stock_move`'s `cost_share` will not sum up to 100 percents anymore. This issue is tested in `test_avco_purchase_nested_kit_explode_cost_share_backorder_2` and fixed similarly to demand merging: https://github.com/odoo/odoo/blob/613f3cb7b2f4813ce6c8f53718a6cca841b081ad/addons/stock/models/stock_move.py#L1122-L1134 ### Note: We also modify the test `test_valuation_with_backorder` to be understandable and to make appropriate asserts. opw-6253776 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276242
Portal subscribe task can create tb because the task was archived before unsubscribing. opw-6397850 Forward-Port-Of: odoo/odoo#279071 Forward-Port-Of: odoo/odoo#278667
Original PR description
Portal subscribe task can create tb because the task was archived before unsubscribing. opw-6397850 Forward-Port-Of: odoo/odoo#279071 Forward-Port-Of: odoo/odoo#278667
Steps to reproduce: - Enable the ZUGFeRD (or Factur-X) e-invoicing format on a German customer. - Create a sale order for that customer, confirm it, create an invoice with a down payment. - Confirm and send the invoice to generate the PDF/XML. - Validate the XML (e.g. on portinvoice.com): it is rejected because the invoice line is missing the mandatory ram:Name field, only ram:Description is present. Cause of the issue: a down payment invoice line created from a sale order no l
Original PR description
Steps to reproduce: - Enable the ZUGFeRD (or Factur-X) e-invoicing format on a German customer. - Create a sale order for that customer, confirm it, create an invoice with a down payment. - Confirm and send the invoice to generate the PDF/XML. - Validate the XML (e.g. on portinvoice.com): it is rejected because the invoice line is missing the mandatory ram:Name field, only ram:Description is present. Cause of the issue: a down payment invoice line created from a sale order no longer carries a product_id: it only has a free-text. The Factur-X/CII export template rendered ram:Name directly from line.product_id.name with no fallback. For a line without a product, this produced an empty ram:Name element, which cleanup_xml_node then stripped entirely from the XML, leaving only ram:Description. Solution: Fall back to the line's name when there is no product opw-6391121 Forward-Port-Of: odoo/odoo#277418