Friday, July 10, 2026
29 changes · saas-19.4
New functionality added to Odoo
Odoo can now recognize and contact WhatsApp users through business-scoped user IDs when phone numbers are not provided. This improves customer matching and continuity for businesses using WhatsApp messaging, while also avoiding crashes when WhatsApp returns an error message.
Original PR description
Add support for whatsapp business-scoped user ids as outline in the [documentation](https://developers.facebook.com/documentation/business-messaging/whatsapp/business-scoped-user-ids) for their introduction this June. This effectively adds a table mapping BSUID to contacts to enable contacting users who contact the business directly, as the business will now not necessarily be provided with their number. Additionally the “whatsapp id”, i.e. the canonical form of the phone number as stored in whatsapp, is stored to help better match contacts regardless of formatting details in odoo and whatsapp. task-5476552 Forward-Port-Of: odoo/enterprise#117782
This update introduces support for Georgian accounting standards, including a new chart of accounts, tax settings, and VAT reporting capabilities. This allows businesses operating in Georgia to accurately manage their financial records and comply with local tax regulations within Odoo.
Original PR description
[ADD] l10n_ge: add Georgian Chart of Accounts - This commit adds the Georgian accounting localization, including the chart of accounts, taxes, fiscal positions, tax groups, and VAT report required for standard accounting and tax reporting flows - It provides support for domestic VAT, reverse charge VAT, and the Georgian VAT declaration report. taskID-3927928 related PR (from 19.0 to saas-19.2) - https://github.com/odoo/odoo/pull/263452 Forward-Port-Of: odoo/odoo#263765
Enhancements to existing features
Self-order kiosk orders that are not yet paid will no longer be sent to the Belgian blackbox fiscal system immediately. This supports workflows where customers complete payment at the counter or with another method, reducing premature fiscal processing.
Original PR description
This commits adapts the code in confirmation_page.js to not send the order to the blackbox from the kiosk if the order is not in paid state. task-id: 5960666 Forward-Port-Of: odoo/enterprise#121524 Forward-Port-Of: odoo/enterprise#117585
Resolved issues and error corrections
Fixes an issue where quotations created from a CRM opportunity could be hidden when viewing the opportunity's quotation list. This ensures sales teams can reliably see and follow up on non-rental quotations even when rental sales features are installed.
Original PR description
Steps to reproduce: 1. Install sale_crm and sale_renting_crm 2. Create an opportunity 3. On this opportunity, create two quotations from the `New quotation` button 4. From the opportunity's form…
Attachment deletion is now much faster for Chilean electronic invoicing records by improving how related invoice and stock transfer records are found. This reduces long wait times during cleanup operations, especially on large databases.
Original PR description
## The problem Deleting attachments checks foreign key triggers. Lookups in `account_move` and `stock_picking` tables for `ir_attachment` related fields coming from `l10n_cl_edi` overrides were slow due to missing indexes. ## The solution Added needed indexes to optimize triggers' lookups. ## Benchmark Time benchmark (deleting attachments from a customer database with 224K account moves and 204K stock pickings): |# of rows|Time (Before)|Time (After)| |----------|--------------|-------------| 100 | 29s | 16ms 1000 | 285s | 300ms OPW-6331845 Forward-Port-Of: odoo/enterprise#123350 Forward-Port-Of: odoo/enterprise#123252
German POS certification now handles retail and restaurant transactions separately, matching Fiskaly’s recommended flow. This improves compliance reliability by starting transactions earlier, avoiding unnecessary updates for retail sales, and sending only relevant kitchen updates for restaurant orders before final validation.
Original PR description
In this commit: ------------------ - Maintain separate Fiskaly transaction flows for retail (short tx) and restaurant (long tx) orders as discussed with the Fiskaly team. - `Initialize order…
In this commit: ------------------ - Maintain separate Fiskaly transaction flows for retail (short tx) and restaurant (long tx) orders as discussed with the Fiskaly team. - `Initialize order transactions` with an empty payload when the `first product` is added. - Start `receipt transactions` with an empty payload when the `first payment line` is added. - For retail flows, no intermediate order updates are sent to Fiskaly before finalization. - For restaurant flows, create additional transaction updates during kitchen synchronization. Ensure already synchronized products are not resent, and only newly added or updated quantities are included in the payload. - `Finalize order and receipt transactions` with complete order lines and payment details when we validate the order. task: 6208963 Reference: <img width="1863" height="1285" alt="de_tss_flow" src="https://github.com/user-attachments/assets/9140788e-7948-4a08-9f11-27197b22ca8b" /> Forward-Port-Of: odoo/enterprise#120529 Forward-Port-Of: odoo/enterprise#117526
Shopfloor now follows the same rules as the backend when work order quantities are updated, avoiding unintended changes to the quantity being produced in continuous production. The work order form layout was also reorganized to make production information clearer and more consistent.
Original PR description
In this commit, shopfloor is modified in order to match the behaviour in the backend; On updating WO's quantity, the quantity producing is not updated if its a continuous production. Workorder form fields were also re-ordered as a part of the ongoing continuous production clean. Task: 6346515
This update introduces a manual process for Know Your Customer (KYC) verification within the PEPPOL account setup. Previously, PEPPOL account creation relied on automated checks. Now, users can complete a manual KYC process, ensuring compliance with regulatory requirements and streamlining the account onboarding experience. This change improves the security and reliability of our PEPPOL service.
Original PR description
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#275371
This update streamlines the continuous production process in MRP by simplifying quantity updates and tracking changes. The system now automatically updates work order status based on production quantity, reducing manual intervention and improving data accuracy. UI enhancements have also been included.
Original PR description
A few points to improve continuous production: - Updating workorder produced qty will no longer update qty producing for the MO, because work orders quantity will always be updated and this will be done by several users simultaneously and updating MO's quantity producing at the same time will make it difficult to manage and its not needed. - Track work order quantites updates, for better tracking of who changed the quantity. - Improved continuous production tool tip. - Work order status will be updated from 'To Do' to 'in progress' when the produced quantity is updated. - Some UI changes. Task 6346515 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Steps to reproduce:
1. Install sale_crm and sale_renting_crm
2. Create an opportunity
3. On this opportunity, create two quotations from the `New quotation` button
4. From the opportunity's form view, click on the `Quotation` smart button
Issue:
- The quotations are not visible in the list view
Why?
- In module `sale_renting_crm`, we override the domain to exclude rental quotations.
https://github.com/odoo/enterprise/blob/fda037c62a6661665611fb061718c01aec39ac1b/sale_renting_crm/models/crm_lead.py#L33-L36 But from the saas-19.3 `is_rental_order` field is no longer stored in the DB, It is computed now and filtered through `_search_is_rental_order`.
https://github.com/odoo/enterprise/blob/fda037c62a6661665611fb061718c01aec39ac1b/sale_renting/models/sale_order.py#L223-L227 The search method did not properly handle the Boolean search shape used by the ORM, so the quotations were incorrectly filtered out. In our case, we gave the domain `("is_rental_order", "=", False)` but the operator is translated to `not in` and value to `Orderedset([True])` by the domain optimiser
Solution:
- Update `_search_is_rental_order()` to handle the ORM-normalized boolean search correctly for rental and non-rental quotations.
opw-6304696
Forward-Port-Of: odoo/enterprise#120869Bank reconciliation models that include taxes now correctly fill in the tax base amount on generated journal entries. This prevents tax lines from showing a misleading zero base amount and improves accounting accuracy without requiring users to reset entries to draft.
Original PR description
### Steps to reproduce the issue: 1. Download Accounting 2. Go to Journal entries and create a new one 3. Click Toggle Studio button and go to the view to add the tax_base_amount field to the list of…
### Steps to reproduce the issue: 1. Download Accounting 2. Go to Journal entries and create a new one 3. Click Toggle Studio button and go to the view to add the tax_base_amount field to the list of existing fields 4. Add 2 lines (example): 1. account Product Sales with 1000 dollars credit and 15% tax under Tax column 2. account Bank with 1000 dollars debit 5. Go to Dashboard > Bank > Click the 3 dots of one random bank matching line and click on Manage Models 6. Go to bank fees and add the 15% tax 7. Go back to bank reconciliation and create a new one of 2000 dollars with label bank fees (it will associate the tax automatically) 8. Go back to Journal Entries, group by Journal and search for the transaction of 2000 sollars for account Bank 9. Problem: see that the Base Amount for the 15% bank fees lines (251000 Tax Received account) is 0. Clicking on Reset to draft button the base amount column is automatically updated but this should happen automatically ### Cause of the issue: This occurs because the _lines_prepare_tax_line method in account.bank.statement.line fails to map this field in its return dictionary. ### Reason to introduce the fix: Currently, when applying a reconciliation model with taxes the generated tax lines incorrectly record a tax_base_amount of $0.00. Instead, it should be displayed and calculated. opw-6220948 Forward-Port-Of: odoo/enterprise#123603 Forward-Port-Of: odoo/enterprise#122139
This change restores payroll salary attachment refund handling and beneficiary bank details that were previously removed. It keeps existing payment and reporting flows working while the team revisits the best way to redesign refund-related attachments later.
Original PR description
In this previous PR (https://github.com/odoo/enterprise/pull/114188) we removed the is_refund flag and, together with it, also the fields related to the beneficiary. This is because there is an onchange method on is_refund that sets the beneficiary bank account for any attachment that is not a refund to False. However, while the removal of is_refund is still in the plans, we want to take back the beneficiary fields and use them even in the case of non-refund attachments. We need to think better about how to remove the is_refund field and structure negative attachments around it, so for now we revert the previous PR. Task: 6376383
Shop floor operators can now see their own and other operators' profile photos instead of generic placeholder images. This makes it easier for manufacturing teams to identify the right operator when viewing or changing assignments.
Original PR description
Currently when a shopfloor operator modifies the operator, operator images are not visible and only place holder images are visible. ## Steps to produce: - Install Manufacturing with demo data -…
Currently when a shopfloor operator modifies the operator, operator images are not visible and only place holder images are visible. ## Steps to produce: - Install Manufacturing with demo data - Login as Marc Demo - Open shopfloor ## Observed Behavior: Operator is unable to see their own profile picture and when you press the Edit Operator the operator is not able to see their as well as other operators images as well. ## Root cause: This issue occurs because by default an operator's user does not have access right for `Employees` in Human Resources section as they are not an HR officer nor an administrator. So when the user tries to access the image url that is meant for users with those access rights (HR officer / Administrator) at [1] and at [2]. The system returns a placeholder image instead of the employee image since the operator does not have access to hr employee model but does have access to hr employee public as per this [commit](https://github.com/odoo/odoo/commit/c9ca3761464413327d2beb697553a3ccd7eef4d1) [1]- https://github.com/odoo/enterprise/blob/5fe2fb44f4c8a938390343f7cdc90674cbf09391/mrp_workorder/static/src/mrp_display/dialog/mrp_employee_dialog.js#L16 [2]- https://github.com/odoo/enterprise/blob/5fe2fb44f4c8a938390343f7cdc90674cbf09391/mrp_workorder/static/src/mrp_display/employees_panel.xml#L9-L10 ## Solution: Allowing operators to see images can be done by modifying the links to use the public model instead of the private model. This will allow operators see other operators based on images and should improve user experience. | Before | |--------| | <img width="1865" height="844" alt="image" src="https://github.com/user-attachments/assets/c28f4209-0a60-486a-bfec-2a4db39cd946" /> | | <img width="995" height="899" alt="image" src="https://github.com/user-attachments/assets/7541edd9-6cc9-42d1-b5c5-39e0740d9a93" /> | | After | |--------| | <img width="1881" height="903" alt="image" src="https://github.com/user-attachments/assets/df3f8357-5cc2-4688-83dd-9a009fd56957" /> | | <img width="1221" height="862" alt="image" src="https://github.com/user-attachments/assets/c9d72b07-e1ad-43e4-85f5-00d8f1db9095" /> | opw-6321989 Forward-Port-Of: odoo/enterprise#123361 Forward-Port-Of: odoo/enterprise#121751
The barcode app now correctly respects operation settings that block adding extra products. This prevents users from scanning and adding unreserved items after all reserved products have been processed, helping keep inventory transfers aligned with planned quantities.
Original PR description
# How to reproduce - Go to Inventory > Settings > Operation Types - Pick any Operation and disable "Allow extra product" - Create a picking for that operation type with atleast one product and click…
# How to reproduce - Go to Inventory > Settings > Operation Types - Pick any Operation and disable "Allow extra product" - Create a picking for that operation type with atleast one product and click on "Mark as Todo" - Go to the Barcode app and find the created picking - Scan all the reserved products - Exit the picking and re-enter # The problem The "Add Product" button is displayed and you can scan unreserved products even tough you sould not be allowed to # Cause of the issue The problem stems from the fact that even with "Allow extra product" disabled, we still allow to add unreserved products for immediate transfers (created directly in the barcode app). The issue is that we don't really have a way to distinguish immediate transfers from plannified ones made in the Inventory app. So we try to guess using the `_useReservation` attribute (If it is false, we allow additional products). `_useReservation` is computed as follows : if any move lines from the inital state is not yet picked, set it to true : https://github.com/odoo/enterprise/blob/ca4b369e4fc9f4655574cf1ca71c81faaadc3e88/stock_barcode/static/src/models/barcode_picking_model.js#L43 So in our case, once all the reserved products are scanned, all the initial move lines are picked and our guessing fails. # Proposed Solution Since immediate transfers are never validated and stays in draft mode, guess using the state of the current picking in addition to `useReservation` opw-6231238 Forward-Port-Of: odoo/enterprise#118378
Fixes an issue where expanding a Knowledge article in the sidebar could show only favorited child articles while hiding the others. This ensures users see the complete article structure immediately, reducing confusion and avoiding the need to reload the page.
Original PR description
The sidebar always loads the user's favorite articles along with the visible ones, so a favorited article is shown as a root of the favorite tree. When that favorite is also a child of a folded…
The sidebar always loads the user's favorite articles along with the visible ones, so a favorited article is shown as a root of the favorite tree. When that favorite is also a child of a folded article, it gets added to its parent's child_ids in the main tree, even though the parent's other children were not fetched. A folded article only gets its favorited children back from get_sidebar_articles, not its whole child set. When the parent is then unfolded, unfold() only read the children from the database when child_ids was empty. The favorited child already filled child_ids, so the call was skipped and the remaining children stayed hidden until the next reload. unfold() now uses a new children_loaded flag instead of the length of child_ids to decide whether to fetch the children. The flag is set once an article's whole child set is loaded: in loadChildren(), and in loadArticles() for the articles that were unfolded, since those come back with all their children. loadChildren() also rebuilds child_ids from the search result so a favorite already loaded is not added twice. The fix lives in the sidebar component because the partial child_ids only exists on the frontend, get_sidebar_articles already returns the right records. Steps to reproduce: 1. Open the Knowledge app 2. Create an article with two child articles 3. Add one of the two children to your favorites with the star icon 4. Open another article that is not under that parent 5. Fold the parent article in the sidebar, then refresh the page 6. Expand the parent article => only the favorited child is shown under the parent, the other child is missing Ticket [link](https://www.odoo.com/odoo/project.task/6186466) opw-6186466 Forward-Port-Of: odoo/enterprise#118804
This update corrects several small issues in accounting reports, including when currency warnings appear, how warnings can be dismissed, and how editable budget values are presented. It also removes an irrelevant setup field for composite reports and improves the appearance of working file cards, making the reporting experience clearer and more stable for users.
Original PR description
**Commit 1: [FIX] account_reports: warning for CTA visibility** Steps to reproduce: - Open the balance sheet -> The warning for CTA is displayed even when a single company is selected. It should only…
**Commit 1: [FIX] account_reports: warning for CTA visibility**
Steps to reproduce:
- Open the balance sheet
-> The warning for CTA is displayed even when a single company is selected.
It should only be the case if multiple companies having different currencies
are involved in the computation of the report.
**Commit 2: [FIX] account_reports: hide groupby field on form view for composite report**
this field is useless for composite reports.
**Commit 3: [FIX] account_reports: differentiate UX for auditable external values.**
When creating a budget in the P&L, the external value cells should not be
auditable. The data was properly set but not the templates.
**Commit 4: [FIX] account_reports: avoid traceback on double-click on warning**
Steps to reproduce:
- Open an accounting report with any warning banner ("draft entries" for example)
- Quickly double-click the delete cross button
-> traceback stating that it cannot read properties of null (reading 'remove').
**Commit 5: [FIX] account_reports: working file card style adjustments**
<img width="401" height="317" alt="image" src="https://github.com/user-attachments/assets/76f636f0-09d8-44e5-8c7c-37561dc00b10" />
task-6361495Fixes an error that could block Kenyan POS refund validation and also affect batches of offline orders syncing back online. Refunds and synced orders are now processed individually so eTIMS reporting and receipt generation can complete reliably.
Original PR description
Steps to reproduce: 1. Install `l10n_ke_edi_oscu_pos`, set company to Kenya. 2. Sell and validate an order. 3. Refund it from the POS and validate the refund order. Issue: - A traceback is raised…
Steps to reproduce: 1. Install `l10n_ke_edi_oscu_pos`, set company to Kenya. 2. Sell and validate an order. 3. Refund it from the POS and validate the refund order. Issue: - A traceback is raised when validating the refund: `ValueError: Expected singleton: pos.order(<refund>, <original>)` raised in `get_l10n_ke_edi_oscu_pos_data`. Cause: - When syncing a refund, `sync_from_ui` returns both the new refund order and the original refunded order. `waitForPushOrder` forces post-processing for every Kenyan order in that list, so `beforePostPushOrderResolve` receives both ids in `order_server_ids` and forwards them as-is to `action_post_order` and `get_l10n_ke_edi_oscu_pos_data`, both of which expect a single record. `action_post_order` fails the same way, but its error was silently swallowed by the surrounding try/catch, letting the traceback surface only on the second call. - The same multi-id list is also produced whenever several orders created offline get synced together once back online. Solution: - `get_l10n_ke_edi_oscu_pos_data` is only needed for the receipt of the order being validated, so call it with `order.id` instead of the full `order_server_ids` list. - Replace the `action_post_order` call with `action_post_selected_orders`, which posts each order individually and skips ones already sent to eTIMS, correctly handling both the refund case (original order is already `sent`) and the offline multi-order sync case. opw-6364221 Forward-Port-Of: odoo/enterprise#123043
This fixes an issue where managers could not request an employee appraisal if the scheduled appraisal date had already passed. The change removes an unnecessary date update during appraisal creation, so users no longer need extra permissions or workarounds to proceed.
Original PR description
# How to reproduce You need to simulate the fact that you are creating an appraisal late so either : A) Directly edit the `next_appraisal_date` in SQL B) Go to Employee App > any Employee > Settings,…
# How to reproduce You need to simulate the fact that you are creating an appraisal late so either : A) Directly edit the `next_appraisal_date` in SQL B) Go to Employee App > any Employee > Settings, set Next Appraisal Date to tomorrow and wait for 2 days Then : - Click on Request Appraisal - Save # The problem An error is shown saying "You cannot set 'Next Appraisal Date' in the past.". You can workaround this by changing the Next Appraisal Date to a date in the future, but the problem is not every user has the right to do this. # Cause `next_appraisal_date` is also defined in hr.appraisal as a relate field of hr.employee : https://github.com/odoo/enterprise/blob/0226ad15abc8db70f8e379fddec3d83d15749c85/hr_appraisal/models/hr_appraisal.py#L56-L57 When creating an hr.appraisal, `next_appraisal_date` is present in `vals_list` because it is defined in the view since : https://github.com/odoo/enterprise/commit/58fba3098f33db82dfbccca2db229550402ed3ab https://github.com/odoo/enterprise/blob/0226ad15abc8db70f8e379fddec3d83d15749c85/hr_appraisal/views/hr_appraisal_views.xml#L92 This triggers a write on `next_appraisal_date` of hr.employee which triggers a constraint : https://github.com/odoo/enterprise/blob/0226ad15abc8db70f8e379fddec3d83d15749c85/hr_appraisal/models/hr_employee.py#L81-L85 opw-6147865 Forward-Port-Of: odoo/enterprise#122440 Forward-Port-Of: odoo/enterprise#114876
This change restores fields used to identify beneficiaries on payroll salary attachments after they were removed in an earlier update. It keeps existing refund handling in place while allowing beneficiary bank details to be used again for regular, non-refund attachments, reducing disruption to payroll payment workflows.
Original PR description
In this previous PR (https://github.com/odoo/enterprise/pull/114188) we removed the is_refund flag and, together with it, also the fields related to the beneficiary. This is because there is an onchange method on is_refund that sets the beneficiary bank account for any attachment that is not a refund to False. However, while the removal of is_refund is still in the plans, we want to take back the beneficiary fields and use them even in the case of non-refund attachments. We need to think better about how to remove the is_refund field and structure negative attachments around it, so for now we revert the previous PR. Task: 6376383
Donation products in Website Sale Subscription are now explicitly set up without sales tax. This prevents donors from being incorrectly charged tax when making a donation, aligning the checkout experience with the intended tax responsibility.
Original PR description
**Steps to reproduce:** 1. Install `website_sale_subscription` 2. Open the Products page, search for Donation and open the products **Issue:** A default Sales Tax is applied on the donation products. If a visitor donates money, they will be charged the sales tax. **Expected behavior:** Donation products should not include tax when web visitors donate money. Tax responsibility does not fall on the donors. **Why this happens:** When a `product.template`record is created without an explicit `taxes_id`, the field falls back to the default, which resolves to `company.account_sale_tax_id`. opw-6367188
Order fetching for UrbanPiper point-of-sale integrations now combines related order requests into a single server call. This reduces waiting time and server load when retrieving orders, making order synchronization more efficient without changing user workflows.
Original PR description
Issue: pos_urban_piper overrode getServerOrders() to add a separate loadServerOrders() call for it's own orders before delegating to super, resulting in up to an additional sequential RPCs on every order fetch. Fix: Extract the base query domain into a new overridable getServerOrdersDomain() method. Each module overrides it to OR in its own domain via Domain.or([super.getServerOrdersDomain(), extraDomain]), so all orders are fetched in a single RPC call instead of three. Task-6284860 Forward-Port-Of: odoo/enterprise#123679 Forward-Port-Of: odoo/enterprise#120001
This fix prevents Belgian payroll processing from crashing when seniority is calculated for multiple employee contract versions at once. It helps payroll teams complete CP200 seniority calculations reliably without manual interruption.
Original PR description
Before this commit, _compute_l10n_be_computed_seniority looped over cp200_versions but still read self.employee_id and self.l10n_be_scale_seniority, so when Odoo batched multiple CP200 versions…
Before this commit, _compute_l10n_be_computed_seniority looped over cp200_versions but still read self.employee_id and self.l10n_be_scale_seniority, so when Odoo batched multiple CP200 versions together, the whole recordset was passed to _get_first_version_date(), crashing with a singleton error.
After this commit, the loop correctly uses version.employee_id and version.l10n_be_scale_seniority instead.
Traceback (important part):
```py
File "/home/odoo/src/enterprise/l10n_be_hr_payroll/models/hr_version.py", line 1508,
in _compute_l10n_be_computed_seniority
company_seniority = relativedelta(fields.Date.today(),
self.employee_id._get_first_version_date()).years
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/addons/hr/models/hr_employee.py", line 621, in
_get_first_version_date
versions = self._get_last_consecutive_versions(date_limit)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/addons/hr/models/hr_employee.py", line 595, in
_get_last_consecutive_versions
self.ensure_one()
File "/home/odoo/src/odoo/odoo/orm/models.py", line 5406, in ensure_one
raise ValueError("Expected singleton: %s" % self)
ValueError: Expected singleton: hr.employee(30, 1)
```
task-6377630Instagram posts with images no longer crash the server when Instagram takes too long to fetch or process the image. Instead, the post is marked as failed with a clearer message, helping users understand the issue and try a smaller image.
Original PR description
Making an Instagram containing an image can crash the server with an unhandled `ReadTimeout` instead of marking the post as failed. ### Cause When creating a media container, Odoo passes a URL pointing to its own server and Instagram fetches the image from it server-side before responding. The timeout therefore covers network latency, Instagram's download speed from the Odoo server, and image processing time, making it prone to being exceeded. When it is, `requests` raises a `ReadTimeout` which is unhandled, leading to a raw RPC error instead of a clean `state='failed'`. ### Fix Catch the network errors and mark the post as failed instead of letting them crash the request. Timeouts get a message suggesting a smaller image, since they are usually caused by Instagram fetching and processing a large image server-side. Any other request error falls back to a generic message. opw-6015997 Forward-Port-Of: odoo/enterprise#122406 Forward-Port-Of: odoo/enterprise#112573
The Argentine VAT Book ZIP export no longer fails when invoices involve foreign partners marked as overseas providers. This helps accounting teams complete required tax reporting without manual workarounds or blocked exports.
Original PR description
Steps to reproduce: - Create a partner with: - State: Ireland - Identification Number: Foreign ID 55000004153 - ARCA Responsibility Type: Proveedor del Exterior - Create an invoice for the partner - Accounting > Reporting > Tax report - Select Report: VAT Book (AR), Tax Type: Sales - Click on gear icon > VAT Book (ZIP) Issue: Action will be blocked with error "No VAT configured for partner [58] <partner>" Analysis: Partners with ARCA responsibility type 'Proveedor del Exterior' (code 8) and a ForeignID identification type, causes a UserError when exporting the VAT Book (ZIP). Code 8 (foreign provider) is the purchase-side counterpart of code 9 (foreign customer), which already fell back to the country-level VAT. Extend the existing fallback branch to cover both codes. opw-6316008 Forward-Port-Of: odoo/enterprise#123506
Rental planning now handles company-wide time off more accurately by applying it only to resources on the matching working schedule, unless the time off is truly global. The change also strengthens rental planning website and backend tests, reducing the risk of incorrect availability or booking behavior.
Original PR description
## [FIX] sale_renting_planning: check global leaves working schedule Before this commit: any `resource.calendar.leaves` with no `resource_id` created would prevent all resources from being allocated…
## [FIX] sale_renting_planning: check global leaves working schedule
Before this commit: any `resource.calendar.leaves` with no `resource_id` created would prevent all resources from being allocated during the leave date.
After this commit: any `resource.calendar.leaves` with `no resource_id` would be applied only to resources with the same `calendar_id` as the leave.
if the leave has no `calendar_id` then the leave applies to all `resource.calendars`
if a resource has no `calendar_id` then leaves with no `calendar_id` apply to it as well
## [IMP] {website_}sale_renting_planning: move tests from industry and fix existing ones
This commit moves the tests from [odoo/industry#1980](vscode-file://vscode-app/snap/code/237/usr/share/code/resources/app/out/vs/code/electron-browser/workbench/workbench.html) to their respective standard modules.
It also fixes the logic behind some tests as they weren't testing a `planning.role` with `sync_shift_rental` enabled.
task-6179505
Forward-Port-Of: odoo/enterprise#120983
Forward-Port-Of: odoo/enterprise#116430Appointment picture and list layouts now use the website's configured tax display setting when showing product prices. This prevents customers from seeing tax-excluded prices on websites configured to display tax-included pricing, improving consistency and avoiding confusion.
Original PR description
When the `appointments_template_picture` and `appointments_template_list` templates were added to `website_appointment_account_payment` in 19.0+, the corresponding overrides in `website_appointment_sale` were not added. This caused the picture and list appointment blocks to display prices using `product_lst_price` (always tax-excluded), ignoring the website's tax display setting (`show_line_subtotals_tax_selection`). The cards template already had a proper override using `_get_combination_info()`, which correctly handles everything. Steps to reproduce: 1. Go to Website > Configuration > Settings > enable "Tax Included" 2. Create an appointment type with a product that has taxes 3. Edit website page > add "Appointments" snippet > select "Picture" or "List" layout => price shown is tax-excluded Ticket [link](https://www.odoo.com/odoo/project.task/5799252) opw-5799252 Forward-Port-Of: odoo/enterprise#123448 Forward-Port-Of: odoo/enterprise#106643
This update fixes an issue where users could create multiple accounts with the same email address, leading to confusion and potential data inconsistencies. The change ensures that only one user account is created per email, improving user experience and data integrity. This applies to both free signup and invitation methods.
Original PR description
Login uniqueness is enforced by a `UNIQUE (login)` constraint that Postgres compares byte for byte, so signing up with foo@example.com and then Foo@example.com produces two separate accounts pointing…
Login uniqueness is enforced by a `UNIQUE (login)` constraint that Postgres compares byte for byte, so signing up with foo@example.com and then Foo@example.com produces two separate accounts pointing at the same real mailbox. https://github.com/odoo/odoo/blob/66a6c16551041543b5addfe846f15e769b4e9afe/odoo/addons/base/models/res_users.py#L274 Even if the DB constraint did catch an exact-case duplicate and `_signup_create_user` re-raised it as a `SignupError`, the controller's friendly "already registered" branch only triggers when the duplicate lookup finds a row, and that lookup goes through `_get_login_domain` with an exact `=` operator. Case variants would fall into the generic "Could not create a new account" branch instead. https://github.com/odoo/odoo/blob/66a6c16551041543b5addfe846f15e769b4e9afe/addons/auth_signup/controllers/main.py#L68-L75 https://github.com/odoo/odoo/blob/66a6c16551041543b5addfe846f15e769b4e9afe/odoo/addons/base/models/res_users.py#L749-L750 `_signup_create_user` now refuses creation when a user with the same email already exists, applying to both b2c free signup and token-based invitations. It raises `UserError` directly so the controller's `except UserError` surfaces the message without a redundant lookup. The check uses `_get_email_domain`, whose base implementation is switched from `=` to `=ilike` over a value escaped via `tools.escape_psql` so `%` and `_` are matched literally rather than as wildcards. Its only existing caller is `reset_password`, which already wants case-insensitive matching. Steps to reproduce: 1. In Settings, set Customer Account to "Free sign up" and save. 2. Log out, then on the login page click "Don't have an account?". 3. Register with foo@example.com. 4. Log out again and click "Don't have an account?". 5. Register with Foo@example.com. => Two distinct user accounts are created for the same mailbox. opw-6199441 Forward-Port-Of: odoo/odoo#273071 Forward-Port-Of: odoo/odoo#263864
This update fixes a usability issue in the mobile Discuss app where actions within the bottom sheet were too small to easily click. The change ensures that CSS styling is correctly applied only to the Discuss bottom sheet, improving the user experience. This resolves a visual inconsistency and makes the app easier to use on mobile devices.
Original PR description
Before this commit, when using discuss in mobile, actions in bottom sheet were too small and hard to click. Steps to reproduce: - open a conversation in discuss with a message - click on "..." or…
Before this commit, when using discuss in mobile, actions in bottom sheet were too small and hard to click. Steps to reproduce: - open a conversation in discuss with a message - click on "..." or long-press the message This comes from changes in spreadsheet_dashboard were some CSS rules that were meant to impact only bottom sheet of spreadsheet_dashboard were actually impacting all the bottom sheets [1], including discuss actions. This commit fixes the issue by putting a specific class on the bottom sheet menu in spreadsheet_dashboard, so that the CSS rule can be made specific to spreadsheet_dashboard and not affect other bottom sheets like the ones used in Discuss app. [1]: https://github.com/odoo/odoo/pull/239190 Before <img width="657" height="524" alt="Screenshot 2026-07-10 at 14 18 38" src="https://github.com/user-attachments/assets/6e4cd083-3d4c-4c38-af89-93819e6eb1a8" /> After <img width="656" height="527" alt="Screenshot 2026-07-10 at 14 18 25" src="https://github.com/user-attachments/assets/48e66648-7f5e-4e42-8a64-b85499aceb97" />
This update resolves an issue where sales orders could incorrectly show analytic distributions exceeding 100%, leading to confusion and inaccurate reporting. The change consolidates analytic distributions from multiple models into a single line, maintaining functionality while preventing this over-allocation. This ensures accurate reporting and simplifies the analytic distribution process.
Original PR description
Steps: 1. Create an analytic model filtered by partner. 2. Create an analytic model filtered by product. 3. Create a project with an analytic distribution. (Make sure the distributions use different plans) 4. Create an SO for a product within the project that both the models apply to. 5. Confirm the SO. 6. Notice the analytic distribution for the project account is at 200%. When an SOL is created, the analytic distribution from each model is added as a separte line The analytic account for the project is added to each analytic distribution line. This can easily cause the account to have >100% distribution for a given SOL. This is unintuitive and confusing behaviour. This PR changes the behaviour to only create one line for all the distributions from analytic models. This should prevent this behaviour while keeping the functionality of applying the project distribution to each line. opw-6250908 / opw-6304033 Forward-Port-Of: odoo/odoo#270151
This update resolves an issue where old, reconciled transactions were incorrectly linked to new invoices, causing errors. The fix ensures that only active, posted transactions are associated with invoices, preventing invoice generation problems with cancelled payments.
Original PR description
Use case -------- A recurring sale order, is automatically invoiced. The transaction is postprocessed and crash with this traceback ``` File…
Use case
--------
A recurring sale order, is automatically invoiced. The transaction is postprocessed and crash with this traceback
```
File "/home/odoo/src/enterprise/saas-19.2/sale_subscription/models/sale_order.py", line 1881, in _handle_automatic_invoices
invoice._post()
File "/home/odoo/src/custom/private/openerp_enterprise/models/subscription_assignation.py", line 439, in _post
posted_moves = super()._post(soft=soft)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/custom/private/openerp_enterprise/models/account.py", line 193, in _post
posted = super()._post(soft)
^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/enterprise/saas-19.2/hr_payroll_expense/models/account_move.py", line 21, in _post
res = super()._post(soft=soft) # Posting will automatically reconcile same-account-same-matching lines
^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/enterprise/saas-19.2/l10n_in_reports/models/account_move.py", line 135, in _post
to_post = super()._post(soft=soft)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/enterprise/saas-19.2/account_loans/models/account_move.py", line 20, in _post
posted = super()._post(soft)
^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/enterprise/saas-19.2/sale_subscription/models/account_move.py", line 21, in _post
posted_moves = super()._post(soft=soft)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/enterprise/saas-19.2/account_invoice_extract/models/account_invoice.py", line 235, in _post
posted = super()._post(soft)
^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/enterprise/saas-19.2/account_asset/models/account_move.py", line 130, in _post
posted = super()._post(soft)
^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/addons/l10n_it_edi/models/account_move.py", line 360, in _post
return super()._post(soft)
^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/addons/l10n_in_edi/models/account_move.py", line 156, in _post
res = super()._post(soft=soft)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/addons/account_peppol_response/models/account_move.py", line 42, in _post
res = super()._post(soft)
^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/addons/sale/models/account_move.py", line 149, in _post
invoice.js_assign_outstanding_line(line.id)
File "/home/odoo/src/enterprise/saas-19.2/account_accountant/models/account_move.py", line 589, in js_assign_outstanding_line
super().js_assign_outstanding_line(line_id)
File "/home/odoo/src/odoo/saas-19.2/addons/account/models/account_move.py", line 6430, in js_assign_outstanding_line
return lines.reconcile()
^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/addons/account/models/account_move_line.py", line 3324, in reconcile
return self._reconcile_plan([self])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/addons/account/models/account_move_line.py", line 2978, in _reconcile_plan
plan_list, all_amls = self._optimize_reconciliation_plan(reconciliation_plan)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/addons/account/models/account_move_line.py", line 2940, in _optimize_reconciliation_plan
amls._check_amls_exigibility_for_reconciliation(shadowed_aml_values=shadowed_aml_values)
File "/home/odoo/src/odoo/saas-19.2/addons/account/models/account_move_line.py", line 2832, in _check_amls_exigibility_for_reconciliation
raise UserError(_("You can not reconcile cancelled entries."))
You can not reconcile cancelled entries.
```
Because an old transaction: state = 'reconciled' but is_reconciled is false get attached to the new invoice. The the tx.payment_id.move_id was cancelled by the accounting team, and the invoice was reconcilled directly with the bank statement.
So we end up with a cancelled move that block any further invoice for this subscription.
Solution
--------
According to accounting team, the move_id of the payment is always posted except if there is some manual intervention. We make sure we link only transaction with payment with posted moved
opw-6368231
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#275445
Forward-Port-Of: odoo/odoo#274764