Monday, September 14, 2026
19 changes · saas-19.3
Resolved issues and error corrections
The Point of Sale customer list now stays available after a coupon reward is removed from an order. This prevents a crash caused by outdated loyalty card references, helping cashiers continue checkout without interruption.
Original PR description
Steps to reproduce: - A coupon program with a coupon assigned to a customer - In the PoS, set that customer on the order - Enter the coupon code, its reward line is added - Remove the reward line -…
Steps to reproduce: - A coupon program with a coupon assigned to a customer - In the PoS, set that customer on the order - Enter the coupon code, its reward line is added - Remove the reward line - Click the customer button Issue: The customer list does not open. The PoS crashes with "TypeError: Cannot read properties of undefined (reading 'id')" raised while rendering PartnerLine. Cause: `partnerId2CouponIds` maps a partner to the ids of its `loyalty.card` records. It is filled at boot and on every `loyalty.card` create, but nothing ever removes an id from it: the models only trigger a create event. Removing the reward line of a code activated coupon deletes that card from the local models (`_setValue` in the order summary), so its id stays in the map while the record is gone. `getLoyaltyCards` pushed `models["loyalty.card"].get(id)` unconditionally, hence an `undefined` entry in the list the PartnerLine template iterates over with `t-key="_loyaltyCard.id"`. Fix: Skip the ids whose record no longer exists. The partner keeps its remaining cards, and the path where no card was deleted is unchanged. The stale id is not pruned from the map: it is reached through the reactive store proxy, and mutating it there would notify subscribers in the middle of a render. opw-6517564 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287503 Forward-Port-Of: odoo/odoo#286971
This fix stops the product information popup from opening unintentionally when users scroll the product list on touch devices. It improves the mobile point of sale experience by preventing scroll gestures from being mistaken for long presses.
Original PR description
Steps to reproduce: - Open the PoS in mobile mode (touch device) - Swipe the product list up and down a few times Issue: The product info popup sometimes opens while scrolling, as if a product had been long pressed. Cause: Since the product card long press listens to pointerdown/pointerup, a touch that turns into a scroll ends with a pointercancel and never a pointerup, so the long press timer survives the gesture. The debounced onScroll handler is the only other canceller, but when the list is still scrolling from a previous swipe the debounce is already armed, its leading call is skipped and the continuous scroll events keep delaying the trailing call past the long press duration. Fix: Cancel the long press on pointercancel, like pointerup. opw-6514079 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Odoo Mail now avoids applying code syntax highlighting inside incoming email message bodies. This prevents chatter views from failing to open when an email contains an embedded code block, improving reliability for users reading email conversations.
Original PR description
**Steps to reproduce:** - Receive a mail with an embedded code block - Open a chatter containing this message - Error: "Cannot mount a component on a detached dom node" **Issue:** Mail message are…
**Steps to reproduce:**
- Receive a mail with an embedded code block
- Open a chatter containing this message
- Error: "Cannot mount a component on a detached dom node"
**Issue:**
Mail message are rendered with a shadow dom body and isolated from surrounding styling.
```xml
<div class="o-mail-Message-shadowBody overflow-x-auto" t-if="this.message.message_type and this.message.message_type.includes('email')" t-custom-ref="shadowBody"/>
```
Then in `useLayoutEffect` parent element is created for the shadow root and the message body is created:
```js
const bodyEl = createElementWithContent(
"span",
this.message.showTranslation
? this.message.richTranslationValue
: this.props.messageSearch?.highlight(this.message.richBody) ??
this.message.richBody
);
const roots = this.prepareMessageBody(bodyEl) ?? [];
this.shadowRoot.appendChild(bodyEl);
```
But after [1] we have `return this.renderEmbeddedCodeBlocks(bodyEl);` which calls:
```js
const { root, mountPromise } = mountComponent(...);
```
And root creation fails as the element is not on the shadow root yet (due to `validateTarget` and `isAttachedToDocument`).
```js
const root = app.createRoot(Component, { props, env });
```
But even if we have the element directly added on the `shadowRoot` the highlighting styling won't be applied properly without all the needed assets.
**Fix:**
Prevent `ReadonlySyntaxHighlightingComponent` for incoming email body.
Doesn't seem worth to try to load the needed css/style elements in the shadow root.
[1] https://github.com/odoo/odoo/commit/8fa3cc31e8b81645d40b5d09c925b57d2c0558ab
opw-6463735This fixes a scheduling issue where one task that could not be placed could incorrectly block later tasks assigned to the same person. Project plans now place each task based on its own fit, helping avoid unnecessary delays in Gantt rescheduling.
Original PR description
Steps to reproduce: 1. create three tasks A,B & C with the same assignee 2. make tasks B and C depend on A and start on the `date_deadline` of task A 3. set the duration of task B (processed before C) to be longer than the 53-week search window 4. reschedule task A to end after task C starts Problem: A candidate that couldn't be scheduled was putting its assignee in conflict, subtracting the inspected availability while failing from the shared pool. Consequently, every later candidate sharing that assignee got force-placed into the same forward reschedule fallback that lands near the tail of the 53-week search window instead of the next available slot. Solution: Each candidate should be evaluated on its own ability to fit, and the time it occupies should count against the shared pool. opw-6380163 --- Forward-Port-Of: odoo/enterprise#131351 Forward-Port-Of: odoo/enterprise#129326
Fixed an inventory issue where separate packages could be combined incorrectly during the final delivery step when related delivery moves were merged. This helps warehouse teams preserve accurate package tracking and avoid confusion during shipping.
Original PR description
Steps to reproduce --- 1. Set the warehouse to deliver in 3 steps. 2. Create a delivery pick not tied to a procurement group (not from a sale order) with a consumable product, and validate it. 3. On…
Steps to reproduce --- 1. Set the warehouse to deliver in 3 steps. 2. Create a delivery pick not tied to a procurement group (not from a sale order) with a consumable product, and validate it. 3. On the pack transfer, put the goods in a package and validate. 4. Duplicate the pick, validate it, put its goods in a second package on the pack transfer, and validate. 5. Open the final delivery: both lines sit in the same package instead of one line per package. Issue --- The two picks share no procurement group, so their delivery moves merge onto a single move keyed by partner. Validating the second pack tops up that already partially reserved move: for a consumable, `_action_assign` builds its lines from `_get_available_move_lines`, which reports the availability of every upstream package without discounting what the move already reserved. https://github.com/odoo/odoo/blob/05af9e6877fd0f044bb46c983fe7300eb1db9307/addons/stock/models/stock_move.py#L1907-L1909 The package already reserved on the first line is therefore offered again and reused, collapsing both lines onto one package. The reserved-product branch below already subtracts the move's own reservation before allocating; doing the same here leaves each package on its own line. opw-6498532 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287935 Forward-Port-Of: odoo/odoo#284956
This fixes an internal database connection cleanup issue where active usage could delay routine checks indefinitely. It helps prevent idle database connections from staying open too long, improving resource management and stability under busy workloads.
Original PR description
Every `borrow()` used to push `_check_free_at` forward, so a busy pool never ran the periodic full idle scan. Oldest idle connections then stayed open until the pool filled. Reset the timer only when that full scan actually runs. I think it is the reason that we have to REV the code here https://github.com/odoo/odoo/pull/287008 for non-blocking connection pool
The salary configurator now shows only one Gross salary line for Indian payroll contracts. This prevents inflated monthly totals and gives HR teams a clearer, more accurate salary breakdown.
Original PR description
Issue: - The salary configurator displays two 'Gross' lines for Indian payroll structures. - The generic salary resume uses the `wage` code, while the Indian payroll defines a structure-specific…
Issue: - The salary configurator displays two 'Gross' lines for Indian payroll structures. - The generic salary resume uses the `wage` code, while the Indian payroll defines a structure-specific 'Gross' resume using the 'GROSS' payslip rule. Both resumes were included in the salary configurator, resulting in duplicate 'Gross' entries. - The 'Monthly Equivalent' total shown in the configurator was also affected, as it included the generic 'wage' amount on top of the actual payslip values. Cause: - The generic 'wage' resume was still included for the Indian payroll structure alongside the India-specific 'GROSS' payslip resume. - The generic 'wage' resume also counts towards the 'Monthly Equivalent' total shown in the configurator, so removing only the duplicate line left this total incorrect. Fix: - Exclude the generic 'wage' resume from the salary configurator results when the selected structure is the Indian employee payroll structure. - This keeps the India-specific 'Gross' resume based on the 'GROSS' payslip rule while preventing the generic contract wage from being displayed as a duplicate. - Subtract the removed 'wage' amount from the 'Monthly Equivalent' total so it stays correct after the duplicate line is removed. task-6511413
This fixes an issue where shortening an already approved time off request could remove its calendar blocking entry without recreating it. Payroll calculations now continue to treat the affected days as time off rather than attendance, helping ensure final payslips are accurate when employee departure dates shorten leave periods.
Original PR description
Problem ------- Fixes bug caused by PR odoo#249527. When a validated time off's dates are shortened while it stays validated (e.g. when the employee's departure date falls int he middle of the…
Problem ------- Fixes bug caused by PR odoo#249527. When a validated time off's dates are shortened while it stays validated (e.g. when the employee's departure date falls int he middle of the leave), the linked resource.calendar.leaves record was unconditionally unlinked. To reproduce: 1. Create and validate a time off request covering a whole month. 2. Register the employee's departure with a departure date in the middle of that time off. 3. Generate the employee's last payslip. The leave is correctly cut at the departure date, but since it never leaves the `validate` state, it never goes through `_validate_leave_request()` again, so its resource.calendar.leaves record is never recreated. The days that were covered by the deleted entry are no longer blocked in the employee's resource calendar, so the payslip's worked day lines (computed from resource.calendar.leaves) count them as attendance instead of time off. Cause ----- `hr.leave.write()` removed the resource.calendar.leaves record any time either the state changed away from `validate` or the leave's dates changed, regardless of whether the leave remained validated. Date-only changes on an already-validated leave never re-trigger validation, so the entry was not recreated. Solution -------- Only remove the resource.calendar.leaves record when the leave actually loses its validated state. When a validated leave's dates change but it stays validated, amend the existing resource.calendar.leaves record in place instead, falling back to creating one if none exists. Related PR: odoo#249527 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287703 Forward-Port-Of: odoo/odoo#286465
Backend refunds in Point of Sale now check that refunded quantities do not exceed the original order quantity. This aligns the backend refund process with the existing frontend behavior and helps prevent accidental over-refunds.
Original PR description
Currently, if you refund an order fro the backend it is possible to modify the qty as if to refund more than the original order qty. Steps to reproduce: ------------------- * Make an order from the…
Currently, if you refund an order fro the backend it is possible to modify the qty as if to refund more than the original order qty. Steps to reproduce: ------------------- * Make an order from the shop (1 product, qty 1) * Validate the order * Go backend * Find the order and select the refund button * Change qty from -1 to -3 * Save and continue the refund process > No problem refunding more than the original quantity Why the fix: ------------ In the frontend we cannot refund more than the original quantity, we assume the same should be in the backend process. The most simple way to do this is by doing a difference between the quantity from the original order and all the refund lines linked. From `self.refunded_orderline_id.refund_orderline_ids` we need to exclude the line that represents self as it holds the quantity before the onchange and we care about the quantity we're trying to write not the previous (allegedly correct). opw-6328635 Forward-Port-Of: odoo/odoo#285623 Forward-Port-Of: odoo/odoo#281405
Fixed an issue where orders shared between trusted Points of Sale could keep the wrong session information in restaurant setups. This prevents valid payments from being incorrectly rejected when shops use different payment methods.
Original PR description
**TL;DR** - in certain cases, Session is not updated on order shared on trusted pos Steps to reproduce: - Install pos_restaurant with demo data. - Have two configs: Cloth Shop and Furniture Shop. -…
**TL;DR** - in certain cases, Session is not updated on order shared on trusted pos Steps to reproduce: - Install pos_restaurant with demo data. - Have two configs: Cloth Shop and Furniture Shop. - Ensure both configs have their own individual payment method (e.g., CASH). - In Cloth Shop, add Furniture Shop as a trusted PoS. - In Cloth Shop, enable "Log in with Employee". - Open Cloth Shop and Furniture Shop in separate browsers (different users). - Refresh Cloth Shop once. - In Cloth Shop, create and save an order. - In Furniture Shop, open that order and try to pay with "CASH". Observation: * A validation error is raised: "The payment method selected is not allowed in the config of the POS session." <img width="320" height="201" alt="image" src="https://github.com/user-attachments/assets/8ca80575-76bf-4ecf-a739-08b03d543ed6" /> - although we can pay the order by a shared payment method Cause: - Refreshing Cloth Shop triggers `notify_synchronisation` due to `setCashierUpdateSession` , which pushes Cloth Shop's `pos.session` record into Furniture Shop's IndexedDB (because Cloth Shop trusts Furniture Shop). https://github.com/odoo/odoo/blob/6a3655c8efd2de68754ace38bd1ab5eb6499faca/addons/pos_hr/static/src/app/services/pos_store.js#L61-L63 - When Cloth Shop saves an order, sync also pushes a copy of that order into Furniture Shop, and passed through `processDynamicRecords` - `processDynamicRecords` ensures that the Record created is in sync with server data, as Furniture Shop now has a local record of session of Cloth Shop, the record keeps `session_id` pointing to Cloth Shop's session, else it would have been left `undefined` - from the `res` we get, we set the session_id on `pos.order ` https://github.com/odoo/odoo/blob/6a3655c8efd2de68754ace38bd1ab5eb6499faca/addons/point_of_sale/static/src/app/utils/devices_synchronisation.js#L127-L139 - However with pos_restaurant installed we get its orveride which stores the result of super() and only returns result early when there are no` pos.order ` records in dynamicRecords. When pos.order records are present (our case), the method proceeds with its own logic but never returns result (or its own output) at the end https://github.com/odoo/odoo/blob/6a3655c8efd2de68754ace38bd1ab5eb6499faca/addons/pos_restaurant/static/src/app/utils/devices_synchronisation.js#L5-L9 - so the corrected data from the super call is silently dropped. - we do not get a change to update the session id - The order keeps Cloth Shop's session ID - Furniture Shop's payment validation checks the order against its session's allowed payment methods, but since the session is still Cloth Shop's, the check fails for methods not shared between Cloth Shop and Furniture Shop (e.g., CASH). Why this is hidden in other cases: - without pos_restaurant, or if Furniture Shop had no prior knowledge of Cloth Shop's session (we made is possible by `Log in with Employee` feature, session_id would stay `undefined` and later get correctly set toFurniture Shop's session in PosOrder.setup(). https://github.com/odoo/odoo/blob/6a3655c8efd2de68754ace38bd1ab5eb6499faca/addons/point_of_sale/static/src/app/models/pos_order.js#L27-L30 or from here https://github.com/odoo/odoo/blob/6a3655c8efd2de68754ace38bd1ab5eb6499faca/addons/point_of_sale/static/src/app/utils/devices_synchronisation.js#L127-L139 - Both safety nets happen to be bypassed here. Fix: * Tighten the early return condition so that data is only discarded when the current configuration is not a restaurant configuration. * Also, A trusted PoS configuration cannot be a restaurant, so not returning later makes sense. opw-6465228 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287525 Forward-Port-Of: odoo/odoo#282962
General Ledger exports now include the same lines users see after applying a search filter. This prevents missing accounts in exported reports and improves consistency between the report view and downloaded files.
Original PR description
When applying a search filter in the General Ledger, the lines displayed in the UI differ from the ones exported. The discrepancy comes from the fact that the UI search bar filters lines using a simple "contains" logic on the displayed line name, while the backend export relies on the account model’s `_name_search` behavior, just as in the chart of accounts. For example, searching for "40" displays the accounts 400000, 400010, and 124000 but the last one (124000) is not is in the export results. task: 5917435 Forward-Port-Of: odoo/enterprise#131011 Forward-Port-Of: odoo/enterprise#107928
This fixes Spanish VeriFactu POS reporting so that when a past POS order is invoiced later, the cancellation sent to AEAT targets the original simplified receipt instead of the newly created full invoice. This helps prevent incorrect tax reporting records for affected Spanish point-of-sale transactions.
Original PR description
When creating an invoice for a previous POS order, the data sent to AEAT cancels the full invoice instead of the order's simplified invoice. Steps to reproduce: - Open a POS and make a sale without invoicing it; - In the POS, go to the Order tab; - Select the order and fully invoice it. Issue: The AEAT cancellation line added to the pos order actually cancels the full invoice just created [opw-6471927](https://www.odoo.com/odoo/project/49/tasks/6471927) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284277
This fix ensures Mexican electronic payment complements report tax amounts using the decimal precision required for the payment currency, such as two decimals for MXN. It prevents payment complements from being rejected by certification providers after stricter SAT validation, including cases involving foreign-currency documents.
Original PR description
The 'ImpuestosP' node of the payment complement (Pagos 2.0) is reported with 6 decimals while the SAT expects the amounts to be expressed with the number of decimals supported by the currency of the…
The 'ImpuestosP' node of the payment complement (Pagos 2.0) is reported with 6 decimals while the SAT expects the amounts to be expressed with the number of decimals supported by the currency of the payment ('MonedaP'), as published in the 'c_Moneda' catalog, for example 2 decimals for MXN.
Steps to reproduce:
- Have an MX Company setup with Quadrum as PAC.
- Create and sign a customer invoice in MXN with a 16% IVA tax.
- Register a full payment and send the payment complement to the PAC.
Issue:
Payment will be rejected
```
Code : CRPER654
Message : El importe del campo BaseP que corresponde a Traslado, no tiene la cantidad de decimales que soporta la moneda (MonedaP)
```
Analysis:
Quadrum recently aligned its validation on that rule and now rejects the document having fields with too much decimals and, currently, fields like BaseP, ImporteP are pinned to 6 decimals in the template.
Reporting them with the decimals of the payment currency is not enough when the payment settles a document expressed in another currency: those amounts are also compared with the ones of the related documents converted with 'EquivalenciaDR', and the PAC expects the values truncated or rounded
```
Code : CRP20274
Extra Info : Traslados: La sumatoria de ImporteDR es mayor al ImporteP 1.30.
Valores minimos permitidos: Truncado: 1.29 o redondeado: 1.3
```
opw-6561617
Forward-Port-Of: odoo/enterprise#131343
Forward-Port-Of: odoo/enterprise#131267This fixes French electronic invoicing setup so that, when PDP is activated, contacts use the required France FRCTC electronic address based on their SIREN. It prevents incorrect Peppol identifiers from remaining in place and helps ensure French e-invoices are addressed correctly.
Original PR description
**Steps to reproduce:** - Install Accounting and l10n_fr_dpd - Switch to a French company (e.g. FR Company) - In Accounting settings, activate electronic invoicing - Create a contact with a VAT - Make sure "Peppol ID" is set to "France VAT" - Enter a SIREN **Issue:** The "Peppol ID" is not recomputed to "France FRCTC Electronic Address" and the "Peppol endpoint" is not recomputed to the SIREN. When PDP is activated, only "France FRCTC Electronic Address" should be accepted as "Peppol ID". **Cause:** By default, when a valid Peppol ID is set for a country, it is never recomputed. As there is no override for the French localization, no specific behavior is applied when PDP is activated. opw-6468656 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Fixed an issue where Taiwan B2B e-invoices involving down payments could be rejected by ECPay or show tax amounts that differed from the booked invoice. The e-invoice now uses the tax values recorded on the invoice, improving successful submission and consistency for Taiwanese businesses.
Original PR description
Current behavior: -- Sending a B2B invoice that deducts a down payment is rejected by ECPay with "(item tax discrepancy exceeds 1 NT$)", so the invoice cannot be issued at all. When it is accepted,…
Current behavior: -- Sending a B2B invoice that deducts a down payment is rejected by ECPay with "(item tax discrepancy exceeds 1 NT$)", so the invoice cannot be issued at all. When it is accepted, the tax on the e-invoice can still differ from the tax the invoice books. Expected behavior: -- The invoice is accepted, and the tax reported on the e-invoice is the one the invoice booked. Steps to reproduce: -- - Set a company up in Taiwan (TWD) with the ECPay credentials filled in - Create a sale order of 190,630 for a customer with a VAT number - Invoice a 50% down payment through the down payment wizard, then a 30% one, and post both - Invoice the remainder and post it - Send the final invoice to ECPay Cause of the issue: -- _l10n_tw_edi_prepare_item_list rebuilt the tax from the raw amount of every line and rounded that total once. Both steps also disagree with the invoice. Negative lines resulting from downpayments seem to be more strictly checked on ECPay and taxes cannot be re-derived easily by ECPay's system. There is a also a relevant but slightly different issue where the invoice amount is calculated differently but the ECPay page will show a different value. e.g. the invoice rounds each computation key on its own: 50.00 - 16.50 = 33.50 rounds to 34 where the invoice books 50 - 17 = 33. No issue was detected for purely positive lines (a transaction with different products but no downpayment) Fix: -- Use the tax amount the line carries when it has one, and fall back to the raw amount otherwise, so the payload reports what the invoice booked. Send the tax of each line in the ItemTax field. Let ECPay derive its own per-item tax and checks it against the declared total for purely positive lines, and recalculate them for negative so the rounding difference between them is spread one unit at a time across the lines, the way ECPay distributes it. opw-6424237 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281928
The UAE payroll data update now restores required salary structure information in the correct order before updating payroll rules. This prevents scheduled payroll updates from failing when a company has deleted UAE payroll structure records, improving reliability for payroll administration.
Original PR description
Currently, an error occurs when the Payroll Update Data schedule action is run. **Steps to Reproduce:** - Install the `l10n_ae_hr_payroll` module without demo data. - Create a new company with the…
Currently, an error occurs when the Payroll Update Data schedule action is run. **Steps to Reproduce:** - Install the `l10n_ae_hr_payroll` module without demo data. - Create a new company with the country set to `United Arab Emirates`. - Switch to that company. - Go to `Payroll` > `Configuration` > `salary` > `Structures`. - Delete all salary structures related to the `United Arab Emirates`. - Go to `Scheduled Actions` and run `Payroll: Update Data`. `ValueError: External ID not found in the system: l10n_ae_hr_payroll.uae_employee_payroll_structure.` After this [recent commit], hr_rule_parameter_data and hr_salary_rule_data were added to _get_data_files_to_update. If the user deletes the salary structure and then updates the data files [1], an error is raised because the structure is missing. This commit ensures that the salary structure data is updated before updating the salary rule data. It also ensures that the salary structure type data is updated beforehand, as the structure depends on it and the user can delete the structure type data. [recent commit]: https://github.com/odoo/enterprise/commit/f3316e056da817ca31fecdc04e0f8df000f0f038 [1]- https://github.com/odoo/enterprise/blob/0a0b3a50a81df062ddee5c310ce06aaa4533a8a8/l10n_ae_hr_payroll/models/hr_payslip.py#L98-L105 sentry-7496380516
This fix improves copying and pasting list items that contain nested lists in the HTML editor. Users should no longer see extra nested content copied by mistake or lose the outer list structure when pasting selected list content.
Original PR description
Problem: Copying content from a list item containing a nested list can either include the unselected nested list or lose part of the copied content. Solution: - Detect whether the whole `<li>` was selected before copying the full list item with its nested lists. - When only part of the list item is selected, copy only the selected content and rebuild the required `<li>` wrapper. - Apply this logic only to list items with multiple top-level children. Steps to reproduce: - Add bullet list with nested list. - CTRL+A - Press Enter twice to create new paragraph. - Paste. - Observe the outer list is lost. task-6438347 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281205
This fix stops Odoo from automatically saving a form at the wrong moment when users are editing an embedded list row and open a mobile file picker. It prevents in-progress edits, such as adding additional resources in eLearning content, from being silently discarded.
Original PR description
The form view autosaves on 'visibilitychange' (e.g. when the user switches tab/app) to avoid losing unsaved changes. On mobile, opening the native file picker for a binary field also fires 'visibilitychange', which triggered this autosave. When that binary field was part of an editable x2many list, the autosave forced the row out of edition before the file could be selected, silently discarding the edition in progress. Skip the autosave when a x2many field of the root record currently has a row in edition. Can be reproduced in eLearning > course > content > Additional Resources, on mobile devices (must force "desktop mode" in the browser), and on tablets. opw~6517735 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287855 Forward-Port-Of: odoo/odoo#287581
Fixed an issue where opening the Project task Pivot view from a saved favorite could crash when tasks were grouped by personal stages. This keeps reporting views usable for users who rely on saved filters in My Tasks.
Original PR description
Since the introduction of `read_grouping_sets` for Pivot views, opening the Pivot view from a saved favorite filter can crash. ### **Steps to reproduce:** - Install Project. - Open My Tasks. - Save…
Since the introduction of `read_grouping_sets` for Pivot views, opening the Pivot view from a saved favorite filter can crash. ### **Steps to reproduce:** - Install Project. - Open My Tasks. - Save the current filter as a favorite. - Switch to the Pivot view. ### **Error:** ``` ValueError: Cannot convert project.task.personal_stage_id to SQL because it is not stored. ``` ### **Root Cause:** Since [commit](https://github.com/odoo/odoo/pull/194413/changes/166a546ec52784c413f5b5d7d29af89d618d6519), Pivot views use `_read_grouping_sets` instead of `_read_group`. `project.task` only remaps `personal_stage_type_id` to the stored `personal_stage_type_ids` in [_read_group](https://github.com/odoo/odoo/blob/5f6fb63d5d7585805642c702d096b2f882e73761/addons/project/models/project_task.py#L2169-L2179), so the remapping is bypassed for Pivot views. The ORM then attempts to group by the non-stored `personal_stage_type_id` relation, leading to the SQL conversion error. ### **Fix:** Mirror the remapping logic in `_read_grouping_sets` so Pivot views use `personal_stage_type_ids` before the ORM generates the SQL query. **opw-6306389** Forward-Port-Of: odoo/odoo#272985