Daily updates from Odoo
Friday, August 14, 2026
38 changes · saas-19.3
Resolved issues and error corrections
Weekly rentals selected for exactly one week were sometimes counted as two weeks because default pickup and return times made the period slightly longer than seven days. The fix aligns those default times for weekly rentals so customers see the correct rental duration and price.
Original PR description
A product with a weekly rental periodicity is booked for 2 weeks when we actually book it for a single week Steps to reproduce: 1. Install Rental and eCommerce 2. Go to Rental > Products and create a new product 'test', in the Sales tab, set the rental periodicity to 'Weeks' 3. Click on the smart button 'Go to Website' 4. Change the rental period so that it exactly covers a week (e.g. from Monday to Monday) 5. The website shows that you're booking for 2 weeks Issue: The default pickup time is 9h and the default return time is 18h. When we select exactly one week for the rental duration, the true duration of the rental is greater than 1 week (because of the pickup and return time) so it is rounded as 2 weeks. Solution: Also swap `pickup_time` and `return_time` when swapping from weeks periodicity. opw-6397786
Fixes an issue that could prevent users from exporting audit report articles as PDFs in environments using the newer PDF processing backend. This restores reliable PDF export behavior and avoids server errors during a business reporting workflow.
Original PR description
…ern pypdf backend `export_article_to_pdf` calls `writer.setPageMode("/UseOutlines")` on the `PdfFileWriter` instance to make the outline/bookmarks panel visible by default when the generated PDF is…
…ern pypdf backend
`export_article_to_pdf` calls `writer.setPageMode("/UseOutlines")` on the `PdfFileWriter` instance to make the outline/bookmarks panel visible by default when the generated PDF is opened. the old PyPDF2
`odoo.tools.pdf` picks its backend dynamically it first tries to import PyPDF2==2.12.1 and only falls back to the modern `pypdf` library if PyPDF2 is not importable, which is the case on Python 3.13 per requirements.txt, or on any worker where PyPDF2 failed to install.
The PyPDF2-based writer still exposes `setPageMode`, so the bug was never seen on backends using it. The modern pypdf-based writer replaced that method with a `page_mode` property (getter/setter) and never kept a camelCase alias for it
As a result, any request hitting this code got a beautiful HTTP 500
`AttributeError: 'BrandedFileWriter' object has no attribute 'setPageMode'`
`setPageMode` is not called anywhere else so
this fixes the call site directly instead of adding a new alias to the shared _pypdf.py
opw-6382905Belgian point-of-sale self-orders are now signed using the designated self-ordering user, so mobile orders can be validated even when no cashier is logged in. This prevents fiscal device rejections and keeps self-order sales compliant while printing remains handled separately.
Original PR description
In this PR (github.com/odoo/enterprise/pull/126611) we removed the blackbox printingQueue, cause the pritning is now handled via Obox. - This commit reintroduce a signingQueue, but only for self-orders, as the printing is now handled via Obox. - It also fix the issue where signing a mobile self-order while no cashier is connected (login screen) was rejected by the FDM: signExternalOrder took the INSZ number from getCashier(), so signSale was sent without its required employeeId. - Self-orders are now signed with the INSZ number of the self-ordering default user, like the kiosk already does. FW of : https://github.com/odoo/enterprise/pull/126695
The Timesheet Assistant now recognizes time spent checking the Discuss inbox and labels the suggestion as “Checking Inbox.” This makes timesheet suggestions clearer and easier for users to understand.
Original PR description
## Previous Behavior When the Timesheet Assistant detected a user spending time in their Discuss inbox, it generated a suggestion labeled "Discussing in/with Inbox". The name of this suggestion was judged to not make much sense. ## New Expected Behavior When the Timesheet Assistant detects a user spending time in their Discuss inbox, it will now generate a suggestion labeled "Checking Inbox" due to a new assistant rule. task-[6420655](https://www.odoo.com/odoo/project/4105/tasks/6420655)
Creating an employee declaration without selecting an employee no longer triggers an application error. This prevents an avoidable interruption in Belgian payroll reporting and makes the declaration workflow more reliable.
Original PR description
When creating an employee declaration without selecting an employee, a traceback occurs. Steps to reproduce the error: - Install ``l10n_be_hr_payroll`` module with demo data - Switch to Belgian…
When creating an employee declaration without selecting an employee, a traceback occurs. Steps to reproduce the error: - Install ``l10n_be_hr_payroll`` module with demo data - Switch to Belgian company - Go to Payroll > Reporting > Individual Accounts > Create a new Individual Account > Click on Eligible Employees > Create a new employee declaration without employee > Save Traceback: ```py ValueError: Expected singleton: hr.employee() ``` https://github.com/odoo/enterprise/blob/000544c3d5b93e194264e15bb73d9599525106e3/hr_payroll/models/hr_payroll_employee_declaration.py#L71 The ``_compute_version_id()`` method calls ``_get_version()``. When ``employee_id`` is empty, ``_get_version()`` is invoked on an empty ``hr.employee`` record, and its ``ensure_one()`` call raises the above traceback at [1]. [1]: https://github.com/odoo/odoo/blob/3c358ae2badad69b125695a97b4a14e8ab77fccd/addons/hr/models/hr_employee.py#L745-L750 sentry-7625826444 Forward-Port-Of: odoo/enterprise#125175
Spanish VAT record book exports now work for accounting users even when the report includes Point of Sale data. This prevents access errors during tax reporting while keeping the POS data use limited to building the report.
Original PR description
Steps to reproduce:
- With an ES Company
- Open a POS session, add product with tax and pay
- As a user with only accounting access
- Go to Accouting > Reporting > Tax report
- Select Generic Tax report
- Print "VAT record Books"
Issue:
An AccessError will raise
```
Access Error
You are not allowed to access 'Point of Sale Session' (pos.session) records.
This operation is allowed for the following groups:
- Point of Sale/User
Contact your administrator to request access if necessary.
```
Analysis:
Vat Record Books handler for POS needs to read pos.session and pos.order records. Currently, the action is performed with the rights of the user running the report, so accounting-only user face an error.
As POS records are only read internally to build the report, we add sudo call to get the data.
opw-5862529
Forward-Port-Of: odoo/enterprise#126590
Forward-Port-Of: odoo/enterprise#125980French VAT reimbursement declaration 3519 now includes the bank account holder's name in the account details section. This helps meet filing requirements and reduces the risk of rejected or incomplete reimbursement declarations.
Original PR description
For reimbursement declarations, the name of the holder of the account is required This commits adds holder's name to the account data zone no-task-id Forward-Port-Of: odoo/enterprise#127708 Forward-Port-Of: odoo/enterprise#127554
The Timesheet Assistant no longer drops very short calendar events when building time suggestions. If a short event matches a larger event by name and group, its time is added to the related suggestion so totals are more accurate.
Original PR description
## Previous Behavior Before this PR: When events were to small to suggestion Timesheet Assistant would completely discard these events. This lead to a suggestion haveing a lower total time than it should. ## New Expected Behavior After this PR: When an event is too small to suggest and shares its name and group with one or more larger event, the duration of the smaller event is added to the last event with the same name and groupe. task-[6452987](https://www.odoo.com/odoo/project/4105/tasks/6452987)
Managers without HR permissions can now launch appraisal campaigns for employees they manage. The fix also prevents campaigns from accidentally applying to all employees when selected employee records were not readable by the user.
Original PR description
**Issue**: -User without hr rights is not allowed to launch campaigns for employees under him in the hierarchy. -This issue appears only in master, but it discovered another issue from 19.3, where the value for `employee_ids` was not accessed by the normal user. Thus, appraisals are created for all employees in the list, because not selecting an employee means selecting "All Employees". **Solution**: -SUDOing the read to allow the compute to see what the user has selected.
This fixes an issue where users without Accounting permissions could be blocked from confirming sales orders when a Studio approval rule used certain customer follow-up fields. Approval checks now run with the right elevated access so normal business workflows are not interrupted by unrelated permission limits.
Original PR description
continuation of [PR](https://github.com/odoo/enterprise/pull/121856) Issue: Inside _get_approval_spec filtered_domain is called a few times and due to a related field that calls an access rights group that the user who used the action isnt apart of is blocked by the filtered_domain. To Replicate: 1) Install studio, sale, Accounting and make sure "account_followup" is installed 2) create a related field on the sales.order form related to "customer -> follow up status" 3) Save 4) Create a "Studio Approval Rule" (studio.approval.rule) with a domain using the new related studio field -> method : "action_confirm" -> approver:admin 5)create a test user with no accounting access rights 6) in an incognito browser try and create a sales order, and then confirm it. it will throw the access rights error Solution: Go one up the stack where _get_approval_spec is called and add a syudo for those calls opw-6316069 Forward-Port-Of: odoo/enterprise#127412
Recruitment officers without payroll permissions can now generate and open Belgian salary offers without running into an access error. This fixes a workflow blocker caused by a payroll-related warning check that used information recruiters are not allowed to read directly.
Original PR description
The field `hr.contract.salary.offer.l10n_be_is_below_scale_warning` is accessible to users outside of the payroll groups but its computation requires a read access to payroll only fields on the related `hr.version`. Steps to reproduce: 1- log in with a user that has the group Recruitment/ Officer, and no payroll privileges. 2- go to an applicant and click generate offer 3- The offer will be created but the user will not have access to it and the redirection will fail reported by our recruitment officers
Submitting Australian Single Touch Payroll records without payslips or employees now shows a clear validation message instead of causing an unexpected error. This helps payroll users understand what information is missing before sending data to the ATO.
Original PR description
When submitting payroll data to the ATO from an STP record without any payslips or employees, a traceback is raised. Steps to reproduce the error: - Install ``l10n_au_hr_payroll_account`` module -…
When submitting payroll data to the ATO from an STP record without any payslips or employees, a traceback is raised. Steps to reproduce the error: - Install ``l10n_au_hr_payroll_account`` module - Switch to ``My australian Company`` - Go to Payroll > Configuration > Settings > In Australian Localization, Set BMS ID > Set STP Responsible and his date of birth - Go to Payroll > Reporting > Single Touch Payroll > Create a new record > Set Payment Date > Submit to ATO > Sign & Submit to ATO Traceback: ```py IndexError: tuple index out of range ``` https://github.com/odoo/enterprise/blob/b0e48baaf99bdc4faefd2ffdd3bd5637fb548593/l10n_au_hr_payroll_account/models/l10n_au_stp.py#L228-L233 The traceback occurs because ``_get_fiscal_year_start()`` assumes that the STP record always contains at least one payslip or one employee. When these recordsets are empty, indexing the first element raises an IndexError. Solution: This commit validates that the required payslips or employees are present before the submission and raises a validation error instead of a traceback. Forward-Port-Of: odoo/enterprise#127733 Forward-Port-Of: odoo/enterprise#124096
WhatsApp channel members can once again access Advanced Settings to manage membership. This fixes a regression in version 19.3 where the option was hidden, preserving a familiar and more suitable workflow for WhatsApp channels.
Original PR description
Show the advanced-settings thread action for WhatsApp channel members, since channel_role is only supported on channel/group types which controls this button. Advanced Settings have long been used to manage channel membership. Their removal in 19.3+ introduced a regression, while channel roles are more cumbersome and do not integrate well with WhatsApp concern similarly to livechat: https://github.com/odoo/enterprise/pull/112959#discussion_r3593831089. so keeping Advanced Settings for WhatsApp for now.
Payroll configuration now only shows Mexico-specific CFDI settings when the active company is based in Mexico. This prevents irrelevant tax invoicing options from appearing for companies in other countries, reducing confusion during setup.
Original PR description
Steps to reproduce: 1. Switch to a non-Mexican company. 2. Go to Payroll > Configuration > Settings. 3. The CFDI settings block is visible. Reason: The CFDI block was missing a country check. Solution: Restrict the CFDI block visibility to Mexican companies. Task-6448440 Forward-Port-Of: odoo/enterprise#127022
This fix prevents batch invoice sending to SUNAT from being blocked when some Peruvian invoices contain lines without taxes. Instead of a generic technical failure, the system handles the issue consistently with single-invoice sending so other invoices can continue processing.
Original PR description
In l10n_pe_edi, invoices containing lines without tax can't be submitted to SUNAT.
When sending a single invoice, an error message is displayed. However, sending multiple invoices processes them in the background by a cron job. In this case, EDI document creation fails without error handling, raising a generic parsing error and blocking the cron from processing other invoices.
Steps to reproduce:
1. Create and post two invoices with no tax on some lines.
2. From the list view, select both invoices and click "Send" and mark "SUNAT".
3. An exception is raised: `ValueError: XMLSyntaxError("Start tag expected, '<' not found, line 1, column 1")`.
opw-6390480
Forward-Port-Of: odoo/enterprise#125143When edit_translations is set, convert_to_record wraps translated terms in branding spans. Related (non-stored) fields re-read that already-wrapped value and ran the same wrapping again, producing nested spans. Only wrap terms for stored fields so related Html inherits the source branding unchanged. Also keep data-oe-translation-state in HTML safe_attrs so sanitization does not strip it. Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior af
Original PR description
When edit_translations is set, convert_to_record wraps translated terms in branding spans. Related (non-stored) fields re-read that already-wrapped value and ran the same wrapping again, producing nested spans. Only wrap terms for stored fields so related Html inherits the source branding unchanged. Also keep data-oe-translation-state in HTML safe_attrs so sanitization does not strip it. 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#281729 Forward-Port-Of: odoo/odoo#280397
Steps to reproduce: --- - Install `website_sale_collect` without demo data. - Create and publish a product. - Add the product to the cart and proceed to checkout. - Complete the main address. - Select a `Pick Up in Store` delivery method and click checkout. Issue: --- The checkout redirects back to the address form instead of continuing to the payment step. Root cause: --- During checkout, `shop/checkout`[1] calls `_check_cart_and_addresses()`, which eventually invokes `_check_ad
Original PR description
Steps to reproduce: --- - Install `website_sale_collect` without demo data. - Create and publish a product. - Add the product to the cart and proceed to checkout. - Complete the main address. -…
Steps to reproduce: --- - Install `website_sale_collect` without demo data. - Create and publish a product. - Add the product to the cart and proceed to checkout. - Complete the main address. - Select a `Pick Up in Store` delivery method and click checkout. Issue: --- The checkout redirects back to the address form instead of continuing to the payment step. Root cause: --- During checkout, `shop/checkout`[1] calls `_check_cart_and_addresses()`, which eventually invokes `_check_addresses()`[2]. That method then calls `_check_delivery_address()`[3] to verify that all mandatory delivery address fields are present. When db is initialized without demo data, the pickup location address may not contain all mandatory fields (such as ZIP code). As a result, the validation fails and the checkout incorrectly redirects the customer back to the address form, even though the delivery address is a pickup location that should not be edited by the customer. Solution: --- Override `_can_be_edited_by_current_customer()` to treat the selected pickup location as a non-editable address. Since the pickup location belongs to the store, it does not make sense to ask the customer to edit or complete its address. This prevents the checkout flow from requesting address completion and allows the customer to proceed directly to the payment step. [1]https://github.com/odoo/odoo/blob/815de3f1a43bccdb5714436da2060f7a45aa385e/addons/website_sale/controllers/main.py#L1141-L1142 [2]https://github.com/odoo/odoo/blob/815de3f1a43bccdb5714436da2060f7a45aa385e/addons/website_sale/controllers/main.py#L1894-L1895 [3]https://github.com/odoo/odoo/blob/815de3f1a43bccdb5714436da2060f7a45aa385e/addons/website_sale/controllers/main.py#L1945 opw-6394166 I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Issue: --- Adding a pickup-in-store delivery method to a sales order from the backend `Add shipping` wizard and clicking on the pickup point selector raises: Invalid props for component `LocationSelectorDialog`: `countryId` is not a number. Steps to reproduce: 1- Create a SO with a partner without country set. 2- Enable debug mode. 3- Use `Add shipping` wizard and choose pick-up delivery method. 4- Open the location selector. Cause: --- `PickupLocationMany2OneField.countryId` re
Original PR description
Issue: --- Adding a pickup-in-store delivery method to a sales order from the backend `Add shipping` wizard and clicking on the pickup point selector raises: Invalid props for component `LocationSelectorDialog`: `countryId` is not a number. Steps to reproduce: 1- Create a SO with a partner without country set. 2- Enable debug mode. 3- Use `Add shipping` wizard and choose pick-up delivery method. 4- Open the location selector. Cause: --- `PickupLocationMany2OneField.countryId` returns the `id` of `this.partnerRecord.country_id` which is `false` when the company is not set. This can be fixed by a safe optional chain access. opw-6321167
Steps to reproduce: - give a user the Point of Sale / User group only, without any accounting access right - open a session, sell a product to a customer and ask for an invoice Issue: The invoice is created and posted, but it is never reconciled with its payment: it stays fully due even though the journals and the accounts are correctly configured. Cause: _reconcile_invoice_payments gathers the lines to reconcile with the rights of the caller and elevates only the final reconcile() cal
Original PR description
Steps to reproduce: - give a user the Point of Sale / User group only, without any accounting access right - open a session, sell a product to a customer and ask for an invoice Issue: The invoice is…
Steps to reproduce:
- give a user the Point of Sale / User group only, without any accounting access right
- open a session, sell a product to a customer and ask for an invoice
Issue:
The invoice is created and posted, but it is never reconciled with its payment: it stays fully due even though the journals and the accounts are correctly configured.
Cause:
_reconcile_invoice_payments gathers the lines to reconcile with the rights of the caller and elevates only the final reconcile() call. The payment moves it reads are linked to the order through account.move.pos_payment_ids, while rule_invoice_pos_user restricts a POS user to the moves matching [('pos_order_ids', '!=', False)], so those moves are not readable by a salesperson. Since saas-19.3, base_user_account_move_line_rule adds [('move_id', 'access', 'read')] on account.move.line, which propagates that restriction to the lines: payment_moves.pos_payment_ids.account_move_id.line_ids silently returns an empty recordset. reconcile() then gets the invoice receivable line alone and has nothing to match it with.
_create_payment_moves does return a sudo recordset, but _generate_pos_order_invoice accumulates it into self.env['account.move'] and the union of two recordsets keeps the env of its left operand, so the payment moves reach the helper back in the salesperson's env.
opw-6439389
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prThe field pack_lot_ids is not present in the pos.order.line model when pos_stock is not installed, so we need to remove it from the test. runbot-941527 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
The field pack_lot_ids is not present in the pos.order.line model when pos_stock is not installed, so we need to remove it from the test. runbot-941527 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Before this commit, a week off on a schedule mixing working time and absence attendances, like a partial incapacity, was counted as 5 days instead of 2.5. The hours worked on a day were compared to hours_per_day, which already leaves the absence attendances out, so a worked morning looked like a full day. After this commit, the absence attendances are taken back in that comparison, so a worked morning counts as half a day again. taskid-6453601
Original PR description
Before this commit, a week off on a schedule mixing working time and absence attendances, like a partial incapacity, was counted as 5 days instead of 2.5. The hours worked on a day were compared to hours_per_day, which already leaves the absence attendances out, so a worked morning looked like a full day. After this commit, the absence attendances are taken back in that comparison, so a worked morning counts as half a day again. taskid-6453601
Before this commit, `waitStoreFetch` returns before the answer is in the store: right after `waitStoreFetch("channels_as_member")`, the store holds no record for a channel that answer carries, on a hundred runs out of a hundred. A test that then asserts on the fetched data depends on timing. This happens because `listenStoreFetch` steps from the `onRpc` callback, which runs before the route is served. The `microTick` at the end of `waitStoreFetch` is meant to cover the rest of the round trip,
Original PR description
Before this commit, `waitStoreFetch` returns before the answer is in the store: right after `waitStoreFetch("channels_as_member")`, the store holds no record for a channel that answer carries, on a hundred runs out of a hundred. A test that then asserts on the fetched data depends on timing.
This happens because `listenStoreFetch` steps from the `onRpc` callback, which runs before the route is served. The `microTick` at the end of `waitStoreFetch` is meant to cover the rest of the round trip, but the answer only reaches the store six microtasks later.
This commit steps from `Store.fetchStoreData` instead, whose promise resolves once the answer is in the store, and drops the tick. The `onRpc` option keeps its route hooks, as tests use it to delay a request.
Forward-Port-Of: odoo/odoo#282155
Forward-Port-Of: odoo/odoo#281499**ISSUE** When running` _cron_migrate_local_to_cloud_storage` manually,` cron._trigger` is called, which schedules the cron to be triggered later by a worker. To check whether the cron is being run manually or not, we check if there is a request. In SaaS, staging, and duplicate databases, the request is only truthy when the cron is run manually and falsy when run by a worker. In a SH production database, however, the request is truthy both when run manually and when run by a worker due to a spe
Original PR description
**ISSUE** When running` _cron_migrate_local_to_cloud_storage` manually,` cron._trigger` is called, which schedules the cron to be triggered later by a worker. To check whether the cron is being run manually or not, we check if there is a request. In SaaS, staging, and duplicate databases, the request is only truthy when the cron is run manually and falsy when run by a worker. In a SH production database, however, the request is truthy both when run manually and when run by a worker due to a specific cron worker configuration in that environment. As a result,` cron._trigger `is called infinitely and nothing gets uploaded to the cloud. **FIX** Instead of calling `cron._trigger()` to reschedule the job, `limit_time_real` is used when `request` is truthy, and `limit_time_real_cron` otherwise. opw-6330674 Forward-Port-Of: odoo/odoo#279581
Before this commit, the Chat action of the meeting view could keep its unread dot instead of showing "1" after "Mark as Unread": FAILED: [14/21] Tour discuss.meeting_view_tour Step .o-mail-Meeting [title='Chat']:has(.badge:contains(1)). Element (.o-mail-Meeting [title='Chat']:has(.badge:contains(1))) has not been found. TIMEOUT step failed to complete within 10000 ms. This happens because a mark as read carries the id of the newest message the client knew when it re
Original PR description
Before this commit, the Chat action of the meeting view could keep its unread dot instead of showing "1" after "Mark as Unread": FAILED: [14/21] Tour discuss.meeting_view_tour Step .o-mail-Meeting…
Before this commit, the Chat action of the meeting view could keep its unread dot instead of showing "1" after "Mark as Unread":
FAILED: [14/21] Tour discuss.meeting_view_tour
Step .o-mail-Meeting [title='Chat']:has(.badge:contains(1)).
Element (.o-mail-Meeting [title='Chat']:has(.badge:contains(1)))
has not been found.
TIMEOUT step failed to complete within 10000 ms.
This happens because a mark as read carries the id of the newest message the client knew when it requested it, and under load it can reach the server after a newer message was posted. The new message separator then moves back before that message, which makes it unread before the user even asks for it. The click on "Mark as Unread" writes the separator the counter is already computed from, so the counter does not change, and the client, which holds the counter it displays while the user reads the thread, never refreshes it.
This commit moves the new message separator forward only, so reading messages never makes another one unread.
https://runbot.odoo.com/odoo/error/945958
Forward-Port-Of: odoo/odoo#281988Steps to reproduce: 1. Install CRM 2. Activate the Arabic language with English 3. Create a lead with a new email, a new company name, and the Arabic language 4. Save and try to send a message from the chatter Issue: - After the message is sent, the language of Lead is changed to EN from Arabic - Contacts created with the English language Cause: - When we send a message from the chatter of a Lead that has an email_from, a partner_name, and the Arabic language, this forces the creati
Original PR description
Steps to reproduce: 1. Install CRM 2. Activate the Arabic language with English 3. Create a lead with a new email, a new company name, and the Arabic language 4. Save and try to send a message from…
Steps to reproduce: 1. Install CRM 2. Activate the Arabic language with English 3. Create a lead with a new email, a new company name, and the Arabic language 4. Save and try to send a message from the chatter Issue: - After the message is sent, the language of Lead is changed to EN from Arabic - Contacts created with the English language Cause: - When we send a message from the chatter of a Lead that has an email_from, a partner_name, and the Arabic language, this forces the creation of a new contact First child contact is created with arabic language, but `parent_name` is present in the creation dictionary, so this triggers `_create_parent_from_name` that builds a dictionary to create the parent company, but it does not pass the language. As a result, the parent company is created with the English language. then `_create_parent_from_name` links the child to this new parent and this linking triggers the `_compute_lang` on the child and overwrites the child's language with the parent's language At last, Lead's own computed field `_compute_lang_id` triggers and sets the language to English Solution: - pass the language in the dictionary to create the parent company opw-6449407 Forward-Port-Of: odoo/odoo#281747
Description of the issue/feature this PR addresses: Odoo supports WebP image fields, but `base_import` validates remote images with `PIL.Image.open()`. Odoo intentionally leaves Pillow's WebP decoder unloaded, so a valid WebP URL is rejected as an unidentified image. Current behavior before PR: Importing a valid WebP image URL fails with `cannot identify image file`. Oversized and unsupported WebP files are also rejected by Pillow before the import-specific size policy can be applied. Desire
Original PR description
Description of the issue/feature this PR addresses: Odoo supports WebP image fields, but `base_import` validates remote images with `PIL.Image.open()`. Odoo intentionally leaves Pillow's WebP decoder…
Description of the issue/feature this PR addresses: Odoo supports WebP image fields, but `base_import` validates remote images with `PIL.Image.open()`. Odoo intentionally leaves Pillow's WebP decoder unloaded, so a valid WebP URL is rejected as an unidentified image. Current behavior before PR: Importing a valid WebP image URL fails with `cannot identify image file`. Oversized and unsupported WebP files are also rejected by Pillow before the import-specific size policy can be applied. Desired behavior after PR is merged: Use Odoo's existing WebP header parser for dimension validation. Valid WebP URLs import unchanged, unsupported WebP remains rejected, and the existing 42-million-pixel import limit remains enforced. Tests cover valid, unsupported, and oversized WebP URL payloads. The complete `test_base_import` suite passes (56 tests, 0 failures/errors). --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282133 Forward-Port-Of: odoo/odoo#276648
Steps to produce: --- - Install `website_sale` and `stock` modules - Create a product, publish it, and add it to the cart from website. - Go to `Website > eCommerce > Orders > Orders`. - Open the order, click on `Add shipping` > add `Standard Delivery`. - Confirm the order, validate the delivery via the Delivery smart button. - From the list view, click the `To fulfill` button. Issue: --- - The order that was just delivered still appears in the `To fulfill` filter results, even thoug
Original PR description
Steps to produce: --- - Install `website_sale` and `stock` modules - Create a product, publish it, and add it to the cart from website. - Go to `Website > eCommerce > Orders > Orders`. - Open the…
Steps to produce: --- - Install `website_sale` and `stock` modules - Create a product, publish it, and add it to the cart from website. - Go to `Website > eCommerce > Orders > Orders`. - Open the order, click on `Add shipping` > add `Standard Delivery`. - Confirm the order, validate the delivery via the Delivery smart button. - From the list view, click the `To fulfill` button. Issue: --- - The order that was just delivered still appears in the `To fulfill` filter results, even though all actual products have been fully delivered. Root cause: --- - The `_search_is_unfulfilled`[1] method checks whether any order line has `qty_delivered < product_uom_qty`. A delivery/shipping line (with `is_delivery = True`) is a service—it is never physically delivered, so its `qty_delivered` remains 0 while its `product_uom_qty` is typically 1. This means `0 < 1` is always true for delivery lines, causing every order with a shipping cost line to permanently appear as unfulfilled, regardless of whether all actual products have been fully delivered. Solution: --- - Added domain to exclude all service-type products from the unfulfilled orders check. - Now, an order line is considered unfulfilled only when: - `qty_delivered < product_uom_qty` (under-delivered) - `product.type != 'service'` (not a service product — excludes services, delivery lines, etc.) [1]https://github.com/odoo/odoo/blob/e5d6650c542e44441c7087c729b3b74af3c75fe0/addons/website_sale/models/sale_order.py#L171-L183 opw-6420723 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279134
Steps to reproduce: - make a few sales in the PoS and refund one of them - close the session - select all those orders, including the refund, and create a consolidated invoice Issue: The invoice is refused with "You cannot validate an invoice with a negative total amount. You should create a credit note instead.", while the total of the selected orders is positive. If a cash rounding method is set on the PoS config, no error is raised but the posted document is a credit note carrying a r
Original PR description
Steps to reproduce: - make a few sales in the PoS and refund one of them - close the session - select all those orders, including the refund, and create a consolidated invoice Issue: The invoice is…
Steps to reproduce: - make a few sales in the PoS and refund one of them - close the session - select all those orders, including the refund, and create a consolidated invoice Issue: The invoice is refused with "You cannot validate an invoice with a negative total amount. You should create a credit note instead.", while the total of the selected orders is positive. If a cash rounding method is set on the PoS config, no error is raised but the posted document is a credit note carrying a rounding line equal to twice the order total (a credit note of 20.00 with a 40.00 "Rounding" line for sales of 10.00 + 20.00 and a refund of 10.00). Cause: _prepare_invoice_vals picked the move type from the presence of a refund in the group instead of its net amount: any group holding an order with is_refund set, or a negative amount_total, became an 'out_refund'. _get_invoice_lines_values then negates the quantities of every order whose direction differs from the move type, so the sales end up as negative lines of a credit note and the document totals -20.00 instead of +20.00. account.move refuses to post it. When invoice_cash_rounding_id is set, the cash rounding line is computed to bring the document back to a total valid for its type, so it absorbs the whole sign error and the wrong credit note is posted silently. Fix: Choose the move type from the net amount_total of the group, as was done up to saas-18.3, and keep is_refund only as the tie-break when that net is zero so a lone zero-total refund still gives a credit note. The sign handling of the lines is unchanged: it already keys on each order's own direction, which is what makes a sale a negative line of a credit note and a refund a negative line of an invoice. opw-6452996 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281703
Steps to reproduce: - Create a "Buy 2 Get 1 free" program whose rule and reward cover three products having the same price - In the PoS, add one unit of each of the three products -> one free product is given - Add three more units of the second product, 6 units in total Issue: Only one free product is given instead of two, the order total is 50 instead of 40. Cause: `_updateRewardLines` deletes the reward lines and re-applies each claimed reward. Beforehand it merges the claims havin
Original PR description
Steps to reproduce: - Create a "Buy 2 Get 1 free" program whose rule and reward cover three products having the same price - In the PoS, add one unit of each of the three products -> one free product…
Steps to reproduce: - Create a "Buy 2 Get 1 free" program whose rule and reward cover three products having the same price - In the PoS, add one unit of each of the three products -> one free product is given - Add three more units of the second product, 6 units in total Issue: Only one free product is given instead of two, the order total is 50 instead of 40. Cause: `_updateRewardLines` deletes the reward lines and re-applies each claimed reward. Beforehand it merges the claims having the same reward and the same price, which is the case for two free products of the same price even when they were claimed for two different products. The merged claim keeps the `_reward_product_id` of the first line only, with a quantity of two. On re-application, `_computeUnclaimedFreeProductQty` only counts in `available` the quantity of that single product, since the other lines are counted only while a reward line is still in the order and they have all just been deleted. It therefore returns 1 and the second free product is lost. Fix: Only merge claims that were made for the same free product. Gift card/ewallet claims have no `_reward_product_id` and claims of a reward having a single reward product all share the same one, so both keep being merged as before. opw-6430385 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282052
As the `requirements.txt` file path changed from `addons/iot_box_image` to `setup/iot_box_builder` the checkout from 19 to saas-19.4 can't find the file (looking at the former path instead of the new one). As a workaround, we add `sentry_sdk` requirement in v19.0. Forward-Port-Of: odoo/odoo#282122 Forward-Port-Of: odoo/odoo#282001
Original PR description
As the `requirements.txt` file path changed from `addons/iot_box_image` to `setup/iot_box_builder` the checkout from 19 to saas-19.4 can't find the file (looking at the former path instead of the new one). As a workaround, we add `sentry_sdk` requirement in v19.0. Forward-Port-Of: odoo/odoo#282122 Forward-Port-Of: odoo/odoo#282001
When creating a MO For a product with no variant, it will add an on apply on variant component, even if only one of it's attribute has a values that match with the product on the mo. Steps to reproduce: ------------------- * Create a Product with two never attributes (att1 and att2) * Add values to both attributes * Create a BOM with components with apply on variant for every possibility: - component att1 val1, att2 val1, apply on variant: att1 value 1 att2 value1 - component att1 v
Original PR description
When creating a MO For a product with no variant, it will add an on apply on variant component, even if only one of it's attribute has a values that match with the product on the mo. Steps to…
When creating a MO For a product with no variant, it will add an on apply on variant component, even if only one of it's attribute has a values that match with the product on the mo. Steps to reproduce: ------------------- * Create a Product with two never attributes (att1 and att2) * Add values to both attributes * Create a BOM with components with apply on variant for every possibility: - component att1 val1, att2 val1, apply on variant: att1 value 1 att2 value1 - component att1 val1, att2 val2, apply on variant: att1 value 1 att2 value2 - component att1 val2, att2 val1, apply on variant: att1 value 2 att2 value1 - ... * Add mto and manufacture to the product * Create and confirm a SO for the product variant att1 value 1 and att value 2 -> On the MO every component that as at least one of the values will be present. Observation: ------------- When confirming the SO it will call action_confirm. Since we are in mto, it will create a procurement order of the manufacture type and will create a MO. When creating the workorder, it will call explote on the bom to know all the components: https://github.com/odoo/odoo/blob/f7fb0a941d6bac39b7057db36f57be079559912c/addons/mrp/models/mrp_production.py#L626 Each line that does not respect the apply on variant condition will be ignored: https://github.com/odoo/odoo/blob/f7fb0a941d6bac39b7057db36f57be079559912c/addons/mrp/models/mrp_bom.py#L450-L451 it will retrieve the line if at least one value from any attribute match: https://github.com/odoo/odoo/blob/f7fb0a941d6bac39b7057db36f57be079559912c/addons/mrp/models/mrp_bom.py#L623-L626 opw-6293259 Forward-Port-Of: odoo/odoo#271351
In commit 004b56a5c31841bc0bf4a9f17902bbff8d0f509d we added warnings to tell French companies to install the PDP module. When working on commit 720294ee523c6f84d0302e6b7fd634069f72dda7 we noticed the problem that the PDP module is not available without rescanning the available modules ("Update Apps List" in debug mode in the "Apps"). This is fixed in this commit: In case the module is not installed we still show the warning but link to the "Update Apps List" wizard. task-None For
Original PR description
In commit 004b56a5c31841bc0bf4a9f17902bbff8d0f509d we added
warnings to tell French companies to install the PDP module.
When working on commit 720294ee523c6f84d0302e6b7fd634069f72dda7
we noticed the problem that the PDP module is not available
without rescanning the available modules ("Update Apps List" in
debug mode in the "Apps").
This is fixed in this commit:
In case the module is not installed we still show the warning
but link to the "Update Apps List" wizard.
task-None
Forward-Port-Of: odoo/odoo#280209In the sampel dashboard the figures are half transparent to indicate that they are just sample data, but they are still interactive. This commit disable all the pointer events on them. Task: [6467022](https://www.odoo.com/web#id=6467022&cids=1&menu_id=4720&action=333&active_id=2328&model=project.task&view_type=form) 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 rea
Original PR description
In the sampel dashboard the figures are half transparent to indicate that they are just sample data, but they are still interactive. This commit disable all the pointer events on them. Task: [6467022](https://www.odoo.com/web#id=6467022&cids=1&menu_id=4720&action=333&active_id=2328&model=project.task&view_type=form) 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#282179
Before this commit, a test answering a route with a response of its own, `new Response(stream)`, reads `content-type: application/json` back from it, a header it never set. At the debug log level, the request itself fails: Unexpected token 'o', "[object Rea"... is not valid JSON This happens because the mocked fetch guesses the content type from its result, and a response is neither a string, a `Blob` nor a `FormData`, so it falls back to JSON, on the very headers the response carries.
Original PR description
Before this commit, a test answering a route with a response of its own, `new Response(stream)`, reads `content-type: application/json` back from it, a header it never set. At the debug log level, the request itself fails:
Unexpected token 'o', "[object Rea"... is not valid JSON
This happens because the mocked fetch guesses the content type from its result, and a response is neither a string, a `Blob` nor a `FormData`, so it falls back to JSON, on the very headers the response carries. The response logger then reads the body as text, "[object ReadableStream]" for a stream, and parses it as JSON.
This commit guesses the content type of a raw value only, so a response keeps the headers it was built with.
Forward-Port-Of: odoo/odoo#281968### Issue: When a company is not connected to the French Electronic Invoicing PDP proxy, the generated XML is missing required notes: `PMT`, `PMD` and `AAB` These notes are required by Factur-X rule `BR-FR-05/BT-22` and their absence causes validation errors on the FNFE validator ### Cause: `_l10n_fr_pdp_get_default_notes` only added the notes when the company was using a PDP proxy type Non-PDP users sending invoices via other means were excluded, which contradicts the French e-invoicing
Original PR description
### Issue: When a company is not connected to the French Electronic Invoicing PDP proxy, the generated XML is missing required notes: `PMT`, `PMD` and `AAB` These notes are required by Factur-X rule `BR-FR-05/BT-22` and their absence causes validation errors on the FNFE validator ### Cause: `_l10n_fr_pdp_get_default_notes` only added the notes when the company was using a PDP proxy type Non-PDP users sending invoices via other means were excluded, which contradicts the French e-invoicing requirements ### Steps to reproduce: - Install `l10n_fr_pdp` and switch to the FR company - In Settings, ensure French Electronic Invoicing is not activated - Create and confirm an invoice (any line with tax) - Send the invoice and open the generated XML Before the fix, the `PMT`, `PMD` and `AAB` notes are missing Activating French Electronic Invoicing would include them opw-6392262 opw-6377507 Forward-Port-Of: odoo/odoo#282031 Forward-Port-Of: odoo/odoo#279966
Before this commit, the hoot test "keep banner for messages received while scrolled up" failed at random on runbot: ``` Failed to find 1 of ".o-mail-Thread-banner:has(:text('1 new message'))" (Timeout of 10 seconds). Found 0 instead. ``` This happens because the test waits for the scroll position it sets in the DOM only, while the thread copies that position to the record on the scroll event, one animation frame later. Bob's message can arrive in between, when the record still says "bot
Original PR description
Before this commit, the hoot test "keep banner for messages received while scrolled up" failed at random on runbot:
```
Failed to find 1 of ".o-mail-Thread-banner:has(:text('1 new message'))"
(Timeout of 10 seconds). Found 0 instead.
```
This happens because the test waits for the scroll position it sets in the DOM only, while the thread copies that position to the record on the scroll event, one animation frame later. Bob's message can arrive in between, when the record still says "bottom": the counter the banner reads stays frozen at 0 and the message is marked as read on arrival, so the banner never shows.
This commit waits until the record holds that position before posting.
https://runbot.odoo.com/odoo/error/945967
Forward-Port-Of: odoo/odoo#282333
Forward-Port-Of: odoo/odoo#282218Steps to reproduce: - Enable presets and set a preset carrying a tax-replacing fiscal position (e.g. "Takeout") as the default preset of the POS config - Take an order with another preset that has no fiscal position (e.g. "Dine In") - Refund that order from the ticket screen Issue: The refund is taxed with the fiscal position of the default preset instead of the one of the refunded order. The ticket screen displays the refund lines with the taxes of the original order, but the refund that
Original PR description
Steps to reproduce: - Enable presets and set a preset carrying a tax-replacing fiscal position (e.g. "Takeout") as the default preset of the POS config - Take an order with another preset that has no…
Steps to reproduce: - Enable presets and set a preset carrying a tax-replacing fiscal position (e.g. "Takeout") as the default preset of the POS config - Take an order with another preset that has no fiscal position (e.g. "Dine In") - Refund that order from the ticket screen Issue: The refund is taxed with the fiscal position of the default preset instead of the one of the refunded order. The ticket screen displays the refund lines with the taxes of the original order, but the refund that is actually created maps them through the wrong fiscal position. With tax-included prices the totals still match on screen, so the operator only sees the discrepancy after going back. Cause: The destination order of a refund is an empty order, created with the default preset and therefore with that preset's fiscal position. In TicketScreen.onDoRefund, the fiscal position of the refunded order was only copied onto it when the refunded order had one, so an order taken without a fiscal position kept the default preset's one. When an already existing empty order is reused as destination, whatever fiscal position was last set on it survives for the same reason. Fix: Always assign the fiscal position of the refunded order to the destination order, an empty one included, so a refund is taxed exactly like the order it refunds instead of silently switching. The preset itself is left untouched: it drives the ordering workflow (timing slot, customer identification) which must not be imposed on a refund. opw-6442664 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281102 Forward-Port-Of: odoo/odoo#280672
Before this commit, the two steps posting "Hello everyone!" were spliced into the meeting view tour at the index of the step clicking on the Chat action, looked up with `steps.find`. That returns the step itself, so `splice` coerced it to NaN and inserted at 0: the message was posted as the very first thing the tour did, and the marker it searched for served no purpose. Reminder that the index it aimed for does not work: the meeting view is fullscreen with the invite panel open there, and the
Original PR description
Before this commit, the two steps posting "Hello everyone!" were spliced into the meeting view tour at the index of the step clicking on the Chat action, looked up with `steps.find`. That returns the step itself, so `splice` coerced it to NaN and inserted at 0: the message was posted as the very first thing the tour did, and the marker it searched for served no purpose. Reminder that the index it aimed for does not work: the meeting view is fullscreen with the invite panel open there, and the only composer belongs to the chat panel, which opens one step later. This commit writes the two steps at the head of the list and drops the marker, so the tour runs in the order it reads. Forward-Port-Of: odoo/odoo#282181 Forward-Port-Of: odoo/odoo#282060