Daily updates from Odoo
Wednesday, July 15, 2026
332 changes
22 changes
Resolved issues and error corrections
In main data service of this PoS indexedDB is called automatically after records are updated. ```js this.debouncedSynchronizeLocalDataInIndexedDB = debounce( this.synchronizeLocalDataInIndexedDB.bind(this), 300 ); ``` But sometimes, the indexedDB is directly called in the code via `synchronizeLocalDataInIndexedDB` which is not debounced and can lead to race conditions and potential data corruption. Now the `synchronizeLocalDataInIndexedDB` is inside a Mutex to avoid concurr
Original PR description
In main data service of this PoS indexedDB is called automatically after records are updated.
```js
this.debouncedSynchronizeLocalDataInIndexedDB = debounce(
this.synchronizeLocalDataInIndexedDB.bind(this),
300
);
```
But sometimes, the indexedDB is directly called in the code via `synchronizeLocalDataInIndexedDB` which is not debounced and can lead to race conditions and potential data corruption.
Now the `synchronizeLocalDataInIndexedDB` is inside a Mutex to avoid concurrent access to the indexedDB.
The old method is renamed to `_synchronizeLocalDataInIndexedDB` and is now private.
Forward-Port-Of: odoo/odoo#275892Steps to reproduce: - Open Chrome. - Set the browser zoom below or above 100%. - Edit a website page. - Hover a resize or padding handle in the overlay. => A white line appears in the middle of the handle. Before this commit, overlay handles changed their inner outline color on hover. With Chrome zoom levels different from 100%, this could leave a white line visible in the middle of the handle. After this commit, overlay handles change their background color on hover and use a consiste
Original PR description
Steps to reproduce: - Open Chrome. - Set the browser zoom below or above 100%. - Edit a website page. - Hover a resize or padding handle in the overlay. => A white line appears in the middle of the handle. Before this commit, overlay handles changed their inner outline color on hover. With Chrome zoom levels different from 100%, this could leave a white line visible in the middle of the handle. After this commit, overlay handles change their background color on hover and use a consistent inner outline width, so no white line is visible. task-6048647 Forward-Port-Of: odoo/odoo#273722
When a product attribute line is used in a confirmed sale order, Odoo archives it (active=False) instead of deleting it when removed from the product template. If the corresponding product.attribute record is also archived, settling that sale order in PoS crashes with: TypeError: Cannot read properties of undefined (reading 'create_variant') opw-6315766 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275273 Forward
Original PR description
When a product attribute line is used in a confirmed sale order, Odoo archives it (active=False) instead of deleting it when removed from the product template. If the corresponding product.attribute record is also archived, settling that sale order in PoS crashes with: TypeError: Cannot read properties of undefined (reading 'create_variant') opw-6315766 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275273 Forward-Port-Of: odoo/odoo#271778
Animated GIFs applied with an image shape freeze on Firefox and Safari when the shape's SVG is otherwise static. A dummy `<animateMotion dur="1ms" repeatCount="indefinite"/>` child on the <image> element keeps the animation running so the GIF plays. This hack was introduced in [1] but was missing from most shapes. This commit adds it to every <image> element that lacked it so any shape can be used with a GIF. task-5967171 [1]: https://github.com/odoo/odoo/commit/144e5ef799060da860a5fb1f
Original PR description
Animated GIFs applied with an image shape freeze on Firefox and Safari when the shape's SVG is otherwise static. A dummy `<animateMotion dur="1ms" repeatCount="indefinite"/>` child on the <image> element keeps the animation running so the GIF plays. This hack was introduced in [1] but was missing from most shapes. This commit adds it to every <image> element that lacked it so any shape can be used with a GIF. task-5967171 [1]: https://github.com/odoo/odoo/commit/144e5ef799060da860a5fb1fbe0fd009919afe79 Forward-Port-Of: odoo/odoo#275259
Problem: When posting expenses, if the expense domain is set as mandatory in any of the analytic plans, users can still submit expenses without entering an analytic distribution. This only happens when the expense is auto-approved on submission, which happens when the expense's employee is also the expense's manager or when the employee doesn't have an expense manager. Steps to reproduce: 1. Create an analytic plan with optional default applicability 2. Add an applicability rule with the d
Original PR description
Problem: When posting expenses, if the expense domain is set as mandatory in any of the analytic plans, users can still submit expenses without entering an analytic distribution. This only happens…
Problem: When posting expenses, if the expense domain is set as mandatory in any of the analytic plans, users can still submit expenses without entering an analytic distribution. This only happens when the expense is auto-approved on submission, which happens when the expense's employee is also the expense's manager or when the employee doesn't have an expense manager. Steps to reproduce: 1. Create an analytic plan with optional default applicability 2. Add an applicability rule with the domain set as Expense and set its applicability as mandatory. 3. Create a new expense 4. Set the expense's manager the same as the expense's employee. 5. Don't enter any analytic distribution. 6. Submit the expense 7. Notice how the expense is submitted and auto-approved without any error, even though no analytic distribution is entered and the analytic plan has a mandatory rule for expenses. Cause: The validation of the analytic distribution was only triggered on the approval of the expense, but when the expense is auto-approved on submission, the validation is not triggered at all. Solution: Move the validation of the analytic distribution to the do_approve method, which gets called both when an expense is approved and when it's auto-approved on submission. opw-6187340 Forward-Port-Of: odoo/odoo#270268
Currently, flexible weekly overtime deducts the raw leave interval duration. ## **Steps to reproduce:** - Install hr_holidays and hr_attendance - Create an employee with flex 40h/week working schedule. - Employee profile>setting>Default Ruleset>Employee schedule Rule and set `If the worked hours on a`: `week`. - Create a public holiday on Monday. - Record daily 8h from Tue to Sat (12 AM to 8 AM). ## **Observed Behavior:** Attendance List View computes "Worked Extra Hours" incorrectly
Original PR description
Currently, flexible weekly overtime deducts the raw leave interval duration. ## **Steps to reproduce:** - Install hr_holidays and hr_attendance - Create an employee with flex 40h/week working…
Currently, flexible weekly overtime deducts the raw leave interval duration. ## **Steps to reproduce:** - Install hr_holidays and hr_attendance - Create an employee with flex 40h/week working schedule. - Employee profile>setting>Default Ruleset>Employee schedule Rule and set `If the worked hours on a`: `week`. - Create a public holiday on Monday. - Record daily 8h from Tue to Sat (12 AM to 8 AM). ## **Observed Behavior:** Attendance List View computes "Worked Extra Hours" incorrectly as 20:30h ## **Expected Behavior:** "Worked Extra Hours" should be computed as 8h ## **Root Cause:** In [_get_daterange_overtime_undertime_intervals_for_quantity_rule](https://github.com/odoo/odoo/blob/53448e5445c8bcbf12126bf27bd675f4b9883d05/addons/hr_attendance/models/hr_attendance_overtime_rule.py#L290-L342), the system manually calculates overtime for flexible employees by deducting `schedule['leave']` durations from the expected working hours at [1]. However, for global public holidays, the system mishandles the timezone conversion within this schedule dictionary. Because public holiday intervals are stored and processed using UTC datetimes before being converted to the employee's local timezone, converting it to the employee's local timezone causes the holiday hours to shift and overlap into the next calendar day. As a result, the `schedule['leave']` calculation incorrectly thinks the employee had time off on normal working days, which throws off the final overtime amount. [1]: http://github.com/odoo/odoo/blob/53448e5445c8bcbf12126bf27bd675f4b9883d05/addons/hr_attendance/models/hr_attendance_overtime_rule.py#L304-L307 ## **Fix:** Replace the manual leave subtraction logic with the existing `_get_expected_hours_from_contract` method. This method naturally handles global public holidays and computes attendance intervals safely across different timezones without shifting hours into the wrong day. **opw-6259328,6284145** Forward-Port-Of: odoo/odoo#274831 Forward-Port-Of: odoo/odoo#269293
Modified `SampleServer._mockWebReadGroup` to intercept `groupby_read_specification`. It now dynamically fetches the requested related fields using `_mockWebSearchReadUnity` and safely injects them into the `__values` payload for each mock group, perfectly mirroring the standard ORM behavior. Task: [6307582](https://www.odoo.com/odoo/project/133/tasks/6307582) Forward-Port-Of: odoo/odoo#272135
Original PR description
Modified `SampleServer._mockWebReadGroup` to intercept `groupby_read_specification`. It now dynamically fetches the requested related fields using `_mockWebSearchReadUnity` and safely injects them into the `__values` payload for each mock group, perfectly mirroring the standard ORM behavior. Task: [6307582](https://www.odoo.com/odoo/project/133/tasks/6307582) Forward-Port-Of: odoo/odoo#272135
**Steps to reproduce:** - Create a product A, tracked by lots - Create a kit product, include a component A - Change the UoM to 0.5 - Go to the PoS, order this kit product - Also order the component A, with a quantity of 2 - Pay for it, ask for an invoice - Go to the created picking - The Demand column is correctly computed and is 0.5 - The Quantity column is wrong and is 2 **Why the fix:** When getting the data from https://github.com/odoo/odoo/blob/e0d84c7fbb270d0d1f82572daefa
Original PR description
**Steps to reproduce:** - Create a product A, tracked by lots - Create a kit product, include a component A - Change the UoM to 0.5 - Go to the PoS, order this kit product - Also order the component…
**Steps to reproduce:** - Create a product A, tracked by lots - Create a kit product, include a component A - Change the UoM to 0.5 - Go to the PoS, order this kit product - Also order the component A, with a quantity of 2 - Pay for it, ask for an invoice - Go to the created picking - The Demand column is correctly computed and is 0.5 - The Quantity column is wrong and is 2 **Why the fix:** When getting the data from https://github.com/odoo/odoo/blob/e0d84c7fbb270d0d1f82572daefa96c2978d3785/addons/point_of_sale/models/stock_picking.py#L283 we always get the component's line, as the move's product is the component, even if it used to be the kit product's move. This is because when exploding a kit's moves, it gets the kit's component as a product instead of keeping the kit product. This was introducing a weird behavior because we took the quantity from the component line, and not from the kit line, meaning the kit would always have the same quantity as the component. We now check if the move is actually a kit product's move, and if it is we adapt the qty to correct one by fetching the correct line's qty, and adapting it with the correct UoM. Changing the line in itself would not work, as the kit itself is not tracked by lots, so we would not enter https://github.com/odoo/odoo/blob/e0d84c7fbb270d0d1f82572daefa96c2978d3785/addons/point_of_sale/models/stock_picking.py#L284 and the move line would not be correctly created. opw-6153000 Forward-Port-Of: odoo/odoo#276098 Forward-Port-Of: odoo/odoo#262551
**Issue** The priority of a purchase order is not propagated to the corresponding picking **Steps to reproduce** - Create a PO for a product - Click on the star (set priority) - Confirm the PO and open the corresponding picking -> The priority is not set on the picking **Cause** The feature was introduced in 19.2 and worked through `_prepare_picking()` (4eb64d15d10a258e2902818304c29681fe268553): https://github.com/odoo/odoo/blob/4eb64d15d10a258e2902818304c29681fe268553/addons/purcha
Original PR description
**Issue** The priority of a purchase order is not propagated to the corresponding picking **Steps to reproduce** - Create a PO for a product - Click on the star (set priority) - Confirm the PO and…
**Issue** The priority of a purchase order is not propagated to the corresponding picking **Steps to reproduce** - Create a PO for a product - Click on the star (set priority) - Confirm the PO and open the corresponding picking -> The priority is not set on the picking **Cause** The feature was introduced in 19.2 and worked through `_prepare_picking()` (4eb64d15d10a258e2902818304c29681fe268553): https://github.com/odoo/odoo/blob/4eb64d15d10a258e2902818304c29681fe268553/addons/purchase_stock/models/purchase_order.py#L401 However a refactor in 19.3 removed that method without preserving the priority propagation (a1fb39eb6ae0d1abc12ab2aaf876b10baed4d7cd). Instead of using `_prepare_picking`, it creates the moves and confirm it: https://github.com/odoo/odoo/blob/a1fb39eb6ae0d1abc12ab2aaf876b10baed4d7cd/addons/purchase_stock/models/purchase_order.py#L387-L388 which will create the picking (if needed): https://github.com/odoo/odoo/blob/9221edd716ac241a301fa25d511fde0edb17b3f4/addons/stock/models/stock_move.py#L1449 https://github.com/odoo/odoo/blob/9221edd716ac241a301fa25d511fde0edb17b3f4/addons/stock/models/stock_move.py#L1476 opw-6275965
Before this commit When a website user had an active cart, opening the pickup location selector from a backend sale order could use the cart's delivery method instead of the one configured on the sale order. Steps to reproduce: 0. Switch to the debug mode 1. Configure 2 delivery methods (A and B) with pickup locations 2. On eCommerce add storable products to the cart and choose the delivery method A 3. In the backend, create a sale order and set the delivery method B 4. Try to set picku
Original PR description
Before this commit When a website user had an active cart, opening the pickup location selector from a backend sale order could use the cart's delivery method instead of the one configured on the sale order. Steps to reproduce: 0. Switch to the debug mode 1. Configure 2 delivery methods (A and B) with pickup locations 2. On eCommerce add storable products to the cart and choose the delivery method A 3. In the backend, create a sale order and set the delivery method B 4. Try to set pickup location and see the traceback This is caused by wrong location selector props validation and by the wrong locations fetching. This commit fixes the props of location selector to match the given ones and get the correct locations for the given delivery method. opw-6267741 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
#### Issue: Opening the MRP planning view could raise a traceback when a maintenance request had a `schedule_end` but no `schedule_date`. `TypeError: '<' not supported between instances of 'NoneType' and 'datetime.datetime'` #### Steps to reproduce: 1. Install maintenance, mrp, and mrp_maintenance. 2. Create a work center. 3. Create a maintenance request linked to that work center. 4. set a `Scheduled end` and Leave `Scheduled Date` empty. 5. Go to MRP > Planning > Work Orders. #
Original PR description
#### Issue: Opening the MRP planning view could raise a traceback when a maintenance request had a `schedule_end` but no `schedule_date`. `TypeError: '<' not supported between instances of 'NoneType'…
#### Issue: Opening the MRP planning view could raise a traceback when a maintenance request had a `schedule_end` but no `schedule_date`. `TypeError: '<' not supported between instances of 'NoneType' and 'datetime.datetime'` #### Steps to reproduce: 1. Install maintenance, mrp, and mrp_maintenance. 2. Create a work center. 3. Create a maintenance request linked to that work center. 4. set a `Scheduled end` and Leave `Scheduled Date` empty. 5. Go to MRP > Planning > Work Orders. #### Cause: `maintenance.request` stores `schedule_end` as a writable field, but no constraint enforces that `schedule_date` and `schedule_end` must be set together. Later, `mrp_maintenance` in `_get_maintenances_intervals` fetches maintenance intervals for the gantt view without filtering null bounds. If a request has `(schedule_date, schedule_end)` = `(False, datetime)`, that interval is passed to `Intervals(...)`, which crashes when comparing `None` with a `datetime`. #### Fix: Add a constraint on `maintenance.request` to require `schedule_date` and `schedule_end` to either both be set or both be empty. Also filter out incomplete intervals in the MRP maintenance gantt query in this enterprise PR: https://github.com/odoo/enterprise/pull/117710 opw-6225772 enterprise PR: https://github.com/odoo/enterprise/pull/117710 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#265208
Since #198829, a `t-att-class` has been added to the `loadOlder` template that relies on `mountedAndLoaded` of thread state to control the visibility of the `Load More` button. But it doesn't read the value from the state properly. As a result, the button is always transparent. This change fixes this by reading the value from the state. Forward-Port-Of: odoo/odoo#275934 Forward-Port-Of: odoo/odoo#275253
Original PR description
Since #198829, a `t-att-class` has been added to the `loadOlder` template that relies on `mountedAndLoaded` of thread state to control the visibility of the `Load More` button. But it doesn't read the value from the state properly. As a result, the button is always transparent. This change fixes this by reading the value from the state. Forward-Port-Of: odoo/odoo#275934 Forward-Port-Of: odoo/odoo#275253
…nding When cash rounding is enabled with "Only for cash payment methods", an order partially paid in cash and completed with an online payment could neither request the correct online amount nor be marked as paid. Steps to reproduce: - Enable cash rounding (e.g. 0.05, HALF-UP) with "Only for cash payment methods" - Create an order with a total of 15.28 - Add a cash payment of 10.00, then an online payment for the remainder The frontend requests 5.28 for the online payment, but as so
Original PR description
…nding When cash rounding is enabled with "Only for cash payment methods", an order partially paid in cash and completed with an online payment could neither request the correct online amount nor be…
…nding When cash rounding is enabled with "Only for cash payment methods", an order partially paid in cash and completed with an online payment could neither request the correct online amount nor be marked as paid. Steps to reproduce: - Enable cash rounding (e.g. 0.05, HALF-UP) with "Only for cash payment methods" - Create an order with a total of 15.28 - Add a cash payment of 10.00, then an online payment for the remainder The frontend requests 5.28 for the online payment, but as soon as the order contained a cash payment the server rounded the whole order total: get_and_set_online_payments_data() returned an unpaid amount of 5.30 (15.30 - 10.00), so the validation failed with "Invalid online payments". Even once the online payment of 5.28 was processed, the order remained stuck in draft with the money captured: _is_pos_order_paid() compared the paid amount (15.28) against the rounded total (15.30). Only the part of the order actually settled in cash must be rounded: non-cash payments (card, online, ...) always pay their exact share. - get_amount_unpaid() now returns the exact residual of the order when the rounding only applies to cash payment methods. - _get_rounded_amount() now only rounds the amount not covered by non-cash payments, resolving its old TODO. Cash-only orders and orders where the cash payment settles the rounded remainder are unaffected. opw-6314690 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275472 Forward-Port-Of: odoo/odoo#275305
When an order is validated, the state is set to "paid" and a sync to the server is attempted. If the network dropped during that sync, the order could be permanently lost: the 300ms IndexedDB debounce had not yet fired, so the paid order lived only in memory, and no guard prevented the cashier from accidentally closing or refreshing the tab in that window. opw-6237823 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#27515
Original PR description
When an order is validated, the state is set to "paid" and a sync to the server is attempted. If the network dropped during that sync, the order could be permanently lost: the 300ms IndexedDB debounce had not yet fired, so the paid order lived only in memory, and no guard prevented the cashier from accidentally closing or refreshing the tab in that window. opw-6237823 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275159 Forward-Port-Of: odoo/odoo#267799
Description of the issue/feature this PR addresses: When reversing or replacing an invoice with a C-symbol (tax code requiring approval), the reverse_moves method used an undefined `_l10n_vn_edi_lookup_invoice`, which is an old artifact from 19.2. This commit replaces it by a call to the SInvoiceService `lookup_invoice`, following the same pattern used throughout the rest of the module. Steps to reproduce: 1- Install `l10n_vn_edi_viettel` 2- Use credentials to put in Account settings
Original PR description
Description of the issue/feature this PR addresses: When reversing or replacing an invoice with a C-symbol (tax code requiring approval), the reverse_moves method used an undefined…
Description of the issue/feature this PR addresses: When reversing or replacing an invoice with a C-symbol (tax code requiring approval), the reverse_moves method used an undefined `_l10n_vn_edi_lookup_invoice`, which is an old artifact from 19.2. This commit replaces it by a call to the SInvoiceService `lookup_invoice`, following the same pattern used throughout the rest of the module. Steps to reproduce: 1- Install `l10n_vn_edi_viettel` 2- Use credentials to put in Account settings 3- Fill the TIN under res.company 4- Go to symbols under configuration, fetch and select a symbol with a 'C' in the name 5- Create invoice, use symbol for VN e-invoice. Click Send 6- Create Credit note, click confirm on wizard Current behavior before PR: Validating the Credit Note would raise a Traceback. ``` AttributeError: 'account.move' object has no attribute '_l10n_vn_edi_lookup_invoice' ``` Desired behavior after PR is merged: Validating the Credit Note should process as expected, without errors. opw-6348241 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
With 8f25ed0bf363, the case of a comodel with active_test set was handled differently for the False value. Align to the previous behaviour. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
With 8f25ed0bf363, the case of a comodel with active_test set was handled differently for the False value. Align to the previous behaviour. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
When registering a payment for multiple bills from different partners, the `source_currency_id` becomes empty/evaluates differently in the wizard. Because of this, the `currency_conversion_div` `invisible` condition failed, causing the UI to redundantly display the exchange rate even when the currencies were identical (e.g., showing '1 EUR = 1 EUR'). This commit simplifies the invisible condition to only check if the `currency_id` matches the `company_currency_id`, keeping the UI clean. se
Original PR description
When registering a payment for multiple bills from different partners, the `source_currency_id` becomes empty/evaluates differently in the wizard. Because of this, the `currency_conversion_div` `invisible` condition failed, causing the UI to redundantly display the exchange rate even when the currencies were identical (e.g., showing '1 EUR = 1 EUR'). This commit simplifies the invisible condition to only check if the `currency_id` matches the `company_currency_id`, keeping the UI clean. see ent pr- https://github.com/odoo/enterprise/pull/118183 task- 6237870
**Steps to reproduce:** 1. Install Sales and EasyPost modules and enable delivery methods in the Settings. 2. Configure a new delivery method in [Sales -> Configuration -> Delivery Methods], choose provider as EasyPost and set the API keys. 3. Set any margin % on the delivery method. 4. Add the shipping line to a sale order via "Add shipping". **Issue:** Unit Price displays at a 4dp precision while Subtotal correctly displays at a 2dp precision **Why this happens:** `_apply_margins(
Original PR description
**Steps to reproduce:** 1. Install Sales and EasyPost modules and enable delivery methods in the Settings. 2. Configure a new delivery method in [Sales -> Configuration -> Delivery Methods], choose provider as EasyPost and set the API keys. 3. Set any margin % on the delivery method. 4. Add the shipping line to a sale order via "Add shipping". **Issue:** Unit Price displays at a 4dp precision while Subtotal correctly displays at a 2dp precision **Why this happens:** `_apply_margins()` returns a float from margin arithmetic calculation with no currency rounding applied. `rate_shipment()` writes this unrounded value directly to res['price'], which becomes the delivery line's price_unit. opw-6355318 Forward-Port-Of: odoo/odoo#275233
Typing in an HTML field (e.g. a contact's Internal Notes) and validating a URL-like token with Enter or Space can crash the editor with "IndexSizeError: The index is not in the allowed range", leaving the user unable to continue typing. It happens on Safari (not Chromium). The trigger is a URL-like token that the editor auto-converts into a link. The splitText calls in prepareConvertToLink, run during beforeinput, leave Safari's native selection anchored on an empty text node with an out-of-ran
Original PR description
Typing in an HTML field (e.g. a contact's Internal Notes) and validating a URL-like token with Enter or Space can crash the editor with "IndexSizeError: The index is not in the allowed range",…
Typing in an HTML field (e.g. a contact's Internal Notes) and validating
a URL-like token with Enter or Space can crash the editor with
"IndexSizeError: The index is not in the allowed range", leaving the
user unable to continue typing. It happens on Safari (not Chromium).
The trigger is a URL-like token that the editor auto-converts into a
link. The splitText calls in prepareConvertToLink, run during
beforeinput, leave Safari's native selection anchored on an empty text
node with an out-of-range offset. Anything reading the selection
afterwards then works from a broken position: on Enter, splitBlock
reads it and makeActiveSelection ends up throwing in Range.setStart;
on Space, the browser inserts the character in the wrong node and the
selection is corrupted the same way.
```
UncaughtClientError > IndexSizeError
Uncaught Javascript Error > The index is not in the allowed range.
setStart@[native code]
createEditorSelection@.../web.assets_web.min.js:12239:15
getSelectionData@.../web.assets_web.min.js:12242:145
updateActiveSelection@.../web.assets_web.min.js:12230:92
@.../web.assets_web.min.js:12218:873
handler@.../web.assets_web.min.js:14366:121
```
Steps to reproduce:
1. Use Safari (Chromium-based browsers work fine)
2. Open any record with an HTML field (e.g. Contacts -> a contact ->
Internal Notes).
3. Type a URL-like token such as KF.16D2.0204.CG (.CG is a valid TLD,
so the editor auto-links it). Do not paste it.
4. Place the caret at the end of that token and press Enter or Space.
5. IndexSizeError is raised and the editor stops accepting input.
Fix it at the source: re-anchor the selection right after the splits in
prepareConvertToLink, so every consumer sees a valid caret position.
Since moving the selection during beforeinput makes WebKit cancel the
pending text insertion, the Space case now prevents the default and
performs the conversion, the space insertion and the caret placement
itself, in two history steps so that undo still reverts the link
conversion while keeping the typed space.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#275587
Forward-Port-Of: odoo/odoo#270910When multiple invoices were sent to MyInvois in a single batch and at least one document failed validation, the failure branch of _myinvois_submit_documents added self.invoice_ids (all invoices in the batch) to invoice_to_cancel instead of the current record's invoices. Every sibling in the batch was then cancelled locally, even those whose own MyInvois submission had been accepted and moved to in_progress. The account.move ended up in state 'cancel' while its myinvois.document stayed 'valid'
Original PR description
When multiple invoices were sent to MyInvois in a single batch and at least one document failed validation, the failure branch of _myinvois_submit_documents added self.invoice_ids (all invoices in the batch) to invoice_to_cancel instead of the current record's invoices. Every sibling in the batch was then cancelled locally, even those whose own MyInvois submission had been accepted and moved to in_progress. The account.move ended up in state 'cancel' while its myinvois.document stayed 'valid', which violates the intended synchronization between the two records and blocked users from posting the credit note. Scope the cancellation to record.invoice_ids so only the invoice tied to the failing document is cancelled. 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#274727
CertificateAdapter presents a client certificate stored in the database instead of on disk when opening an HTTPS connection (used by the l10n_es EDI modules verifactu, sii and tbai). It loaded that certificate on the connection path of requests 2.31, but requests >= 2.32 changed that path (no longer calls get_connection()), so the step was skipped and the call crashed with: "TypeError: expected str, bytes or os.PathLike object, not certificate" Odoo pins requests 2.31.0 (max depending on Pyth
Original PR description
CertificateAdapter presents a client certificate stored in the database instead of on disk when opening an HTTPS connection (used by the l10n_es EDI modules verifactu, sii and tbai). It loaded that…
CertificateAdapter presents a client certificate stored in the database instead of on disk when opening an HTTPS connection (used by the l10n_es EDI modules verifactu, sii and tbai). It loaded that certificate on the connection path of requests 2.31, but requests >= 2.32 changed that path (no longer calls get_connection()), so the step was skipped and the call crashed with: "TypeError: expected str, bytes or os.PathLike object, not certificate" Odoo pins requests 2.31.0 (max depending on Python version), but online databases can use the version shipped by the OS (2.32.x on recent Ubuntu 26). Set the certificate up when the adapter is created instead of on that connection call. That step runs the same on every requests version, so the fix works both before and after 2.32. Steps to reproduce: - Spanish company with Veri*Factu and a certificate, on a server running requests >= 2.32 (saas-19.3 database for exemple on ubuntu 26) - Post a customer invoice and send it to Veri*Factu. => TypeError Reference: https://github.com/psf/requests/blob/f361ead047be5cb873174218582f7d8b9fcd9f49/HISTORY.md?plain=1#L146 Ticket [link](https://www.odoo.com/odoo/project.task/6366028) opw-6366028 Forward-Port-Of: odoo/odoo#275324
Miscellaneous changes
Commit [1] introduces the concept of "global" cache for optimization. However when loading a bundle inside a secondary document such as an iframe, containing source files that are already loaded in the main document, those source files are not properly added in the secondary document. Example: load `web.assets_web` inside an iframe (this use case can happen in `mass_mailing` where we have to wrap some component inside a sandboxed iframe in order to display unsafe content (poorly sanitize
Original PR description
Commit [1] introduces the concept of "global" cache for optimization. However when loading a bundle inside a secondary document such as an iframe, containing source files that are already loaded in…
Commit [1] introduces the concept of "global" cache for optimization. However when loading a bundle inside a secondary document such as an iframe, containing source files that are already loaded in the main document, those source files are not properly added in the secondary document. Example: load `web.assets_web` inside an iframe (this use case can happen in `mass_mailing` where we have to wrap some component inside a sandboxed iframe in order to display unsafe content (poorly sanitized), such as emails). The issue is caused by an inconsistent usage of the `globalCache`: `getBundle` adds `bundleName` key to JS and CSS libs, but `computeAssetCaches` adds `url` keys to link or script elements. After this commit: - `globalCache` is only used in `getBundle` to map bundleNames to their libs - `loadCSS` and `loadJS` only use the documents caches to map urls to their element in the respective document. [1]: https://github.com/odoo/odoo/commit/5b1dc282b0f09c0fe6dcf9910a3a29cfd01d66a3 Forward-Port-Of: odoo/odoo#276239
24 changes
Enhancements to existing features
LEGAL REQUIREMENTS - As of January 2026, the 9% VAT will increase to 12%. PURPOSE - For each 9% VAT, add 12% VAT with the same tax tag and descriptions, so in the VAT report, it's put under the same lines. - And add the missing taxes from the sheet provided in the task description. Related PR: https://github.com/odoo/enterprise/pull/101773 Task-5269617 Forward-Port-Of: odoo/odoo#239388
Original PR description
LEGAL REQUIREMENTS - As of January 2026, the 9% VAT will increase to 12%. PURPOSE - For each 9% VAT, add 12% VAT with the same tax tag and descriptions, so in the VAT report, it's put under the same lines. - And add the missing taxes from the sheet provided in the task description. Related PR: https://github.com/odoo/enterprise/pull/101773 Task-5269617 Forward-Port-Of: odoo/odoo#239388
Resolved issues and error corrections
The reporting labels "Difference" and "Balance" are confusing because "Difference" tracks system-qualified overtime while "Balance" represents accepted overtime hours. There is also a lack of consistency across views. This commit renames these fields to "Worked Extra Hours" and "Validated Extra Hours" to harmonize the naming everywhere task-6352142 Description of the issue/feature this PR addresses: Confusing and inconsistent naming for extra hours Current behavior before PR: - Rep
Original PR description
The reporting labels "Difference" and "Balance" are confusing because "Difference" tracks system-qualified overtime while "Balance" represents accepted overtime hours. There is also a lack of consistency across views. This commit renames these fields to "Worked Extra Hours" and "Validated Extra Hours" to harmonize the naming everywhere task-6352142 Description of the issue/feature this PR addresses: Confusing and inconsistent naming for extra hours Current behavior before PR: - Reporting uses "Difference" and "Balance". - Views use inconsistent labels. Desired behavior after PR is merged: Labels are consistently named "Worked Extra Hours" and "Validated Extra Hours" everywhere. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275944 Forward-Port-Of: odoo/odoo#273631
### Steps to reproduce 1. Create a invoice with a section and add products under it with values 2. Enable **Hide Composition** on the section. 3. Print the invoice PDF. <table> <tr> <td> <img width="1278" height="425" alt="image" src="https://github.com/user-attachments/assets/969a222a-5e2f-48e2-962d-fc1cf6440619" /> </td> </tr> </table> ### Description When an invoice contains a section and products in it with values and with **Hide Composition** enabled,
Original PR description
### Steps to reproduce 1. Create a invoice with a section and add products under it with values 2. Enable **Hide Composition** on the section. 3. Print the invoice PDF. <table> <tr> <td> <img…
### Steps to reproduce
1. Create a invoice with a section and add products under it with values
2. Enable **Hide Composition** on the section.
3. Print the invoice PDF.
<table>
<tr>
<td>
<img width="1278" height="425" alt="image" src="https://github.com/user-attachments/assets/969a222a-5e2f-48e2-962d-fc1cf6440619" />
</td>
</tr>
</table>
### Description
When an invoice contains a section and products in it with values and with **Hide Composition** enabled, the PDF invoice report incorrectly displays the **Disc.%** column header even though no discount values in that section line.
The report currently computes `display_discount` using `o.invoice_line_ids`:
```xml
<t t-set="display_discount" t-value="any(l.discount for l in o.invoice_line_ids)"/>
```
Since `o.invoice_line_ids` still contains the hidden product lines, `display_discount` evaluates to `True`, causing the **Disc.%** column header to be displayed. However, those product lines are replaced by the section line in the report, so no discount values are shown, resulting in an empty column.
### Current behavior
The **Disc.%** column is displayed, but all its cells are empty.
<table>
<tr>
<td>
<img width="808" height="488" alt="image" src="https://github.com/user-attachments/assets/7d9afee6-fef5-49c8-bc4e-b01caa8b43bd" />
</td>
</tr>
</table>
### Expected behavior
The **Disc.%** column should not be displayed when the reported lines do not contain any discounts.
<table>
<tr>
<td>
<img width="798" height="427" alt="image" src="https://github.com/user-attachments/assets/6ffe7985-a7d0-43f5-8d40-41e700ecbed3" />
</td>
</tr>
</table>
### Solution
Compute `lines_to_report` before evaluating `display_discount` and use it instead:
```xml
<t t-set="lines_to_report" t-value="o._get_move_lines_to_report()"/>
<t t-set="display_discount" t-value="any(l.discount for l in lines_to_report)"/>
```
Forward-Port-Of: odoo/odoo#276003
Forward-Port-Of: odoo/odoo#275793Steps to produce: --- - Install the `sales` module. - Create a product and `enable track inventory.` - Log in as user with only view access rights in products and also have the sales access rights. - Create a sale order containing product and confirm it. Issue: --- - An access error is raised during order confirmation. Root cause: --- - In [commit], to handle inventory tracking, the `qty_available` field was moved to `product.product`. Unlike before, this value is increased or
Original PR description
Steps to produce: --- - Install the `sales` module. - Create a product and `enable track inventory.` - Log in as user with only view access rights in products and also have the sales access rights. -…
Steps to produce: --- - Install the `sales` module. - Create a product and `enable track inventory.` - Log in as user with only view access rights in products and also have the sales access rights. - Create a sale order containing product and confirm it. Issue: --- - An access error is raised during order confirmation. Root cause: --- - In [commit], to handle inventory tracking, the `qty_available` field was moved to `product.product`. Unlike before, this value is increased or decreased depending on the operation performed. - As a consequence, creating or updating a sale order triggers a write to this `qty_available` field on the related product. This write happens under the current user's permissions, so users who only have read access to products (but can create/edit sale orders) hit an `AccessError`, since they lack write access on `product.product`. Solution: --- - Use `sudo()` when accessing the required product quantity information to ensure the operation can be completed without requiring additional product access rights. The same issue also occurs when confirming a purchase order. [commit]: https://github.com/odoo/odoo/commit/ca96992919b11105da44238c3e522f8eec4a740b opw-6290608 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270067
opw-6368979 Description of the issue/feature this PR addresses: Update the Worldline Cofidis payment method mapping to match the latest payment product ID defined in the Worldline documentation. Current behavior before PR: The Cofidis payment method was mapped to the outdated payment product ID (3012), causing payment requests to use an incorrect mapping. Desired behavior after PR is merged: The Cofidis payment method is mapped to the correct payment product ID (5129) as per
Original PR description
opw-6368979 Description of the issue/feature this PR addresses: Update the Worldline Cofidis payment method mapping to match the latest payment product ID defined in the Worldline documentation. Current behavior before PR: The Cofidis payment method was mapped to the outdated payment product ID (3012), causing payment requests to use an incorrect mapping. Desired behavior after PR is merged: The Cofidis payment method is mapped to the correct payment product ID (5129) as per the latest Worldline documentation, ensuring payment requests use the correct mapping. Forward-Port-Of: odoo/odoo#275881
Backport the changes from `b9370ea6b70ca3020c73a6940d70ff0cf954f69f` into `mail/convert_inline` to ensure Outlook-compatible image rendering. opw-3776054 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276071 Forward-Port-Of: odoo/odoo#269436
Original PR description
Backport the changes from `b9370ea6b70ca3020c73a6940d70ff0cf954f69f` into `mail/convert_inline` to ensure Outlook-compatible image rendering. opw-3776054 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276071 Forward-Port-Of: odoo/odoo#269436
### Before this PR When importing a FatturaPA XML, Odoo sets the fiscal position on the bill from the partner but does not apply it to the line taxes so a fiscal position that remaps taxes (partial deductibility, reverse charge, split payment) never map the imported lines. ### After this PR the fiscal position is correctly applied ### To reproduce 1. Apply to Italian vendor a fiscal position that maps the 22% purchase tax to a partial-deductibility tax (e.g. "22%" →"22% ind. 50%").
Original PR description
### Before this PR When importing a FatturaPA XML, Odoo sets the fiscal position on the bill from the partner but does not apply it to the line taxes so a fiscal position that remaps taxes (partial deductibility, reverse charge, split payment) never map the imported lines. ### After this PR the fiscal position is correctly applied ### To reproduce 1. Apply to Italian vendor a fiscal position that maps the 22% purchase tax to a partial-deductibility tax (e.g. "22%" →"22% ind. 50%"). 2. Import a FatturaPA XML from that vendor with 22% lines. 3. The bill header shows the fiscal position, but the lines keep the plain 22% tax instead of the mapped one. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275614 Forward-Port-Of: odoo/odoo#274738
**Steps to reproduce:** - Create a dynamic variant with 2 values, A and B - B should have an extra price of 200 - Create a product which has those two variants, with a price of 1000 - Set up a barcode for the product with the B variant only - Go to the PoS, enter the barcode for the product with the B variant - The price is 1400 instead of 1200 **Why the fix:** The extra price for dynamic variants ordered through the barcode will be counted twice. This is because it is first count
Original PR description
**Steps to reproduce:** - Create a dynamic variant with 2 values, A and B - B should have an extra price of 200 - Create a product which has those two variants, with a price of 1000 - Set up a…
**Steps to reproduce:** - Create a dynamic variant with 2 values, A and B - B should have an extra price of 200 - Create a product which has those two variants, with a price of 1000 - Set up a barcode for the product with the B variant only - Go to the PoS, enter the barcode for the product with the B variant - The price is 1400 instead of 1200 **Why the fix:** The extra price for dynamic variants ordered through the barcode will be counted twice. This is because it is first counted in the _scan(code) method when we fetch the product from the models, then counted again when adding the line to the current order. https://github.com/odoo/odoo/blob/f4d079cc5a9c47672cf1a6747bb073e8e74f7350/addons/point_of_sale/static/src/app/services/pos_store.js#L1264-L1269 This step is necessary for the usual flow without the barcode as we need to add this extra price, but when using the barcode, the list price of the product we fetch is already 1200, as the extra price is already included when fetching it from the backend. It works for always attributes because we explicitly check that we are not adding the extra price again in the above code, and that the list price already includes the extra price. It also works for the never attributes because values.product_id.product_template_variant_value_ids.length is 0, so the code to update the extra price is never triggered. As we still need to add the extra price for the usual flow, we now just check if we have a code, meaning we added the product through the barcode and that we do not need to add it again, as the list price already accounts for the extra price. opw-6328600 Forward-Port-Of: odoo/odoo#272395
In main data service of this PoS indexedDB is called automatically after records are updated. ```js this.debouncedSynchronizeLocalDataInIndexedDB = debounce( this.synchronizeLocalDataInIndexedDB.bind(this), 300 ); ``` But sometimes, the indexedDB is directly called in the code via `synchronizeLocalDataInIndexedDB` which is not debounced and can lead to race conditions and potential data corruption. Now the `synchronizeLocalDataInIndexedDB` is inside a Mutex to avoid concurr
Original PR description
In main data service of this PoS indexedDB is called automatically after records are updated.
```js
this.debouncedSynchronizeLocalDataInIndexedDB = debounce(
this.synchronizeLocalDataInIndexedDB.bind(this),
300
);
```
But sometimes, the indexedDB is directly called in the code via `synchronizeLocalDataInIndexedDB` which is not debounced and can lead to race conditions and potential data corruption.
Now the `synchronizeLocalDataInIndexedDB` is inside a Mutex to avoid concurrent access to the indexedDB.
The old method is renamed to `_synchronizeLocalDataInIndexedDB` and is now private.
Forward-Port-Of: odoo/odoo#275892[FIX] fleet: fix vendor bill vehicle association bug Bug reprod: Go to 19.2 or above 1 - Go to vendor bills. 2 - Create an invoice line add vehicle. 3 - Click to vehicle via the link 4 - You will see fleet model and try to press to some smart buttons, traceback will occur (Odometer, Services...) Bug cause: 1 - When we press to smart buttons for Odometer or Services we are going to return_action_to_open function. 2 - In this function corresponding action's
Original PR description
[FIX] fleet: fix vendor bill vehicle association bug Bug reprod: Go to 19.2 or above 1 - Go to vendor bills. 2 - Create an invoice line add vehicle. 3 - Click to vehicle via the link 4 - You will see…
[FIX] fleet: fix vendor bill vehicle association bug
Bug reprod: Go to 19.2 or above
1 - Go to vendor bills.
2 - Create an invoice line add vehicle.
3 - Click to vehicle via the link
4 - You will see fleet model and try to press to some smart buttons, traceback will occur (Odometer, Services...)
Bug cause:
1 - When we press to smart buttons for Odometer or Services we are going to return_action_to_open function.
2 - In this function corresponding action's xml id is calculated and we are calling that action and that will load some view.
3 - self.env.context is passed directly as a context
4 - In the view_move_form (That include invoice lines, account_id and vehicle_id fields), account_id has a context list_view_ref="account.view_account_list_from_entry"
5 - This context is passed in self.env.context and that's why it tries to load this list_view when we press to odometer,service smart buttons, which shouldn't be the case.
6 - In 19.1 this context is not in self.env.context because >=19.2 m2o_cell_with_extra_m2o_fields is used for account_id and account_id and vehicle_id fields are combined in the single cell.
7 - That's why the context of account_id is passed to the vehicle page as well.
Bug solution:
1 - In the return_action_to_open function I'm dropping the list_view_ref context and we can load the correct related views about odometer or service or other ones.
task - 6385611
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-prWe cannot assume in methods that the active_test is set. Therefore, we should always add active=True in search domains. Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
We cannot assume in methods that the active_test is set. Therefore, we should always add active=True in search domains. 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
`completeActiveField` crashes with "Cannot read properties of undefined (reading 'activeFields')" when the `extra` argument carries a `.related` object but the target `activeField` does not have one. This happens when a many2one field appears twice in the same view tree with different widget configurations — one plain, one with `relatedFields`. The concrete trigger: 1. `stock.picking.batch` has an x2many `picking_ids` whose inline list/kanban view contains `partner_id` as a plain many2one.
Original PR description
`completeActiveField` crashes with "Cannot read properties of undefined (reading 'activeFields')" when the `extra` argument carries a `.related` object but the target `activeField` does not have one.…
`completeActiveField` crashes with "Cannot read properties of undefined (reading 'activeFields')" when the `extra` argument carries a `.related` object but the target `activeField` does not have one.
This happens when a many2one field appears twice in the same view tree with different widget configurations — one plain, one with `relatedFields`. The concrete trigger:
1. `stock.picking.batch` has an x2many `picking_ids` whose inline list/kanban view contains `partner_id` as a plain many2one. `extractFieldsFromArchInfo` creates an activeField for `partner_id` with no `.related` property.
2. `website_sale_stock` inherits the `stock.picking` form view and adds a second `partner_id` node with `widget="pickup_location_many2one"`. That widget declares `relatedFields` (`pickup_location_data`), which `Field.parseFieldNode` converts into a synthetic `views.default`. When `extractFieldsFromArchInfo` processes the inline form view of `picking_ids`, the resulting activeField for `partner_id` gets a `.related` object from those fields.
3. `extractFieldsFromArchInfo` then merges the form view fields into the list view fields via `completeActiveFields`. For `partner_id` the field already exists in the list's activeFields (without `.related`), so `completeActiveField` is called. It checks `if (extra.related)` — true — then immediately accesses `activeField.related.activeFields`, which is undefined → crash.
The sibling function `patchActiveFields` already handles this exact scenario correctly:
activeField.related = activeField.related || { activeFields: {}, fields: {} };
Apply the same defensive initialisation in `completeActiveField`.
Part-of: odoo/odoo#160187
Related: odoo/enterprise#59935
Related: odoo/upgrade#6315
Backport-of odoo/odoo@03c0d6F
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-prIf we are in a case of a salary simulation, we don't care about future public holidays. The unlink done in _delete_future_public_holidays_timesheets was causing some cache invalidations which were messing up with the original offer.
Original PR description
If we are in a case of a salary simulation, we don't care about future public holidays. The unlink done in _delete_future_public_holidays_timesheets was causing some cache invalidations which were messing up with the original offer.
Backport of b667cacb (odoo/odoo#276156), currently on master. The guards of mark as read were only evaluated when requesting it, while the RPC itself goes through a sequential queue. A mark as read requested while another one was still in flight was thus executed later without any re-validation. Under CI load, the bus sync of a previous mark as read can lag enough for a focus-triggered mark as read to legitimately pass its guards on stale state and be queued. When the user then clicked "Ma
Original PR description
Backport of b667cacb (odoo/odoo#276156), currently on master. The guards of mark as read were only evaluated when requesting it, while the RPC itself goes through a sequential queue. A mark as read…
Backport of b667cacb (odoo/odoo#276156), currently on master.
The guards of mark as read were only evaluated when requesting it, while the RPC itself goes through a sequential queue. A mark as read requested while another one was still in flight was thus executed later without any re-validation.
Under CI load, the bus sync of a previous mark as read can lag enough for a focus-triggered mark as read to legitimately pass its guards on stale state and be queued. When the user then clicked "Mark as Unread", the queued mark as read executed right after and reverted that explicit action on the server, and through the resulting bus push, on the client as well. In the meeting view tour, the unread badge of the Chat action then never showed "1":
FAILED: [17/24] Tour discuss.meeting_view_public_tour
Step .o-mail-Meeting [title='Chat']:has(.badge:contains(1))
The state guards are now re-validated when the queued call actually executes: the member must still exist, the messages must not have been read in the meantime, and the channel must not have been marked as unread since the call was requested. The newest persistent message is still captured at request time as it is the payload of the intent: messages that arrived later have not been validated as read by the caller, their own triggers request another mark as read when appropriate.
https://runbot.odoo.com/odoo/error/941491Issue: ---------------------------------------- The units (day, year, etc.) aren't being translated in the Milestones view. Steps to reproduce: ---------------------------------------- - Switch the language to French - Go on an Accrual plan form view - In the milestones view, the units aren't translated Cause: ---------------------------------------- We input the key value of the selections fields `start_type` and `added_value_type`. These values aren't translated. Solution: --
Original PR description
Issue: ---------------------------------------- The units (day, year, etc.) aren't being translated in the Milestones view. Steps to reproduce: ---------------------------------------- - Switch the language to French - Go on an Accrual plan form view - In the milestones view, the units aren't translated Cause: ---------------------------------------- We input the key value of the selections fields `start_type` and `added_value_type`. These values aren't translated. Solution: ---------------------------------------- We create a dictionary with the same keys as the fields and a translated value as values. In the view, we read the values of the dictionary to get the translated units. opw-6367235 Forward-Port-Of: odoo/odoo#275575
Animated GIFs applied with an image shape freeze on Firefox and Safari when the shape's SVG is otherwise static. A dummy `<animateMotion dur="1ms" repeatCount="indefinite"/>` child on the <image> element keeps the animation running so the GIF plays. This hack was introduced in [1] but was missing from most shapes. This commit adds it to every <image> element that lacked it so any shape can be used with a GIF. task-5967171 [1]: https://github.com/odoo/odoo/commit/144e5ef799060da860a5fb1f
Original PR description
Animated GIFs applied with an image shape freeze on Firefox and Safari when the shape's SVG is otherwise static. A dummy `<animateMotion dur="1ms" repeatCount="indefinite"/>` child on the <image> element keeps the animation running so the GIF plays. This hack was introduced in [1] but was missing from most shapes. This commit adds it to every <image> element that lacked it so any shape can be used with a GIF. task-5967171 [1]: https://github.com/odoo/odoo/commit/144e5ef799060da860a5fb1fbe0fd009919afe79 Forward-Port-Of: odoo/odoo#275259
Problem: When posting expenses, if the expense domain is set as mandatory in any of the analytic plans, users can still submit expenses without entering an analytic distribution. This only happens when the expense is auto-approved on submission, which happens when the expense's employee is also the expense's manager or when the employee doesn't have an expense manager. Steps to reproduce: 1. Create an analytic plan with optional default applicability 2. Add an applicability rule with the d
Original PR description
Problem: When posting expenses, if the expense domain is set as mandatory in any of the analytic plans, users can still submit expenses without entering an analytic distribution. This only happens…
Problem: When posting expenses, if the expense domain is set as mandatory in any of the analytic plans, users can still submit expenses without entering an analytic distribution. This only happens when the expense is auto-approved on submission, which happens when the expense's employee is also the expense's manager or when the employee doesn't have an expense manager. Steps to reproduce: 1. Create an analytic plan with optional default applicability 2. Add an applicability rule with the domain set as Expense and set its applicability as mandatory. 3. Create a new expense 4. Set the expense's manager the same as the expense's employee. 5. Don't enter any analytic distribution. 6. Submit the expense 7. Notice how the expense is submitted and auto-approved without any error, even though no analytic distribution is entered and the analytic plan has a mandatory rule for expenses. Cause: The validation of the analytic distribution was only triggered on the approval of the expense, but when the expense is auto-approved on submission, the validation is not triggered at all. Solution: Move the validation of the analytic distribution to the do_approve method, which gets called both when an expense is approved and when it's auto-approved on submission. opw-6187340 Forward-Port-Of: odoo/odoo#270268
**Steps to reproduce:** - Install Contacts app - Open any record - Go to the chatter - Create an activity with a description - Duplicate the tab - Go back to the initial tab - Description doesn't appear anymore - Refreshing shows it but will remove it from the other tab **Issue:** Behavior comes from the broadcasting of activity changes between tabs `new browser.BroadcastChannel("mail.activity.channel");`. Computed fields are not recomputed on the receiver side after value inserti
Original PR description
**Steps to reproduce:** - Install Contacts app - Open any record - Go to the chatter - Create an activity with a description - Duplicate the tab - Go back to the initial tab - Description doesn't…
**Steps to reproduce:**
- Install Contacts app
- Open any record
- Go to the chatter
- Create an activity with a description
- Duplicate the tab
- Go back to the initial tab
- Description doesn't appear anymore
- Refreshing shows it but will remove it from the other tab
**Issue:**
Behavior comes from the broadcasting of activity changes between tabs `new browser.BroadcastChannel("mail.activity.channel");`.
Computed fields are not recomputed on the receiver side after value insertion in `_onActivityBroadcastChannelMessage` (also related components are not (re)mounted, e.g. when a new activity is created the other tab doesn't show it without a refresh).
This means that `isNoteEmpty` keeps its default value `true` (added by `this.toData()`) and the `note` stays hidden here [1]:
```xml
<div t-if="!props.activity.isNoteEmpty" class="o-mail-Activity-note text-break" t-out="props.activity.note"/>
```
**Fix:**
Remove computed fields in activity `serialize` before broadcasting them to ensure they don't force the default value.
(note installing `calendar` in 19.3+ removes this issue due to [2] which overrides the condition on `isNoteEmpty`)
[1] https://github.com/odoo/odoo/commit/eb9f0658c3da1a9fef69f1cc1117c2d44f9d61b1
[2] https://github.com/odoo/odoo/commit/44e2c2c5ca07849fd8964140f3ca61122c47f0c6
opw-6247412
Forward-Port-Of: odoo/odoo#276032
Forward-Port-Of: odoo/odoo#275528Modified `SampleServer._mockWebReadGroup` to intercept `groupby_read_specification`. It now dynamically fetches the requested related fields using `_mockWebSearchReadUnity` and safely injects them into the `__values` payload for each mock group, perfectly mirroring the standard ORM behavior. Task: [6307582](https://www.odoo.com/odoo/project/133/tasks/6307582) Forward-Port-Of: odoo/odoo#272135
Original PR description
Modified `SampleServer._mockWebReadGroup` to intercept `groupby_read_specification`. It now dynamically fetches the requested related fields using `_mockWebSearchReadUnity` and safely injects them into the `__values` payload for each mock group, perfectly mirroring the standard ORM behavior. Task: [6307582](https://www.odoo.com/odoo/project/133/tasks/6307582) Forward-Port-Of: odoo/odoo#272135
`_l10n_tr_nilvera_add_pdf_to_invoice` writes the response from `client.request('GET', '.../pdf')` directly into `ir.attachment.raw`. The Nilvera client sets `Accept: application/json` on the session and calls `response.json()` by default, so the returned value is a Python `str` holding the base64-encoded PDF body, not raw binary bytes. The previous code wrote to the base64-aware `datas` field, which auto-decoded its input. An earlier fix switched to `raw` to work around a `binascii.Error` fro
Original PR description
`_l10n_tr_nilvera_add_pdf_to_invoice` writes the response from `client.request('GET', '.../pdf')` directly into `ir.attachment.raw`. The Nilvera client sets `Accept: application/json` on the session…
`_l10n_tr_nilvera_add_pdf_to_invoice` writes the response from `client.request('GET', '.../pdf')` directly into `ir.attachment.raw`. The Nilvera client sets `Accept: application/json` on the session and calls `response.json()` by default, so the returned value is a Python `str` holding the base64-encoded PDF body, not raw binary bytes.
The previous code wrote to the base64-aware `datas` field, which auto-decoded its input. An earlier fix switched to `raw` to work around a `binascii.Error` from Python 3.14's stricter base64 validation in the `datas` auto-decode path. That switch silently changed what ends up on disk (`datas` decodes its input, `raw` does not)
Storing that string in the binary `raw` field encodes it as UTF-8, so the file on disk ends up as the literal ASCII of the base64 text. The attachment is served as `application/pdf` but the browser receives base64 ASCII and cannot preview or download the PDF.
Call `b64decode(response)` before storing so the attachment contains the actual PDF bytes.
OPW-6302803
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#274501
Forward-Port-Of: odoo/odoo#270759This fix is the same as this one https://github.com/odoo/odoo/pull/271577 but for the backend part of the code. After the fix, if you followed the same steps to reproduce and tried to close the session you would have an unbalanced entry for the session. Steps to reproduce: ------------------- * Create a 21% tax not included in price * Create a product with a price of 76.01 and the tax created above * Create a loyalty program with a 10% discount * Create a POS order with the product ab
Original PR description
This fix is the same as this one https://github.com/odoo/odoo/pull/271577 but for the backend part of the code. After the fix, if you followed the same steps to reproduce and tried to close the session you would have an unbalanced entry for the session. Steps to reproduce: ------------------- * Create a 21% tax not included in price * Create a product with a price of 76.01 and the tax created above * Create a loyalty program with a 10% discount * Create a POS order with the product above and apply the loyalty program * Validate the order and generate the invoice * Close the session > Observation: You need to force close the session because of unbalanced entry Why the fix: ------------ Apply the same fix for backend code. opw-6052112 Forward-Port-Of: odoo/odoo#276100 Forward-Port-Of: odoo/odoo#274985
**Steps to reproduce:** - Create a product A, tracked by lots - Create a kit product, include a component A - Change the UoM to 0.5 - Go to the PoS, order this kit product - Also order the component A, with a quantity of 2 - Pay for it, ask for an invoice - Go to the created picking - The Demand column is correctly computed and is 0.5 - The Quantity column is wrong and is 2 **Why the fix:** When getting the data from https://github.com/odoo/odoo/blob/e0d84c7fbb270d0d1f82572daefa
Original PR description
**Steps to reproduce:** - Create a product A, tracked by lots - Create a kit product, include a component A - Change the UoM to 0.5 - Go to the PoS, order this kit product - Also order the component…
**Steps to reproduce:** - Create a product A, tracked by lots - Create a kit product, include a component A - Change the UoM to 0.5 - Go to the PoS, order this kit product - Also order the component A, with a quantity of 2 - Pay for it, ask for an invoice - Go to the created picking - The Demand column is correctly computed and is 0.5 - The Quantity column is wrong and is 2 **Why the fix:** When getting the data from https://github.com/odoo/odoo/blob/e0d84c7fbb270d0d1f82572daefa96c2978d3785/addons/point_of_sale/models/stock_picking.py#L283 we always get the component's line, as the move's product is the component, even if it used to be the kit product's move. This is because when exploding a kit's moves, it gets the kit's component as a product instead of keeping the kit product. This was introducing a weird behavior because we took the quantity from the component line, and not from the kit line, meaning the kit would always have the same quantity as the component. We now check if the move is actually a kit product's move, and if it is we adapt the qty to correct one by fetching the correct line's qty, and adapting it with the correct UoM. Changing the line in itself would not work, as the kit itself is not tracked by lots, so we would not enter https://github.com/odoo/odoo/blob/e0d84c7fbb270d0d1f82572daefa96c2978d3785/addons/point_of_sale/models/stock_picking.py#L284 and the move line would not be correctly created. opw-6153000 Forward-Port-Of: odoo/odoo#276098 Forward-Port-Of: odoo/odoo#262551
**Steps to reproduce:** 1. Install Sales and EasyPost modules and enable delivery methods in the Settings. 2. Configure a new delivery method in [Sales -> Configuration -> Delivery Methods], choose provider as EasyPost and set the API keys. 3. Set any margin % on the delivery method. 4. Add the shipping line to a sale order via "Add shipping". **Issue:** Unit Price displays at a 4dp precision while Subtotal correctly displays at a 2dp precision **Why this happens:** `_apply_margins(
Original PR description
**Steps to reproduce:** 1. Install Sales and EasyPost modules and enable delivery methods in the Settings. 2. Configure a new delivery method in [Sales -> Configuration -> Delivery Methods], choose provider as EasyPost and set the API keys. 3. Set any margin % on the delivery method. 4. Add the shipping line to a sale order via "Add shipping". **Issue:** Unit Price displays at a 4dp precision while Subtotal correctly displays at a 2dp precision **Why this happens:** `_apply_margins()` returns a float from margin arithmetic calculation with no currency rounding applied. `rate_shipment()` writes this unrounded value directly to res['price'], which becomes the delivery line's price_unit. opw-6355318 Forward-Port-Of: odoo/odoo#275233
Miscellaneous changes
During the _run_average_batch(), we call _get_value() on each dropship move on which an AVCO product is used. In this function, if the following condition is met, we call the function _get_manual_value(): https://github.com/odoo/odoo/blob/12dd03fb678870ddd3f1f8dca66aa3f934aa7985/addons/stock_account/models/stock_move.py#L359-L361 This method's goal is to search product.value records related to the current move. https://github.com/odoo/odoo/blob/12dd03fb678870ddd3f1f8dca66aa3f934aa7985/a
Original PR description
During the _run_average_batch(), we call _get_value() on each dropship move on which an AVCO product is used. In this function, if the following condition is met, we call the function…
During the _run_average_batch(), we call _get_value() on each dropship move on which an AVCO product is used. In this function, if the following condition is met, we call the function _get_manual_value(): https://github.com/odoo/odoo/blob/12dd03fb678870ddd3f1f8dca66aa3f934aa7985/addons/stock_account/models/stock_move.py#L359-L361 This method's goal is to search product.value records related to the current move. https://github.com/odoo/odoo/blob/12dd03fb678870ddd3f1f8dca66aa3f934aa7985/addons/stock_account/models/stock_move.py#L431-L447 This search is performed once per move selected previously even if they are not related to any product.value. We propose to cache the id of every move that is linked to at least one product.value to ensure the search method is only performed for those and potentially reduce the number of calls to the search method. Benchmark ------------ Reducing the execution time with this modification supposes that the majority of stock.move records are not linked to any product.value, which is usually the case. The following benchmark shows the execution times of _run_average_batch() depending on that. | No stock.move | No of moves linked to product.value | Before PR | After PR | |---------------|-------------------------------------|-----------|----------| | 100 | 10 | 1.03 s | 421 ms | | 1000 | 100 | 7.13 s | 1.14 s | | 10000 | 100 | 57.21 s | 1.55 s | | 10000 | 1000 | 60.42 s | 8.98 s | When every stock.move is linked to a product.value, the modification will introduce more operations than needed and slow down the execution. The following benchmark illustrates that. | No stock.move | Before PR | After PR | |---------------|-----------|----------| | 100 | 1.14 s | 1.15 s | | 1000 | 8.21 s | 8.37 s | | 10000 | 80.64 s | 81.51 s | opw-6050007 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#257619
24 changes
Enhancements to existing features
It is mandatory in BE to add a legal note on the invoice when using a "Co-Contractant" tax task-5905176 Forward-Port-Of: odoo/odoo#263893 Forward-Port-Of: odoo/odoo#251797
Original PR description
It is mandatory in BE to add a legal note on the invoice when using a "Co-Contractant" tax task-5905176 Forward-Port-Of: odoo/odoo#263893 Forward-Port-Of: odoo/odoo#251797
Servers would return a 403 because we annoy them for downloading the WSDL/XSD at every call. opw-6237180 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#267482
Original PR description
Servers would return a 403 because we annoy them for downloading the WSDL/XSD at every call. opw-6237180 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#267482
Resolved issues and error corrections
### Before this PR When importing a FatturaPA XML, Odoo sets the fiscal position on the bill from the partner but does not apply it to the line taxes so a fiscal position that remaps taxes (partial deductibility, reverse charge, split payment) never map the imported lines. ### After this PR the fiscal position is correctly applied ### To reproduce 1. Apply to Italian vendor a fiscal position that maps the 22% purchase tax to a partial-deductibility tax (e.g. "22%" →"22% ind. 50%").
Original PR description
### Before this PR When importing a FatturaPA XML, Odoo sets the fiscal position on the bill from the partner but does not apply it to the line taxes so a fiscal position that remaps taxes (partial deductibility, reverse charge, split payment) never map the imported lines. ### After this PR the fiscal position is correctly applied ### To reproduce 1. Apply to Italian vendor a fiscal position that maps the 22% purchase tax to a partial-deductibility tax (e.g. "22%" →"22% ind. 50%"). 2. Import a FatturaPA XML from that vendor with 22% lines. 3. The bill header shows the fiscal position, but the lines keep the plain 22% tax instead of the mapped one. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275614 Forward-Port-Of: odoo/odoo#274738
**Steps to reproduce:** - Create a dynamic variant with 2 values, A and B - B should have an extra price of 200 - Create a product which has those two variants, with a price of 1000 - Set up a barcode for the product with the B variant only - Go to the PoS, enter the barcode for the product with the B variant - The price is 1400 instead of 1200 **Why the fix:** The extra price for dynamic variants ordered through the barcode will be counted twice. This is because it is first count
Original PR description
**Steps to reproduce:** - Create a dynamic variant with 2 values, A and B - B should have an extra price of 200 - Create a product which has those two variants, with a price of 1000 - Set up a…
**Steps to reproduce:** - Create a dynamic variant with 2 values, A and B - B should have an extra price of 200 - Create a product which has those two variants, with a price of 1000 - Set up a barcode for the product with the B variant only - Go to the PoS, enter the barcode for the product with the B variant - The price is 1400 instead of 1200 **Why the fix:** The extra price for dynamic variants ordered through the barcode will be counted twice. This is because it is first counted in the _scan(code) method when we fetch the product from the models, then counted again when adding the line to the current order. https://github.com/odoo/odoo/blob/f4d079cc5a9c47672cf1a6747bb073e8e74f7350/addons/point_of_sale/static/src/app/services/pos_store.js#L1264-L1269 This step is necessary for the usual flow without the barcode as we need to add this extra price, but when using the barcode, the list price of the product we fetch is already 1200, as the extra price is already included when fetching it from the backend. It works for always attributes because we explicitly check that we are not adding the extra price again in the above code, and that the list price already includes the extra price. It also works for the never attributes because values.product_id.product_template_variant_value_ids.length is 0, so the code to update the extra price is never triggered. As we still need to add the extra price for the usual flow, we now just check if we have a code, meaning we added the product through the barcode and that we do not need to add it again, as the list price already accounts for the extra price. opw-6328600 Forward-Port-Of: odoo/odoo#272395
When creating or editing a portal billing address, the Company Name field was pre-filled from `commercial_company_name`. For contacts without a parent company, the commercial partner is the contact itself, so the contact name was shown as the company name. Use the partner's actual parent company name instead, so the field stays empty when no company is linked while still showing the existing parent company when one exists. see: https://github.com/odoo/odoo/commit/18a59cf26f2d9400f76deec483
Original PR description
When creating or editing a portal billing address, the Company Name field was pre-filled from `commercial_company_name`. For contacts without a parent company, the commercial partner is the contact itself, so the contact name was shown as the company name. Use the partner's actual parent company name instead, so the field stays empty when no company is linked while still showing the existing parent company when one exists. see: https://github.com/odoo/odoo/commit/18a59cf26f2d9400f76deec483f6ddab87da0c55 Task-6372638
Click Working Files menu, then open studio. Before this commit there was an error, because the accounting code tried to check access rights on an new record (no id) After this commit there is no crash. runbot-error-941248 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
Click Working Files menu, then open studio. Before this commit there was an error, because the accounting code tried to check access rights on an new record (no id) After this commit there is no crash. runbot-error-941248 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
Before this commit, when making a test print from the POS backend, the following issues would occur: - Very slow response - Missing cut, and extra 'A' character is printed This commit fixes both these issues. The slow response is avoided by not performing the network tests when they aren't used in the printed receipt. The cut issue is solved by appending a newline character to the message. task-6391141 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/s
Original PR description
Before this commit, when making a test print from the POS backend, the following issues would occur: - Very slow response - Missing cut, and extra 'A' character is printed This commit fixes both these issues. The slow response is avoided by not performing the network tests when they aren't used in the printed receipt. The cut issue is solved by appending a newline character to the message. task-6391141 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Animated GIFs applied with an image shape freeze on Firefox and Safari when the shape's SVG is otherwise static. A dummy `<animateMotion dur="1ms" repeatCount="indefinite"/>` child on the <image> element keeps the animation running so the GIF plays. This hack was introduced in [1] but was missing from most shapes. This commit adds it to every <image> element that lacked it so any shape can be used with a GIF. task-5967171 [1]: https://github.com/odoo/odoo/commit/144e5ef799060da860a5fb1f
Original PR description
Animated GIFs applied with an image shape freeze on Firefox and Safari when the shape's SVG is otherwise static. A dummy `<animateMotion dur="1ms" repeatCount="indefinite"/>` child on the <image> element keeps the animation running so the GIF plays. This hack was introduced in [1] but was missing from most shapes. This commit adds it to every <image> element that lacked it so any shape can be used with a GIF. task-5967171 [1]: https://github.com/odoo/odoo/commit/144e5ef799060da860a5fb1fbe0fd009919afe79 Forward-Port-Of: odoo/odoo#275259
Loading a certificate could raise an unhandled exception instead of failing gracefully. Clearing the content, uploading a bundle with a corrupted certificate block, or handling certificates with unsupported signature algorithms or malformed extensions all could end up in a traceback. Guard those paths to ensure loading errors are handled. opw-6370529 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I
Original PR description
Loading a certificate could raise an unhandled exception instead of failing gracefully. Clearing the content, uploading a bundle with a corrupted certificate block, or handling certificates with unsupported signature algorithms or malformed extensions all could end up in a traceback. Guard those paths to ensure loading errors are handled. opw-6370529 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#275325
Previously: 1.`purchase_cdnur_regular` section was assigned to credit/debit notes of: - import of goods - import of services without RCM However: - import of goods should be handled through bill of supply - import of services without RCM is not possible Therefore, with this commit, such journal items are moved to `purchase_out_of_scope`. 2.`purcha
Original PR description
Previously:
1.`purchase_cdnur_regular` section was assigned to credit/debit notes of:
- import of goods
- import of services without RCM However:
- import of goods should be handled through bill of supply
- import of services without RCM is not possible Therefore, with this commit, such journal items are moved to `purchase_out_of_scope`.
2.`purchase_imp_services` section included import of services both with and
without RCM. Since import of services without RCM is not possible, those
journal items are now moved to `purchase_out_of_scope`.
3.Credit/debit notes of import of services with RCM were previously moved to
`purchase_out_of_scope`, which was incorrect. With this commit, they are now
correctly moved to `purchase_imp_services`.
task-6330737
Forward-Port-Of: odoo/odoo#272453Problem: When posting expenses, if the expense domain is set as mandatory in any of the analytic plans, users can still submit expenses without entering an analytic distribution. This only happens when the expense is auto-approved on submission, which happens when the expense's employee is also the expense's manager or when the employee doesn't have an expense manager. Steps to reproduce: 1. Create an analytic plan with optional default applicability 2. Add an applicability rule with the d
Original PR description
Problem: When posting expenses, if the expense domain is set as mandatory in any of the analytic plans, users can still submit expenses without entering an analytic distribution. This only happens…
Problem: When posting expenses, if the expense domain is set as mandatory in any of the analytic plans, users can still submit expenses without entering an analytic distribution. This only happens when the expense is auto-approved on submission, which happens when the expense's employee is also the expense's manager or when the employee doesn't have an expense manager. Steps to reproduce: 1. Create an analytic plan with optional default applicability 2. Add an applicability rule with the domain set as Expense and set its applicability as mandatory. 3. Create a new expense 4. Set the expense's manager the same as the expense's employee. 5. Don't enter any analytic distribution. 6. Submit the expense 7. Notice how the expense is submitted and auto-approved without any error, even though no analytic distribution is entered and the analytic plan has a mandatory rule for expenses. Cause: The validation of the analytic distribution was only triggered on the approval of the expense, but when the expense is auto-approved on submission, the validation is not triggered at all. Solution: Move the validation of the analytic distribution to the do_approve method, which gets called both when an expense is approved and when it's auto-approved on submission. opw-6187340 Forward-Port-Of: odoo/odoo#270268
#### Issue: When creating a reordering rule for a shared manufactured product in a multi-company database, saving the rule may raise an `AccessError` on `mrp.bom`. The orderpoint is still created, but the user sees a record-rule error if the product also has BoMs in companies that are not currently active. #### Example: A product is shared across multiple companies, and each company has its own BoM for that product. In the reproduced case, the active company has the correct variant Bo
Original PR description
#### Issue: When creating a reordering rule for a shared manufactured product in a multi-company database, saving the rule may raise an `AccessError` on `mrp.bom`. The orderpoint is still created,…
#### Issue: When creating a reordering rule for a shared manufactured product in a multi-company database, saving the rule may raise an `AccessError` on `mrp.bom`. The orderpoint is still created, but the user sees a record-rule error if the product also has BoMs in companies that are not currently active. #### Example: A product is shared across multiple companies, and each company has its own BoM for that product. In the reproduced case, the active company has the correct variant BoM. However, the orderpoint computation first checks the broader product-template BoM relation, which may include BoMs from the other companies. As a result, Odoo can try to access a BoM from another company while the user is only working in the active company, causing an access error. #### Steps to reproduce: Use a multi-company database with MRP enabled. Create or use a shared product available to multiple companies. Create BoMs for that product in more than one company. Set the active company to the company where the reordering rule should be created. Create a reordering rule for the product. Save the reordering rule. Note the AccessError related to mrp.bom. #### Root Cause: The MRP orderpoint computations read `product_id.bom_ids` directly. This is the product-template BoM relation and can include BoMs from other companies for a shared product. Reading fields on those BoMs, such as `product_uom_id`, can hit the standard `mrp.bom` multi-company record rule. #### Fix: Prefer `product_id.variant_bom_ids` before falling back to `product_id.bom_ids` in the affected orderpoint computations. This avoids reading template-level BoMs from other companies when the product has a variant-specific BoM for the current reordering-rule use case. opw-6253743 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#267411
**Steps to reproduce:** - Install Contacts app - Open any record - Go to the chatter - Create an activity with a description - Duplicate the tab - Go back to the initial tab - Description doesn't appear anymore - Refreshing shows it but will remove it from the other tab **Issue:** Behavior comes from the broadcasting of activity changes between tabs `new browser.BroadcastChannel("mail.activity.channel");`. Computed fields are not recomputed on the receiver side after value inserti
Original PR description
**Steps to reproduce:** - Install Contacts app - Open any record - Go to the chatter - Create an activity with a description - Duplicate the tab - Go back to the initial tab - Description doesn't…
**Steps to reproduce:**
- Install Contacts app
- Open any record
- Go to the chatter
- Create an activity with a description
- Duplicate the tab
- Go back to the initial tab
- Description doesn't appear anymore
- Refreshing shows it but will remove it from the other tab
**Issue:**
Behavior comes from the broadcasting of activity changes between tabs `new browser.BroadcastChannel("mail.activity.channel");`.
Computed fields are not recomputed on the receiver side after value insertion in `_onActivityBroadcastChannelMessage` (also related components are not (re)mounted, e.g. when a new activity is created the other tab doesn't show it without a refresh).
This means that `isNoteEmpty` keeps its default value `true` (added by `this.toData()`) and the `note` stays hidden here [1]:
```xml
<div t-if="!props.activity.isNoteEmpty" class="o-mail-Activity-note text-break" t-out="props.activity.note"/>
```
**Fix:**
Remove computed fields in activity `serialize` before broadcasting them to ensure they don't force the default value.
(note installing `calendar` in 19.3+ removes this issue due to [2] which overrides the condition on `isNoteEmpty`)
[1] https://github.com/odoo/odoo/commit/eb9f0658c3da1a9fef69f1cc1117c2d44f9d61b1
[2] https://github.com/odoo/odoo/commit/44e2c2c5ca07849fd8964140f3ca61122c47f0c6
opw-6247412
Forward-Port-Of: odoo/odoo#276032
Forward-Port-Of: odoo/odoo#275528Remove the generic active 296 and 297 impairment accounts and make the existing French PCG 296/297 subaccounts active instead, because what we use in the balance sheet formulas are the subaccounts, and it's better to remove the generic ones to not give users the ability to post on these generic accounts, also adapt their translations to be aligned with PCG wording. Move pcg_2962 from the companies chart file to the base French chart file, as 2962 is a general PCG account and not a compa
Original PR description
Remove the generic active 296 and 297 impairment accounts and make the existing French PCG 296/297 subaccounts active instead, because what we use in the balance sheet formulas are the subaccounts, and it's better to remove the generic ones to not give users the ability to post on these generic accounts, also adapt their translations to be aligned with PCG wording. Move pcg_2962 from the companies chart file to the base French chart file, as 2962 is a general PCG account and not a company related one. Note: this is how things were already in 19.0 and this is how they should be, the changes happened by mistake as an unwanted side effect of commit 4f6068a6c88bf0530c19254df403e1194823b415 task-[6226138](https://www.odoo.com/odoo/project/967/tasks/6226138) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
v19 WIoT Boxes will be listening on localhost when updating to v19.1+, we need to ensure they use `http_interface = 0.0.0.0` after the update.
Original PR description
v19 WIoT Boxes will be listening on localhost when updating to v19.1+, we need to ensure they use `http_interface = 0.0.0.0` after the update.
Modified `SampleServer._mockWebReadGroup` to intercept `groupby_read_specification`. It now dynamically fetches the requested related fields using `_mockWebSearchReadUnity` and safely injects them into the `__values` payload for each mock group, perfectly mirroring the standard ORM behavior. Task: [6307582](https://www.odoo.com/odoo/project/133/tasks/6307582) Forward-Port-Of: odoo/odoo#272135
Original PR description
Modified `SampleServer._mockWebReadGroup` to intercept `groupby_read_specification`. It now dynamically fetches the requested related fields using `_mockWebSearchReadUnity` and safely injects them into the `__values` payload for each mock group, perfectly mirroring the standard ORM behavior. Task: [6307582](https://www.odoo.com/odoo/project/133/tasks/6307582) Forward-Port-Of: odoo/odoo#272135
**Steps to reproduce:** 1. Install Sales and EasyPost modules and enable delivery methods in the Settings. 2. Configure a new delivery method in [Sales -> Configuration -> Delivery Methods], choose provider as EasyPost and set the API keys. 3. Set any margin % on the delivery method. 4. Add the shipping line to a sale order via "Add shipping". **Issue:** Unit Price displays at a 4dp precision while Subtotal correctly displays at a 2dp precision **Why this happens:** `_apply_margins(
Original PR description
**Steps to reproduce:** 1. Install Sales and EasyPost modules and enable delivery methods in the Settings. 2. Configure a new delivery method in [Sales -> Configuration -> Delivery Methods], choose provider as EasyPost and set the API keys. 3. Set any margin % on the delivery method. 4. Add the shipping line to a sale order via "Add shipping". **Issue:** Unit Price displays at a 4dp precision while Subtotal correctly displays at a 2dp precision **Why this happens:** `_apply_margins()` returns a float from margin arithmetic calculation with no currency rounding applied. `rate_shipment()` writes this unrounded value directly to res['price'], which becomes the delivery line's price_unit. opw-6355318 Forward-Port-Of: odoo/odoo#275233
**Steps to reproduce:** - create storable avco product - set the cost to 10 - set an onhand quantity of 100 in WH/stock - create another warehouse (if you don't already have another one) - create an internal transfer from WH/Stock to WH2 - open the 'stock' view - click on inventory at date - confirm **Current behavior:** the total value is 11.000 **Expected behavior:** total value should be 10.000 **Cause of the issue:** To compute the total_value of the product, _co
Original PR description
**Steps to reproduce:** - create storable avco product - set the cost to 10 - set an onhand quantity of 100 in WH/stock - create another warehouse (if you don't already have another one) - create an…
**Steps to reproduce:** - create storable avco product - set the cost to 10 - set an onhand quantity of 100 in WH/stock - create another warehouse (if you don't already have another one) - create an internal transfer from WH/Stock to WH2 - open the 'stock' view - click on inventory at date - confirm **Current behavior:** the total value is 11.000 **Expected behavior:** total value should be 10.000 **Cause of the issue:** To compute the total_value of the product, _compute_value calls _run_average_batch https://github.com/odoo/odoo/blob/68f258e99f42693131a5309b3606c3b95f93d824/addons/stock_account/models/product.py#L260 Inside run_average_batch we need the qty_available at the time of last manual value (which is when we set the cost to 10 manually) in order to value all this quantity at the value of the manual value. https://github.com/odoo/odoo/blob/68f258e99f42693131a5309b3606c3b95f93d824/addons/stock_account/models/product.py#L435 This qty should be 0 cause we had no onhand quantity when we set the cost to 10. But it's actually going to be 10, here is why : Inside _compute_quantities_dict, because we're asking for a quantity in the past, the computation is current quantity - quantities that went in between the date in the past and now + quantities that went out between the date in the past and now. https://github.com/odoo/odoo/blob/154b49ec6be6230989ee4eb420e7f83b681ff520/addons/stock/models/product.py#L255 So we should have : 100 - 100 (moves_in_res_past) + 0 (moves_out_res_past) = 0 because we have 100 now and between the date we're asking for (the time of the manual value) and now there is one move in (when we set a quantity of 100) and no move out. But moves_out_res_past will actually be 10 for our product instead of 0. That's because in the read_group, our internal move will be considered as a move out and be taken into account https://github.com/odoo/odoo/blob/60cf607d7148ebe389cc8513aa3b82e156cd4400/addons/stock/models/product.py#L233-L234 That's because: When we called _run_average_batch from _compute_value, we called it on 'products_to_value', which is based on 'products', which was computed calling with_valuation_context() https://github.com/odoo/odoo/blob/60cf607d7148ebe389cc8513aa3b82e156cd4400/addons/stock_account/models/product.py#L206 which passes the valued internal location in the context https://github.com/odoo/odoo/blob/60cf607d7148ebe389cc8513aa3b82e156cd4400/addons/stock_account/models/product.py#L366-L370 As wh2 is not internal (it's a view) it's not included in the locations from the context. strict is also set to True Therefore at the beginning of compute_quantities_dict, when we call _get_domain_location to compute domain_move_out_loc (on which domain_move_out_done will be based), https://github.com/odoo/odoo/blob/60cf607d7148ebe389cc8513aa3b82e156cd4400/addons/stock/models/product.py#L165 inside _get_domain_location, because a location is given in the context that's the one we're going to use. https://github.com/odoo/odoo/blob/60cf607d7148ebe389cc8513aa3b82e156cd4400/addons/stock/models/product.py#L365 and because strict is in the context, dest_location_domain_out will be "location_dest_id not in [the list of valued location which does not include wh2]". https://github.com/odoo/odoo/blob/7e95d32d669a7ee7c50b5e665697cb577be0af93/addons/stock/models/product.py#L402-L405 And back in compute_quantities_dict(), domain_move_out_loc will be "location_id in [the list of valued location] and location_dest_id not in [the list of valued location]". Our internal move will therefore be considered as an out move and taken into account in the computation mentioned above. Which explains why quantity will be 10 inside run_average_batch and why the computation of total_value is wrong **fix:** in 19.0 the fix is in the xml to take less risk with regards to stable policy, however starting from 19.1 the fix will be in python opw-6321636 Forward-Port-Of: odoo/odoo#273388
**Steps to reproduce:** 1. Go to Website > Edit a page. 2. Add multiple Badges side by side. 3. Save the page. 4. Switch the website language, click on Edit/Translate and translate the badges. 5. Save. **Issue:** After saving in translate mode, all badge elements are merged into one. **Why this happens:** During save, `cleanForSave` triggers `mergeAdjacentInlines` on a detached clone of the dirty element. This clone lacks all the css styling, so `getComputedStyle` returns `""` f
Original PR description
**Steps to reproduce:** 1. Go to Website > Edit a page. 2. Add multiple Badges side by side. 3. Save the page. 4. Switch the website language, click on Edit/Translate and translate the badges. 5.…
**Steps to reproduce:** 1. Go to Website > Edit a page. 2. Add multiple Badges side by side. 3. Save the page. 4. Switch the website language, click on Edit/Translate and translate the badges. 5. Save. **Issue:** After saving in translate mode, all badge elements are merged into one. **Why this happens:** During save, `cleanForSave` triggers `mergeAdjacentInlines` on a detached clone of the dirty element. This clone lacks all the css styling, so `getComputedStyle` returns `""` for all padding/margin on detached nodes. Consequently, `areSimilarElements` incorrectly considers sibling `s_badge` spans as identical and merges them. A recent fix (https://github.com/odoo/odoo/commit/91972ec2bbd85f9cfd7a1af794bbb2385a312f30) applied to `BadgeOptionPlugin` registers `s_badge` as unsplittable via `unsplittable_node_predicates`, preventing the merge in normal edit mode. However, translate mode loads a separate plugin, `BadgeTranslationPlugin`, which was added in the commit https://github.com/odoo/odoo/commit/cbb2eb2edfeecbc21a70c1a3cba81ad0a7ac9c75 that lacks the same predicate. opw-6261146 Forward-Port-Of: odoo/odoo#276040 Forward-Port-Of: odoo/odoo#273986
The "Opening thread with needaction messages should mark all messages of thread as read" test opens a channel that holds an inbox (needaction) message and asserts mark_all_as_read is sent. Two flows can mark that message as read: the channel messages fetch, through set_message_done, and mark_all_as_read, sent by markAsRead when the channel gets focused on open. When the self member's new_message_separator is 0, opening the channel fetches its messages around 0, and that fetch marks the messag
Original PR description
The "Opening thread with needaction messages should mark all messages of thread as read" test opens a channel that holds an inbox (needaction) message and asserts mark_all_as_read is sent. Two flows can mark that message as read: the channel messages fetch, through set_message_done, and mark_all_as_read, sent by markAsRead when the channel gets focused on open. When the self member's new_message_separator is 0, opening the channel fetches its messages around 0, and that fetch marks the message as read and drops the needaction counter to 0 before markAsRead runs. mark_all_as_read is then skipped and the step assertion receives nothing. Give the member a non-zero separator (the pre-existing message is already read) so opening the channel no longer fetches around 0, leaving mark_all_as_read as the flow that marks the inbox message read. https://runbot.odoo.com/odoo/error/243651 Forward-Port-Of: odoo/odoo#276181
## Steps to reproduce: - Install sale_timesheet - Create a timesheet with a Sale order item linked to it - Change the sale order item on that timesheet - Change the project linked to the timesheet to a non-billable project - Notice the sale order item still linked to the timesheet ## Cause: When computing the so_line we filter out the records that has is_so_line_edited as true, so when changing the SOL before changing the project we don't reset so_line field when setting a non-bill
Original PR description
## Steps to reproduce: - Install sale_timesheet - Create a timesheet with a Sale order item linked to it - Change the sale order item on that timesheet - Change the project linked to the timesheet to a non-billable project - Notice the sale order item still linked to the timesheet ## Cause: When computing the so_line we filter out the records that has is_so_line_edited as true, so when changing the SOL before changing the project we don't reset so_line field when setting a non-billable project. ## Fix: We reset the is_so_line_edited field to false when changing the project to a non-billable one. opw-6311549 Forward-Port-Of: odoo/odoo#275919
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#275995 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#275995 Forward-Port-Of: odoo/odoo#275677
When multiple invoices were sent to MyInvois in a single batch and at least one document failed validation, the failure branch of _myinvois_submit_documents added self.invoice_ids (all invoices in the batch) to invoice_to_cancel instead of the current record's invoices. Every sibling in the batch was then cancelled locally, even those whose own MyInvois submission had been accepted and moved to in_progress. The account.move ended up in state 'cancel' while its myinvois.document stayed 'valid'
Original PR description
When multiple invoices were sent to MyInvois in a single batch and at least one document failed validation, the failure branch of _myinvois_submit_documents added self.invoice_ids (all invoices in the batch) to invoice_to_cancel instead of the current record's invoices. Every sibling in the batch was then cancelled locally, even those whose own MyInvois submission had been accepted and moved to in_progress. The account.move ended up in state 'cancel' while its myinvois.document stayed 'valid', which violates the intended synchronization between the two records and blocked users from posting the credit note. Scope the cancellation to record.invoice_ids so only the invoice tied to the failing document is cancelled. 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#274727
Miscellaneous changes
This reverts commit 787223c. In the point of sale, concurrent sales of the same product can happen, e.g. if several physical points of sales are open at the same time, each with their own session. In the case where the underlying product's valuation is tracked automatically and perpetually, the creation of the pos order leads to the creation of the stock picking, which in turn leads to a search for the next available svl. Because of the flush_all, this can cause lots of retry failures and ass
Original PR description
This reverts commit 787223c. In the point of sale, concurrent sales of the same product can happen, e.g. if several physical points of sales are open at the same time, each with their own session. In the case where the underlying product's valuation is tracked automatically and perpetually, the creation of the pos order leads to the creation of the stock picking, which in turn leads to a search for the next available svl. Because of the flush_all, this can cause lots of retry failures and associated delay/latency/overall perceived slowness for the end user. opw-6206709 Forward-Port-Of: odoo/odoo#275264
13 changes
Resolved issues and error corrections
*: website, website_sale `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#275993 Forward-Port-Of: odoo/odoo#275262
Original PR description
*: website, website_sale `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#275993 Forward-Port-Of: odoo/odoo#275262
# How to reproduce - Set an employee's work schedule to flexible 40hrs/week with 8hrs of work per day - Create a new attendance for that employee that : - Is the very first attendance of that employee - Is not on a monday (e.g. a Friday) - Create a second attendance for that employee that : - Is atleast one week after the first attendance - Is one of the two days of the week before the day of the week of the first attendance (e.g. a Wednesday or a Thursday) - Go to the settings, upda
Original PR description
# How to reproduce - Set an employee's work schedule to flexible 40hrs/week with 8hrs of work per day - Create a new attendance for that employee that : - Is the very first attendance of that…
# How to reproduce - Set an employee's work schedule to flexible 40hrs/week with 8hrs of work per day - Create a new attendance for that employee that : - Is the very first attendance of that employee - Is not on a monday (e.g. a Friday) - Create a second attendance for that employee that : - Is atleast one week after the first attendance - Is one of the two days of the week before the day of the week of the first attendance (e.g. a Wednesday or a Thursday) - Go to the settings, update the value for "Tolerance Time In Favor Of Company" and save - Come back to the second attendance # The problem The Extra Hours have changed and are equal to the worked time # Cause When we update the tolerance time, we recompute the overtime of every attendance of every employee. In this recomputation, we compute the `expected_attendance` of every employee and transforms them into a dict of the expected working time for each day : https://github.com/odoo/odoo/blob/281658e86971687656f3235ac1ff8afcb52f2908/addons/hr_attendance/models/hr_attendance.py#L349-L356 Later, if an attendace of the employee was not in their expected working days, then we consider that all hours worked were overtime : https://github.com/odoo/odoo/blob/281658e86971687656f3235ac1ff8afcb52f2908/addons/hr_attendance/models/hr_attendance.py#L381-L384 This makes sense but our issue is that days in the middle of the week are considered "time-off" when they should not. That's because for flexible working schedules, we emulate the expected working hours based on the total hours per week and the maximum hours per day : https://github.com/odoo/odoo/blob/281658e86971687656f3235ac1ff8afcb52f2908/addons/resource/models/resource_calendar.py#L384-L386 For every "week", we start from the first day and continue forward by greedily using all hours for that day until there is no more hours for the week. The problem is that since this PR, we don't start "weeks" on mondays, but on the `start_datime` : https://github.com/odoo/odoo/commit/af36e73108cef4326f6125c3af491b07a51534fe And in our case, `start_datetime` is the very first attendance day of the employee : https://github.com/odoo/odoo/blob/281658e86971687656f3235ac1ff8afcb52f2908/addons/hr_attendance/models/hr_attendance.py#L344 So our expected working days are desynched with the days of the week # Proposed solution We partly revert https://github.com/odoo/odoo/commit/af36e73108cef4326f6125c3af491b07a51534fe opw-6289079 Forward-Port-Of: odoo/odoo#269820
## Steps to reproduce: - Install sale_timesheet - Create a timesheet with a Sale order item linked to it - Change the sale order item on that timesheet - Change the project linked to the timesheet to a non-billable project - Notice the sale order item still linked to the timesheet ## Cause: When computing the so_line we filter out the records that has is_so_line_edited as true, so when changing the SOL before changing the project we don't reset so_line field when setting a non-bill
Original PR description
## Steps to reproduce: - Install sale_timesheet - Create a timesheet with a Sale order item linked to it - Change the sale order item on that timesheet - Change the project linked to the timesheet to a non-billable project - Notice the sale order item still linked to the timesheet ## Cause: When computing the so_line we filter out the records that has is_so_line_edited as true, so when changing the SOL before changing the project we don't reset so_line field when setting a non-billable project. ## Fix: We reset the is_so_line_edited field to false when changing the project to a non-billable one. opw-6311549 Forward-Port-Of: odoo/odoo#275919
Animated GIFs applied with an image shape freeze on Firefox and Safari when the shape's SVG is otherwise static. A dummy `<animateMotion dur="1ms" repeatCount="indefinite"/>` child on the <image> element keeps the animation running so the GIF plays. This hack was introduced in [1] but was missing from most shapes. This commit adds it to every <image> element that lacked it so any shape can be used with a GIF. task-5967171 [1]: https://github.com/odoo/odoo/commit/144e5ef799060da860a5fb1f
Original PR description
Animated GIFs applied with an image shape freeze on Firefox and Safari when the shape's SVG is otherwise static. A dummy `<animateMotion dur="1ms" repeatCount="indefinite"/>` child on the <image> element keeps the animation running so the GIF plays. This hack was introduced in [1] but was missing from most shapes. This commit adds it to every <image> element that lacked it so any shape can be used with a GIF. task-5967171 [1]: https://github.com/odoo/odoo/commit/144e5ef799060da860a5fb1fbe0fd009919afe79 Forward-Port-Of: odoo/odoo#275259
Problem: After updating a file name, the link popover still shows the original file name. Cause: The link popover always displays the attachment name instead of the current link content. Solution: Use the link content as the popover title so it reflects the updated file title. Steps to reproduce: - Go to To-Do → Create New. - Upload a file. - Change its title. - Observe that the title shown in the link popover still uses the original file name. task-6213840 --- I confirm I
Original PR description
Problem: After updating a file name, the link popover still shows the original file name. Cause: The link popover always displays the attachment name instead of the current link content. Solution: Use the link content as the popover title so it reflects the updated file title. Steps to reproduce: - Go to To-Do → Create New. - Upload a file. - Change its title. - Observe that the title shown in the link popover still uses the original file name. task-6213840 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270817 Forward-Port-Of: odoo/odoo#264127
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#265573 Forward-Port-Of: odoo/odoo#263837
On touch devices (smartphones, tablets), the in-browser notification sound plays through the media audio channel, which is not affected by the device's silent mode. This causes unexpected sound playback when the device is set to silent. Suppress _playSound() on devices with maxTouchPoints > 1 (touch/mobile). Push notifications handle alerts on mobile and properly respect the device's silent mode. The side effect is that also laptops with touch screen are affected and need to use web push n
Original PR description
On touch devices (smartphones, tablets), the in-browser notification sound plays through the media audio channel, which is not affected by the device's silent mode. This causes unexpected sound playback when the device is set to silent. Suppress _playSound() on devices with maxTouchPoints > 1 (touch/mobile). Push notifications handle alerts on mobile and properly respect the device's silent mode. The side effect is that also laptops with touch screen are affected and need to use web push notifications in order to get notification sounds played. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275797
**Purpose of this PR:** Before this PR, the Voice detection sensitivity indicator in call settings could get stuck showing the last detected level after clicking "Stop" right after "Test". The `AudioWorkletNode`'s port kept receiving tic messages briefly after `disconnect()`, since disconnecting only unroutes the audio graph and does not stop the worklet from posting pending messages. <img width="546" height="73" alt="voice_test_bug" src="https://github.com/user-attachments/assets/05a10d23
Original PR description
**Purpose of this PR:** Before this PR, the Voice detection sensitivity indicator in call settings could get stuck showing the last detected level after clicking "Stop" right after "Test". The…
**Purpose of this PR:** Before this PR, the Voice detection sensitivity indicator in call settings could get stuck showing the last detected level after clicking "Stop" right after "Test". The `AudioWorkletNode`'s port kept receiving tic messages briefly after `disconnect()`, since disconnecting only unroutes the audio graph and does not stop the worklet from posting pending messages. <img width="546" height="73" alt="voice_test_bug" src="https://github.com/user-attachments/assets/05a10d23-fe60-4a85-b906-bfec6d235ec5" /> Steps to reproduce: 1. Open Voice & Video Settings. 2. Start the Voice detection sensitivity test. 3. Quickly click Stop immediately after clicking Test. 4. It may take a few tries, but eventually the Voice detection sensitivity indicator remains stuck at the last detected level. > [!NOTE] > this is timing-dependent. A tic message must already be in-flight from the worklet thread when `disconnect()` runs, so it won't happen every attempt. This race condition existed in the `disconnect` callback of `_loadAudioWorkletProcessor` since #66611, but stayed silent until #183969 introduced the Voice detection sensitivity feature in call settings, exposing it. This PR clears `port.onmessage` before disconnecting so late tic messages can no longer update the Voice detection sensitivity indicator after monitoring has stopped.
Since 414e55cf7c397, we can assign multiple users to a user-defined filter but because it's now a many2many, any user that got archived won't be shown in the `user_ids` fields anymore, it could mislead the filter being a global filter; whereas it's not. This commit also display archived users so we can see all users effectively assigned to the user-defined filter. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
Since 414e55cf7c397, we can assign multiple users to a user-defined filter but because it's now a many2many, any user that got archived won't be shown in the `user_ids` fields anymore, it could mislead the filter being a global filter; whereas it's not. This commit also display archived users so we can see all users effectively assigned to the user-defined filter. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Currently, overlays remain open when the POS screensaver is loaded [^1]. #### Steps to reproduce: - Open the POS interface. - Open a dropdown/popover menu (e.g., the navbar hamburger menu). - Wait for the screensaver (SaverScreen) to trigger due to inactivity. - The open dropdown menu remains visible on top of the screensaver. #### Issue Dropdowns and popovers are rendered as active overlays outside the main screen container. While the screensaver setup closes active dialogs, it doe
Original PR description
Currently, overlays remain open when the POS screensaver is loaded [^1]. #### Steps to reproduce: - Open the POS interface. - Open a dropdown/popover menu (e.g., the navbar hamburger menu). - Wait for the screensaver (SaverScreen) to trigger due to inactivity. - The open dropdown menu remains visible on top of the screensaver. #### Issue Dropdowns and popovers are rendered as active overlays outside the main screen container. While the screensaver setup closes active dialogs, it does not handle active overlays. #### Fix Retrieve the overlay service in SaverScreen and close all active overlays during its setup phase using a dedicated `closeAllOverlays` method. [^1]:  Forward-Port-Of: odoo/odoo#274716
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#275557 Forward-Port-Of: odoo/odoo#274051
**Steps to reproduce:** - activate subcontracting in the settings - create a storable product 'comp' with categ avco auto - confirm a PO and validate receipt for 1 comp at 10 - create a storable product 'prod' with categ avco auto - in the purchase tab set a vendor with a price of 30 - create a subcontracting bom with the same vendor as as the subcontractor and 1 quantity of our comp products in the components - confirm a PO for 1 prod with the subcontractor as the vendor - on
Original PR description
**Steps to reproduce:** - activate subcontracting in the settings - create a storable product 'comp' with categ avco auto - confirm a PO and validate receipt for 1 comp at 10 - create a storable…
**Steps to reproduce:** - activate subcontracting in the settings - create a storable product 'comp' with categ avco auto - confirm a PO and validate receipt for 1 comp at 10 - create a storable product 'prod' with categ avco auto - in the purchase tab set a vendor with a price of 30 - create a subcontracting bom with the same vendor as as the subcontractor and 1 quantity of our comp products in the components - confirm a PO for 1 prod with the subcontractor as the vendor - on the receipt, click on 'record component and change the 'quantity' to 2 then click on record production (you might need to click anywhere on the form to have the 'record production' appear) - validate the receipt - create and confirm the Bill for the 2 quantity - open valuation view **Current behavior:** an extra stock valuation layer was created with no quantity, no reference and a total value of 20 **Expected behavior:** This extra layer is not needed, the two svl for the receipt are made with a quantity of 2 and so is the bill, so the amls created are as follow : - svl comp : - credit 20 stock valuation - debit 20 cost of production - svl prod : - debit 80 stock valuation - credit 60 stock interim received - credit 20 cost of production - amls from the bill : - 60 stock interim received - 60 account payable After all of this the result is : debit 60 in stock valuation credit 60 account payable which is what we want The extra layer is not needed and creates unbalance, its amls are : credit 20 stock interim received debit 20 stock valuation **Cause of the issue:** This extra svl comes from the _apply_price_difference() method https://github.com/odoo/odoo/blob/deeecf7cd02e7383b591835b0c6495e3ddead0ff/addons/purchase_stock/models/account_invoice.py#L131 This method creates svl and/or amls to compensate difference between the bill price and the PO price (see https://github.com/odoo/odoo/pull/126536 for more details) To check if there is a difference, we compute the layer_price_unit https://github.com/odoo/odoo/blob/deeecf7cd02e7383b591835b0c6495e3ddead0ff/addons/purchase_stock/models/account_move_line.py#L140 and pass it as parameter to _prepare_pdiff_vals() https://github.com/odoo/odoo/blob/deeecf7cd02e7383b591835b0c6495e3ddead0ff/addons/purchase_stock/models/account_move_line.py#L159 were it will be compared to the aml price https://github.com/odoo/odoo/blob/deeecf7cd02e7383b591835b0c6495e3ddead0ff/addons/purchase_stock/models/account_move_line.py#L239-L244 In the case of a subcontracted product, price unit of the bill (30 in our case) should be the price unit of the svl of the subcontracted product (40) - the price of the comps for one unit of the subcontracted product (10). That's why when we call _get_layer_price_unit, there is a mrp_subcontracting_purchase override to remove the component price from the svl of the subcontracted product. https://github.com/odoo/odoo/blob/deeecf7cd02e7383b591835b0c6495e3ddead0ff/addons/mrp_subcontracting_purchase/models/stock_valuation_layer.py#L18-L20 To get the price of the components for on unit of the subcontracted product, we divide the value of the components svls by production.product_uom_qty, but that is not necessarily the same number as the quantity of the svl of the subcontracted product (in most cases yes but not in the case of our steps to reproduce for instance). In terms of account move lines the quantity of the svl is the one that maters so that's the one that we should take into account also in this computation. opw-6191829 Forward-Port-Of: odoo/odoo#271467
Miscellaneous changes
This reverts commit 787223c. In the point of sale, concurrent sales of the same product can happen, e.g. if several physical points of sales are open at the same time, each with their own session. In the case where the underlying product's valuation is tracked automatically and perpetually, the creation of the pos order leads to the creation of the stock picking, which in turn leads to a search for the next available svl. Because of the flush_all, this can cause lots of retry failures and ass
Original PR description
This reverts commit 787223c. In the point of sale, concurrent sales of the same product can happen, e.g. if several physical points of sales are open at the same time, each with their own session. In the case where the underlying product's valuation is tracked automatically and perpetually, the creation of the pos order leads to the creation of the stock picking, which in turn leads to a search for the next available svl. Because of the flush_all, this can cause lots of retry failures and associated delay/latency/overall perceived slowness for the end user. opw-6206709 Forward-Port-Of: odoo/odoo#275264
10 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
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
Resolved issues and error corrections
steps to reproduce: ----------- - install the industry_fsm_repair module - create a product with type goods and enable `create repair` - enable `create repair orders` from returns in delivery orders in operation types - create a sale order with an fsm product and a goods product - confirm the sale order and validate the delivery - return the delivery and validate it (wh/in/0000x) - open the return receipt (wh/in/0000x) and create a repair - create an fsm user without sto
Original PR description
steps to reproduce: ----------- - install the industry_fsm_repair module - create a product with type goods and enable `create repair` - enable `create repair orders` from returns in delivery orders…
steps to reproduce: ----------- - install the industry_fsm_repair module - create a product with type goods and enable `create repair` - enable `create repair orders` from returns in delivery orders in operation types - create a sale order with an fsm product and a goods product - confirm the sale order and validate the delivery - return the delivery and validate it (wh/in/0000x) - open the return receipt (wh/in/0000x) and create a repair - create an fsm user without stock access - log in with the fsm user - open a task, go to `pick up`, and open the stock move - open the repair order issue: -------- the fsm user does not have access to stock lots and repair tags, which causes an access error. fix: ---- added the stock user group to the repair tags and stock lot fields so that users without the stock user group cannot access them. technical: ------ in the stable version, i did not extend the repair view in the industry_fsm_repair module. instead, i added the group directly in the repair module. In master, the group is added through the industry_fsm_repair module. task-6032336
opw-6368979 Description of the issue/feature this PR addresses: Update the Worldline Cofidis payment method mapping to match the latest payment product ID defined in the Worldline documentation. Current behavior before PR: The Cofidis payment method was mapped to the outdated payment product ID (3012), causing payment requests to use an incorrect mapping. Desired behavior after PR is merged: The Cofidis payment method is mapped to the correct payment product ID (5129) as per
Original PR description
opw-6368979 Description of the issue/feature this PR addresses: Update the Worldline Cofidis payment method mapping to match the latest payment product ID defined in the Worldline documentation. Current behavior before PR: The Cofidis payment method was mapped to the outdated payment product ID (3012), causing payment requests to use an incorrect mapping. Desired behavior after PR is merged: The Cofidis payment method is mapped to the correct payment product ID (5129) as per the latest Worldline documentation, ensuring payment requests use the correct mapping. Forward-Port-Of: odoo/odoo#275881
Description of the issue/feature this PR addresses: Current behavior before PR: meeting.rrule is stored as a full dateutil rrule string, e.g.: "DTSTART:20250218T113209\nRRULE:FREQ=YEARLY;COUNT=720" Passing the full multi-line string as a single RRULE property value causes vobject to emit two RRULE lines, where the first one ("RRULE:DTSTART:...") has no FREQ. This is not standard-compliant and is rejected by calendar clients (e.g. Thunderbird: "invalid frequency null"). Desired behavior af
Original PR description
Description of the issue/feature this PR addresses:
Current behavior before PR: meeting.rrule is stored as a full dateutil rrule string, e.g.: "DTSTART:20250218T113209\nRRULE:FREQ=YEARLY;COUNT=720"
Passing the full multi-line string as a single RRULE property value causes vobject to emit two RRULE lines, where the first one ("RRULE:DTSTART:...") has no FREQ. This is not standard-compliant and is rejected by calendar clients (e.g. Thunderbird: "invalid frequency null").
Desired behavior after PR is merged: Only a single RRULE line is generated.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prMicrosoft issues a new refresh token on every access token refresh (rolling 90-day sliding window). The previous code discarded it, causing users to be forced to re-authenticate every 90 days once the original token expired. Closes #253543 Forward-Port-Of: odoo/odoo#268284
Original PR description
Microsoft issues a new refresh token on every access token refresh (rolling 90-day sliding window). The previous code discarded it, causing users to be forced to re-authenticate every 90 days once the original token expired. Closes #253543 Forward-Port-Of: odoo/odoo#268284
Issue: --- If a product template has dynamic attributes, some variants might not exist. For those variants, we are showing wrong stock in the website. To reproduce: 1- Create a product with a dynamic attribute and two values. 2- Publish the product and uncheck sell when out-of-stock and check show product when the qty is less than 5. 3- Create a purchase order with qty = 4 for the first value, so a variant is created for it. 4- Go to the website shop. Open the product. 4 available qty i
Original PR description
Issue: --- If a product template has dynamic attributes, some variants might not exist. For those variants, we are showing wrong stock in the website. To reproduce: 1- Create a product with a dynamic…
Issue: --- If a product template has dynamic attributes, some variants might not exist. For those variants, we are showing wrong stock in the website. To reproduce: 1- Create a product with a dynamic attribute and two values. 2- Publish the product and uncheck sell when out-of-stock and check show product when the qty is less than 5. 3- Create a purchase order with qty = 4 for the first value, so a variant is created for it. 4- Go to the website shop. Open the product. 4 available qty in stock is shown for the first variant which is correct. 5- Select 2nd variant. As you see, still 4 available qty is shown which is wrong. As the out-of-stock sale is unchecked, an out-of-stock warning should be shown. Cause and Fix: --- This is due to `isMainProduct` being always False when `product_id` is not set which makes `free_qty` and `out_of_stock` not to be updated. Also in `get_combination_info_website`, `is_storable` value is set to variant `is_storable` field which will be False if product variant is not exist. opw-6237602 Forward-Port-Of: odoo/odoo#273104
### Steps to reproduce the issue: 1. Download Accounting 2. Go to one move type (ex. customer invoices, vendor bills, etc.) 3. Select some records, click the wheel button and then export ZIP 4. Error raised: Nothing to export ### Cause of the issue: Commit 438603ac forward-ported the zip export feature from v17 to v18, but failed to adapt the action_export_zip function and import the controller route. ### Reason to introduce the fix: It has been decided to completly remove the butto
Original PR description
### Steps to reproduce the issue: 1. Download Accounting 2. Go to one move type (ex. customer invoices, vendor bills, etc.) 3. Select some records, click the wheel button and then export ZIP 4. Error raised: Nothing to export ### Cause of the issue: Commit 438603ac forward-ported the zip export feature from v17 to v18, but failed to adapt the action_export_zip function and import the controller route. ### Reason to introduce the fix: It has been decided to completly remove the button EXPORT ZIP since we already have other buttons that download/export the PDFs in a zip file (ex. button Download PDF). opw-6313750 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Steps: - Enable `pos_hr` and configure employees - Open the POS - Log in as an employee - Open the burger menu in the navbar Issue: - The "Create Product" menu is not visible immediately after the employee logs in. - It only appears after refreshing the POS. Cause: - The visibility of the menu is determined when `Navbar` component is mounted. - Since the `Navbar` is mounted only once when the POS UI loads, the value is not updated after employee login. Fix: - Replace the asynch
Original PR description
Steps: - Enable `pos_hr` and configure employees - Open the POS - Log in as an employee - Open the burger menu in the navbar Issue: - The "Create Product" menu is not visible immediately after the employee logs in. - It only appears after refreshing the POS. Cause: - The visibility of the menu is determined when `Navbar` component is mounted. - Since the `Navbar` is mounted only once when the POS UI loads, the value is not updated after employee login. Fix: - Replace the asynchronous permission check with a getter that evaluates product creation rights. - Cache the group access information in `posService` and let the hr override use the getter. Task-6361787 Related PR: https://github.com/odoo/enterprise/pull/123073
Rather than assign `log_target` and blow up if the third branch is taken, use a `defaultdict` and just increment the count in each branch on hit. The continue in the third branch is not strictly necessary, but that way it protects us if anyone decides to add post-processing which also doesn't account for the third branch.
Original PR description
Rather than assign `log_target` and blow up if the third branch is taken, use a `defaultdict` and just increment the count in each branch on hit. The continue in the third branch is not strictly necessary, but that way it protects us if anyone decides to add post-processing which also doesn't account for the third branch.
Miscellaneous changes
This reverts commit 787223c. In the point of sale, concurrent sales of the same product can happen, e.g. if several physical points of sales are open at the same time, each with their own session. In the case where the underlying product's valuation is tracked automatically and perpetually, the creation of the pos order leads to the creation of the stock picking, which in turn leads to a search for the next available svl. Because of the flush_all, this can cause lots of retry failures and ass
Original PR description
This reverts commit 787223c. In the point of sale, concurrent sales of the same product can happen, e.g. if several physical points of sales are open at the same time, each with their own session. In the case where the underlying product's valuation is tracked automatically and perpetually, the creation of the pos order leads to the creation of the stock picking, which in turn leads to a search for the next available svl. Because of the flush_all, this can cause lots of retry failures and associated delay/latency/overall perceived slowness for the end user. opw-6206709 Forward-Port-Of: odoo/odoo#275264
7 changes
Resolved issues and error corrections
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
Before this commit: If a user, with crm.leads linked to it, decided to request a password reset AND during that password reset process decided to activate the google oauth for their account, it would cause a crash. The reason is, during the password rest + oauth activation, self.env.user is an empty record set, which obviously will fail during the _is_portal check, due to its call to ensure_one() opw-6347228
Original PR description
Before this commit: If a user, with crm.leads linked to it, decided to request a password reset AND during that password reset process decided to activate the google oauth for their account, it would cause a crash. The reason is, during the password rest + oauth activation, self.env.user is an empty record set, which obviously will fail during the _is_portal check, due to its call to ensure_one() opw-6347228
... opw-6270530 closes #268601
Original PR description
... opw-6270530 closes #268601
Steps to produce: 1) Install Manufacturing & enable "Work Order Dependencies" in the settings 2) Make a new BOM with 2 operations 3) Check "Operation Dependencies" in the miscellaneous tab on the BOM's form view. 4) Configure the first sequential operation to be blocked by the second 5) Make and confirm an MO using this BOM 6) Uncheck "Operation Dependencies" on the BOM 7) Press "Plan" on the MO, a validation error is thrown stating "You cannot create cyclic dependency." Issue occur
Original PR description
Steps to produce: 1) Install Manufacturing & enable "Work Order Dependencies" in the settings 2) Make a new BOM with 2 operations 3) Check "Operation Dependencies" in the miscellaneous tab on the…
Steps to produce: 1) Install Manufacturing & enable "Work Order Dependencies" in the settings 2) Make a new BOM with 2 operations 3) Check "Operation Dependencies" in the miscellaneous tab on the BOM's form view. 4) Configure the first sequential operation to be blocked by the second 5) Make and confirm an MO using this BOM 6) Uncheck "Operation Dependencies" on the BOM 7) Press "Plan" on the MO, a validation error is thrown stating "You cannot create cyclic dependency." Issue occurs because after the MO is confirmed the blocked_by_workorder_ids field for mrp.workorder records is set based on the order manually configured on the BOM (operation 1 is blocked by operation 2). After the BOM is edited to have allow_operation_dependencies = false, then Odoo uses the default sequential ordering when planning the operations (operation 2 is blocked by operation 1). Since the old ordering is never cleared, a cycle is created unintentionally. This PR resolves this issue by clearing the blocked_by_workorder_ids field on mrp.workorder records. [Ticket](https://www.odoo.com/odoo/project/49/tasks/6334271?debug=assets) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
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
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
Note: In odoo all date/datetime fields are stored and computed by default as UTC Before this commit, dates were called using local timezone getters. This caused the time returned from web to be shifted by the timezone as the dates returned would be treated as UTC. After this commit, dates are now called using UTC timzone getters. Now all web times are retrived as UTC and in sync with the rest of the odoo fields and computations. task-6271421
Original PR description
Note: In odoo all date/datetime fields are stored and computed by default as UTC Before this commit, dates were called using local timezone getters. This caused the time returned from web to be shifted by the timezone as the dates returned would be treated as UTC. After this commit, dates are now called using UTC timzone getters. Now all web times are retrived as UTC and in sync with the rest of the odoo fields and computations. task-6271421
When searching on the Website site, using the main search on the navbar, it opens a list view with the results (`website.list_hybrid`) which raises the warning on the logs (2 times): "Unknown directives or unused attributes: {'t-key'} in website.list_hybrid"  This happens after the attribute `t-key` was added to the template [\[1\]] because the template is only use
Original PR description
When searching on the Website site, using the main search on the navbar, it opens a list view with the results (`website.list_hybrid`) which raises the warning on the logs (2 times): "Unknown…
When searching on the Website site, using the main search on the navbar, it opens a list view with the results (`website.list_hybrid`) which raises the warning on the logs (2 times):
"Unknown directives or unused attributes: {'t-key'} in website.list_hybrid"

This happens after the attribute `t-key` was added to the template [\[1\]] because the template is only used in QWeb. The validation for them doesn't include the `t-key` [\[2\]] as one of the "iter_directives" nor has a `_compile_directive_*` method to check and remove it from the validation as it's done with the `t-as` and `t-foreach`.
This also causes the raise of the warnings on tours that use the tour method `searchProduct` (of the module `website_sale`) because it uses the first input with the name of search and happens to be the search on the navbar.

[\[1\]]: https://github.com/odoo/odoo/commit/7b1d82aa
[\[2\]]: https://github.com/odoo/odoo/blob/f52cfb09/odoo/addons/base/models/ir_qweb.py#L1400
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr