Daily updates from Odoo
Wednesday, July 8, 2026
72 changes · saas-19.3
Enhancements to existing features
Hong Kong payroll can now use the scheduled payroll data update process. This helps keep standard, unmodified salary rules up to date automatically, reducing manual maintenance for payroll teams.
Original PR description
Currently, the "Payroll: Update data" cron doesn't work for HK payroll as we never set up the _get_data_files_to_update. We can set up the list of data files to keep up to date to better support our users by automatically keeping non-edited salary rules up to date. task-6360339 Forward-Port-Of: odoo/enterprise#122777
Belgian blackbox POS sessions now warn cashiers when they need to be closed within the required 24-hour period, helping ensure Z reports are sent on time. Session and sales reports now handle negative order lines in the required refund summaries, and self-ordering with a blackbox now requires the appropriate Belgian blackbox self-ordering support.
Original PR description
Belgian fiscal regulations require a blackbox POS session to be closed at least once every 24h so the Z reports are sent to the FDM. The POS now warns the cashier once the session should be closed. Also: - include negative lines of regular orders in the negative quantities summary of session reports, and show them as positive amounts in the refund tables of the sale details report as required by the SPF - require `l10n_be_pos_blackbox_self_order` when self-ordering (mobile/kiosk) is enabled with a blackbox community PR: https://github.com/odoo/odoo/pull/273994
Belgian payroll rules are updated with the new employment bonus parameters taking effect on 1 July 2026 and 1 September 2026. This helps ensure payslip calculations remain aligned with Belgian payroll requirements for the affected periods.
Original PR description
Update the employment bonus parameters for 1st July 2026 and 1st September 2026. task-6369633 Forward-Port-Of: odoo/enterprise#123262
The Azerbaijani Manat (AZN) now uses its official currency symbol, ₼, in Odoo. This improves consistency and makes financial documents and displays more accurate for users working with this currency.
Original PR description
This commit updates the base currency symbol for the Azerbaijani Manat (AZN) to its official Unicode character '₼'. Related Upgrade PR: https://github.com/odoo/upgrade/pull/10107 task-6112867 Forward-Port-Of: odoo/odoo#262471
Backorder creation during receipt validation has been optimized so it no longer slows down or times out on very large operations. This should make large warehouse processing more reliable and reduce the risk of database memory errors.
Original PR description
## The Problem Validating receipts with a large number of operations by creating a backorder was timing out due to $O(N*M)$ filtering inside `_get_qty_received_without_self`, where N is the number of…
## The Problem Validating receipts with a large number of operations by creating a backorder was timing out due to $O(N*M)$ filtering inside `_get_qty_received_without_self`, where N is the number of moves in the picking, and M is the number of moves being processed for valuation. This happened while computing price units for moves one by one: each move filtered all picking moves to find the ones with the same `product_id`. Another issue was a PostgreSQL `"memory exhausted"` error caused by generating a large number of OR'ed conditions, equal to the number of processed moves. ## The Solution The massive filtering was fixed by filtering moves of the `purchase_line` instead of the `picking`, which is typically associated with only a few moves. This is still correct as the loop just after already ignores moves that don't have the same `purchase_line_id` of `self` anyways. The PostgreSQL error was fixed by grouping moves by `location_dest_id` and generating one condition per location using an `in` clause, which is typically much smaller than generating one condition per move. ## Benchmark Benchmark on a customer database, validating a receipt with 10k+ operations by creating a backorder: ```text Time: timeout -> 6 min ``` OPW-6272667 Forward-Port-Of: odoo/odoo#273555 Forward-Port-Of: odoo/odoo#269350
This change makes the tax supply date available for companies using the German localization. It helps ensure tax reporting can reflect the correct supply timing, which supports more accurate compliance handling.
Original PR description
Forward-Port-Of: odoo/odoo#272461
This update reduces the time needed to load forum pages, especially the most visited post pages on odoo.com. It speeds up database work and cuts overall page load time almost in half, which helps the site handle very high traffic more efficiently.
Original PR description
This PR improves the cost of `/forum/my-forum-1/my-slug-1234` by ~48%. This has a huge impact on odoo.com The `/forum/...` routes are the `#1` on odoo.com in terms of absolute count and in terms of…
This PR improves the cost of `/forum/my-forum-1/my-slug-1234` by ~48%. This has a huge impact on odoo.com The `/forum/...` routes are the `#1` on odoo.com in terms of absolute count and in terms of CPU and SQL cost. They are called several million times a day. The average total time for `/forum/my-forum-1/my-slug-1234` goes from ~358ms to ~187ms (sql: 107ms -> 52ms - cpu 250ms -> 135ms) This has been tested by extracting 30k real forum post urls from odoo.com logs and replaying them on a staging server. That day `/forum/...` routes were called 2.8M times ## before <img width="1343" height="122" alt="image" src="https://github.com/user-attachments/assets/2b8264b7-4f26-40a7-a20a-20d478f5a93a" /> ## after <img width="1339" height="124" alt="image" src="https://github.com/user-attachments/assets/7c73aeb0-a4b4-45b8-ae98-093ac70c438e" /> ### First commit before <img width="1857" height="946" alt="image" src="https://github.com/user-attachments/assets/63b9d966-f5fd-4047-b4cb-533b7e9491bc" /> after <img width="1844" height="867" alt="image" src="https://github.com/user-attachments/assets/9494efe0-0b7c-419d-b918-dcfe5e942e63" /> query plan for most used tags as public user: - with the index https://explain.dalibo.com/plan/gbf9fbd358687f3e - without the index https://explain.dalibo.com/plan/da5gg6cd27f67496 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272716
The point of sale sales details report now treats negative order lines consistently based on their sign. This helps local setups, such as Belgian POS reporting, classify negative lines in normal orders as refunds with the correct positive amounts and taxes.
Original PR description
Amounts and taxes now consistently follow the order line sign, allowing localizations (e.g. l10n_be_pos_blackbox) to report negative lines of regular orders as refunds with positive amounts. enterprise PR: https://github.com/odoo/enterprise/pull/122411 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update enhances how the French PDP tracks partner verification status. Previously, verification states were logged inconsistently. Now, a specific PDP-related verification state is displayed in the chatter, ensuring accurate representation of the receiver's status when routed through the French PDP annuaire – a critical distinction for French businesses.
Original PR description
In saas-19.3, `account_peppol` no longer uses `_log_verification_state_update()` and now logs partner verification changes through `_track_add()`. French PDP still needs to display a PDP-specific verification state in the chatter instead of the raw Peppol state, because the same Peppol technical result can have a different business meaning when the receiver is routed through the French PDP annuaire. Task-6296383
Resolved issues and error corrections
This fix prevents Belgian CODA bank statement imports from crashing when the file contains a type 4 block. Businesses using Belgian bank statement imports can now process these files reliably without manual workarounds.
Original PR description
### Issue: After the fix in commit (https://github.com/odoo/enterprise/commit/3ef8ae7a6b8eb362e18c74dfab9aadce792b5dc2), importing a CODA file containing a type 4 block raises a traceback ### Cause: That commit introduced `communication_struct_by_ref_move`, which iterates over all lines and accesses `line['communication_struct']` Type 4 lines are not assigned a `communication_struct` value by the parser in `_get_coda_file_statements` Accessing the key directly raises a `KeyError` in `_get_coda_final_statements` in `communication_struct_by_ref_move` ### Steps to reproduce: - Install `l10n_be_coda` - Switch to the BE company - Create a Bank Journal with account `BE33737018595246` - Go to the Accounting Dashboard and import a CODA file containing a type 4 block (Like the one on the ticket) Before the fix, a traceback is raised on import opw-6363148 Forward-Port-Of: odoo/enterprise#123222
Pasting document links into an empty message no longer adds an unnecessary blank line at the start. Messages look cleaner while still keeping links separated when text is already present.
Original PR description
Before this commit, adding document links always prepended a line break before the generated links. When the composer was empty, this resulted in messages starting with an unnecessary blank line. This commit only inserts a line break when the composer already contains text, avoiding the extra spacing while preserving the separation between existing content and pasted links. task-[5947683](https://www.odoo.com/odoo/project/1519/tasks/5947683) Forward-Port-Of: odoo/enterprise#120952
Scanning a component product barcode on the shop floor now opens or selects the matching item even when that component is tied to a specific manufacturing operation. This prevents operators from being blocked by scans that previously appeared to do nothing, improving reliability during work order processing.
Original PR description
When scanning a product barcode in the shopfloor, it would not simulate a click on the product if the corresponding BoM line was linked to an operation. Steps to reproduce: ------------------- * Create a BoM for product A with a BoM line for product B and link it to any operation. * Create a WO for product A and confirm it * Open the shopfloor with the WO and scan the barcode of product B > Observation: Nothing happens Why the fix: ------------ We simulate the onClick for the product even if the move is not marked manual_consumption. opw-6268665 Forward-Port-Of: odoo/enterprise#122337
New planning shifts now use the employee or user's local timezone when setting default hours. This prevents shifts from appearing offset, such as 10 AM to 6 PM instead of the expected 8 AM to 4 PM, improving scheduling accuracy.
Original PR description
Before: When creating a new shift, we set 8 AM - 4 PM as the default hours in UTC. With the timezone in Belgium, this becomes 10 AM - 6 PM. After: Change the timezone of the new shift to match the user's timezone. This will make the hours always be from 8 to 4 (working hours) --- task-6285596 Forward-Port-Of: odoo/enterprise#120062
This fixes an error that could block Sendcloud batch deliveries when a transfer was split into multiple packages. Businesses can now validate those deliveries normally, ensuring labels are generated without interruption.
Original PR description
Issue ----- When using Sendcloud batch deliveries, users get a traceback if the transfer is split into multiple packages. Steps to reproduce ----- - Setup Sendcloud - Enable batch delivery - Create a…
Issue ----- When using Sendcloud batch deliveries, users get a traceback if the transfer is split into multiple packages. Steps to reproduce ----- - Setup Sendcloud - Enable batch delivery - Create a delivery for 2 units of a product - Delivery method set to sendcloud - Put each unit in a separate package - Validate the delivery > Traceback Cause ----- Bug introduced by #90302 (so only present in 19.3+). When using the batch deliveries option, the packages are sent in a single list. This means that, in `_prepare_parcel`, when we iterate over the packages, `pkg` can be a list https://github.com/odoo/enterprise/blob/2c89c8e5da7dcb83d5a60c9616de4d5d07f0b9ab/delivery_sendcloud/models/sendcloud_service.py#L462 This means that `pkg.name` will fail https://github.com/odoo/enterprise/blob/2c89c8e5da7dcb83d5a60c9616de4d5d07f0b9ab/delivery_sendcloud/models/sendcloud_service.py#L481 Considering that a batch delivery uses the same reference number on all labels, we can pass `None` as a fallback to `_get_unique_order_number_reference` (it is an accepted value) https://github.com/odoo/enterprise/blob/2c89c8e5da7dcb83d5a60c9616de4d5d07f0b9ab/delivery_sendcloud/models/sendcloud_service.py#L388-L393 ----- Ticket: opw-6267928
The live field service map no longer recalculates routes when the current user's position changes. This avoids unnecessary route processing and helps preserve routing service tokens without changing the displayed routes.
Original PR description
The routing fetching in `updateUserPosition` of the `MapModel` should not be triggered for the live map. Right now, when changing user position, the routes are fetched again. However, for the live map, this should have no effect on routes, as they start from the user pins instead of the current user's position. This will avoid computing all over routes and preserve tokens. task-6307279
Fixes an issue where opening the AI chat could hide the dialog used to add snippets in the fullscreen mailing editor. Users can now add mailing blocks normally without the editor appearing frozen or blocking save and discard actions.
Original PR description
When an AI chatbox is active, all non-error dialog overlays are set to be behind the chatbox through their z-index. This causes an issue where the dialog overlay that adds new snippets to a mailing is placed behind the fullscreen mailing editor, preventing its use and freezing the use of some commands (save & discard). This commit restores the snippet dialog's z-index to its original value. Steps to reproduce: - Create a new mailing - Select a builder-enabled theme (such as Events Promo) - Open a new AI chat by clicking the AI icon in the top right - Open the fullscreen editor - Click on the Headers block category task-6321624 Forward-Port-Of: odoo/enterprise#123276
This fix corrects how eco cheques are calculated in Belgian payroll so employees receive the right voucher amounts. It also updates related payroll and accounting tests to help prevent future mistakes in payslip validation, reporting, and performance checks.
Original PR description
Forward-Port-Of: odoo/enterprise#119074
This fix prevents delivery tracking from failing when EasyPost returns an empty tracker value. Users can continue viewing shipment information without encountering an error screen in affected delivery workflows.
Original PR description
The PR https://github.com/odoo/enterprise/pull/111833 handled the specific case when the tracker data is missing from the EasyPost response, however in certain cases `tracker` key exists, but it has a `None` value, which leads to a traceback when trying to access the stock move:
```
File "/home/odoo/src/enterprise/18.0/delivery_easypost/models/easypost_request.py", line 392, in get_tracking_link
public_url = shipment.get('tracker', {}).get('public_url')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'get'
```
This commit provides a fallback to avoid getting the error from the side of the user.
opw-6270242
Forward-Port-Of: odoo/enterprise#119400The Swiss payroll time off request form now consistently shows the start date field. This prevents confusion and ensures employees can always enter the required date regardless of the type of time off request.
Original PR description
The time off request view was showing the request_date_from field conditionally, which makes no sense as you would always need to pick a date for a time off no matter which unit the request uses. runbot-241099
The website now shows the exact discount percentage configured for subscription products when prices are displayed tax-included. This prevents customers from seeing an incorrect lower discount, such as 4% instead of 5%, due to rounding precision.
Original PR description
Steps to reproduce: 1. Install eCommerce and Subscriptions. 2. Create a 21% Excluded tax. 3. Create a subscription product with a price of 45 with 21% tax and enable "Accept One-Time" in the…
Steps to reproduce: 1. Install eCommerce and Subscriptions. 2. Create a 21% Excluded tax. 3. Create a subscription product with a price of 45 with 21% tax and enable "Accept One-Time" in the Recurring Prices tab. 4. Publish the product on the website under the Sales tab. 5. Create a pricelist for 6 months recurring with two lines: - If min quantity is 0, then 0% discount - If min quantity is 2, then 5% discount 6. Set "Display Product Prices" to "Tax Included" in the Settings. 7. Open the product on the website, select the 6-month plan, and increase quantity to 2. Issue: The discount percentage displayed on the website shows 4% instead of the configured 5%. Why this happens: In `_get_additionnal_combination_info`, the discount is reverse-calculated from the tax-included price vs the tax-included sales price. When the 21% tax is included to both prices, it introduces a floating-point precision loss (4.9954..%), which floor() then truncates to 4%. Fix: When the pricelist rule uses 'percentage' discount, read `percent_price` directly from the pricing rule instead of reverse-calculating from tax-adjusted prices, as it represents the exact discount percentage the merchant configured with no floating-point involvement. opw-6224735 Forward-Port-Of: odoo/enterprise#121654
Marketing Automation now shows the correct reason when a campaign participant is removed because they no longer match the campaign filter. This avoids misleading users with a "Record deleted" message when the underlying lead or record still exists.
Original PR description
`sync_participants` calls `action_set_unlink` on every participant whose record is no longer in the campaign domain, and `action_set_unlink` writes "Record deleted" on each scheduled trace. The…
`sync_participants` calls `action_set_unlink` on every participant whose record is no longer in the campaign domain, and `action_set_unlink` writes "Record deleted" on each scheduled trace. The removed bucket also contains records that still exist but no longer match the campaign filter, so the cancelled trace dialog shows "Record deleted" even when the record was only filtered out. In `sync_participants`, the to_remove participants are split between those whose record still exists in the database (filtered out by the campaign domain) and those whose record was actually deleted. `action_set_unlink` accepts an optional `trace_message` argument, defaulting to "Record deleted", and the filtered-out batch passes "Record no longer matches campaign filter" so the cancelled trace dialog reflects the real cause. Steps to reproduce: 1. Install Marketing Automation and CRM. 2. Open Marketing Automation, create a campaign on Lead with filter Stage = New. 3. Add a begin activity to the workflow. 4. Open CRM, create a Lead in the New stage. 5. Back in the campaign, click Generate Participants. 6. In the CRM pipeline, drag the Lead from New to Qualified. 7. Back in the campaign, click Generate Participants again. 8. Open the Participants smart button, click the participant for the moved Lead. 9. Click the cancelled activity in the workflow timeline. => The activity dialog shows "Error message: Record deleted" although the Lead still exists. Ticket [link](https://www.odoo.com/odoo/project/49/tasks/6251264) opw-6251264 Forward-Port-Of: odoo/enterprise#118692
Shop floor operators can now see employee profile photos instead of generic placeholder images when viewing or changing operators. This makes it easier to identify the right person during manufacturing work without requiring HR-level access.
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#121751
Hong Kong payroll now handles payslips with missing start or end dates without crashing. This prevents interruptions when users edit payslip periods and ensures related wage and end-of-year pay calculations are safely skipped until valid dates are provided.
Original PR description
Currently, an error occurs when a user removes the payslip date. **Steps to Reproduce:** - Install `l10n_hk_hr_payroll` with demo data. - Switch to the `Hong Kong` company. - Go to `Payroll` >…
Currently, an error occurs when a user removes the payslip date. **Steps to Reproduce:** - Install `l10n_hk_hr_payroll` with demo data. - Switch to the `Hong Kong` company. - Go to `Payroll` > `Payslips` > `Payslips`. - Create a `payslip` and remove the `start` or `end` period. **Error 1:** `TypeError: unsupported operand type(s) for +: 'bool' and 'relativedelta'` **Error2:** `AttributeError: 'bool' object has no attribute 'month'` When a user removes the start or end date of a payslip, the system computes the Average Daily Wage. Based on the payslip dates, it finds the previous year's payslips [1]. If the start or end date is not set, it raises an error [2]. For the second error, when computing whether to include EOY pay, it compares the company's EOY pay date with the end date's month. If the end date is not set, accessing its month raises an error [3]. This commit ensures that when retrieving previous-year payslips, if the start or end date is not set, it returns an empty payslip recordset. It also ensures that when computing whether to include EOY pay, if the end date is not set, `include_eoy_pay` is set to `False`. [1]: https://github.com/odoo/enterprise/blob/ec8a009794863090351d91650aff727e6fbeab7e/l10n_hk_hr_payroll/models/hr_payslip.py#L124 [2]- https://github.com/odoo/enterprise/blob/ec8a009794863090351d91650aff727e6fbeab7e/l10n_hk_hr_payroll/models/hr_payslip.py#L209-L215 [3]- https://github.com/odoo/enterprise/blob/ec8a009794863090351d91650aff727e6fbeab7e/l10n_hk_hr_payroll/models/hr_payslip.py#L141 Forward-Port-Of: odoo/enterprise#123135 Forward-Port-Of: odoo/enterprise#120586
Popover content now looks more consistent and easier to use in dark mode. Secondary buttons stand out as clickable actions, and forms inside popovers better match the surrounding popover background.
Original PR description
Before this commit, content like form & secondary button rendered inside popovers had inconsistent styling in dark mode: - secondary buttons did not stand out properly from the popover background, making them look like plain text blocks rather than actionable buttons; - forms rendered inside popovers (such as the multi-create popover) kept their default background, which visually clashed with the popover background. This commit fixes these issues by: - adding dedicated secondary button background colors for popovers, including hover state; - aligning form backgrounds inside popovers with the popover background. task-6249985 Forward-Port-Of: odoo/enterprise#121631
International shipments through Easypost now use the sale order currency on commercial invoices when available, instead of defaulting to the company currency. This helps ensure customs paperwork matches customer pricing and reduces errors for cross-border deliveries.
Original PR description
Issue ----- When shipping internationally with Easypost, the currency on the commercial invoice does not always match the one of the sale order. Steps to reproduce ----- - Install Easypost - Create a…
Issue ----- When shipping internationally with Easypost, the currency on the commercial invoice does not always match the one of the sale order. Steps to reproduce ----- - Install Easypost - Create a new pricelist using a different currency from the company - Create a SO - Some product with a weight & HS code - Customer must be in another country from company (for commercial invoice) - Use the new pricelist - Add easypost delivery - Confirm SO - Validate linked picking > Commercial invoice uses company currency instead of SO's Cause ----- The currency being sent to Easypost is retrieved from the package in https://github.com/odoo/enterprise/blob/4c540f450d4de8b59b871662123f85ed54cca2a9/delivery_easypost/models/easypost_request.py#L146 The package object is actually created by calling the carrier's `_get_packages_from_picking` method https://github.com/odoo/enterprise/blob/4c540f450d4de8b59b871662123f85ed54cca2a9/delivery_easypost/models/easypost_request.py#L266-L270 Solution ----- We could be fixing this in `stock_delivery` by creating the package with the correct currency when calling `_get_packages_from_picking`. The problem with this approach is that this might negatively affect other carrier services, as discussed in https://github.com/odoo/odoo/pull/268224. Instead, we can apply a band-aid fix to take the currency from the picking's sale in the `delivery_easypost` module, which is the only one where the problem was reported. ----- Ticket: opw-6224883 Forward-Port-Of: odoo/enterprise#123083
Lazada order synchronization no longer updates individual item statuses when they differ from the overall order status. This prevents sync failures caused by trying to reduce quantities for items that were already delivered, helping orders continue importing reliably.
Original PR description
Lazada stores order statuses at the item level. When an item is canceled, we mirrored this by decreasing the ordered quantity on the sale order line. But if the item was already delivered, decreasing the quantity below the delivered amount is forbidden and raises a `UserError`, which aborts the whole order sync:
```python
File ".../sale_stock/models/sale_order_line.py", line 420, in _update_line_quantity
raise UserError(_('The ordered quantity of a sale order line cannot be decreased below the amount already delivered. [...]'))
```
In practice, item-level statuses only differ from the order status in exceptional cases. Stop syncing statuses at the item level and assume the entire order shares a single status, which avoids the quantity decrease and the resulting traceback.
opw-6267730
Forward-Port-Of: odoo/enterprise#122851This update makes the AI test suite skip markdown rendering checks when the optional markdown component is not installed. It helps avoid false failures in environments that do not include that optional dependency, improving reliability of validation without changing user-facing features.
Original PR description
markdown2 is an optional dependency, so `markdown_format` can fail to process markdown, in which case all the markdown tests fail. Skip the markdown rendering test if there's no markdown rendering to test. Forward-Port-Of: odoo/enterprise#123002
This fix prevents confirmed sales orders from accidentally adding recurring products without a required subscription plan when using the catalog view. It makes validation consistent with manual order line entry, reducing billing setup mistakes and subscription order inconsistencies.
Original PR description
Steps to reproduce: --------------------------------------- 1. Install Subscription Module 2. Create and Confirm SO with no recurring plan and a non-recurring product 3. Add a recurring product >…
Steps to reproduce: --------------------------------------- 1. Install Subscription Module 2. Create and Confirm SO with no recurring plan and a non-recurring product 3. Add a recurring product > Save SO > Observe the User Error 4. Now add the same recurring product through Catalog View Observation: --------------------------------------- No User Error raised stating 'You cannot save a sale order with recurring product and no subscription plan.' Issue: --------------------------------------- When you manually add a line and click 'Save', the constraint (`_constraint_subscription_plan`) is triggered and raised `UserError` https://github.com/odoo/enterprise/blob/434d88960abb5e424fdc1106fc93935d328bff78/sale_subscription/models/sale_order.py#L176-L177 When you add a product via the catalog view, it calls `_update_order_line_info` which directly creates/updates order lines, Which do not trigger the python constraint. https://github.com/odoo/odoo/blob/ef9772bba1515bdaf5410c3af5a3e395f562d513/addons/sale/models/sale_order.py#L1926-L1933 Solution: --------------------------------------- Two private helpers are introduced: * `_is_exempt_from_subscription_plan_check`: single source of truth for all exempt states (draft, cancelled, upsell, and legacy upgrade orders). * `_check_recurring_plan_mismatch`: raises a `UserError` when the order has or will have a recurring product but no subscription plan, reusing the exemption helper so both call sites stay in sync. `_constraint_subscription_plan` is refactored to delegate to these helpers, and `_update_order_line_info` is overridden to call `_check_recurring_plan_mismatch` before the catalog update is applied, ensuring consistent validation across both entry points. opw-6194865 Forward-Port-Of: odoo/enterprise#122799 Forward-Port-Of: odoo/enterprise#117879
Fixed a barcode workflow issue where scanning an existing package followed by a package type could create a new package without assigning it to the products. Warehouse users now get the expected destination package set correctly, avoiding missing package links during delivery processing.
Original PR description
When scanning a package then a package type, from the point of view of the user nothing happend, and in the backend it will created a new package but it will not link it to the products nor will it…
When scanning a package then a package type, from the point of view of the user nothing happend, and in the backend it will created a new package but it will not link it to the products nor will it show any warning. Steps to reproduce: ------------------- * Install barcode and stock * Enable packages in settings * Open Inventory * Create a product, * Create a Package Type -> barcode PACKTYPE, * Create a Package linked to this package type -> PACK, * Add at least 2 unit of product to this package, * Create a delivery for 2 unit of the product, Open Barcode * Operation > Delivery orders > your delivery * Erase the destination package from the first line * Scan PACK ( don't click on the green line) * Scan PACKTYPE **Actual behavior** create a new package but does not link it to the new products **Expected behavior** create a new package and set it as destination package. Observation: ------------- When scanning the package (PACK), we will go through ```_processPackage``` -> ```async _processPackage``` where in the end the line is unselected: https://github.com/odoo/enterprise/blob/39d8a473fe03038ca0494a6a8165e3eb75bd8492/stock_barcode/static/src/models/barcode_picking_model.js#L2090 When we scan our package type (PACKTYPE), we will go to ``` _processPackage``` -> ```_processPackage```->```_processPackageType``` where we will obtains packagesIds checking that we have a source package: https://github.com/odoo/enterprise/blob/7cd9834d1d918f12dec43844cae6f112309e5772/stock_barcode/static/src/models/barcode_picking_model.js#L2123-L2132 and will send us to ```_putPackInPack```: https://github.com/odoo/enterprise/blob/7cd9834d1d918f12dec43844cae6f112309e5772/stock_barcode/static/src/models/barcode_picking_model.js#L2133-L2136 Where we will avoid the empty packageIds since we checked on the source package and not the destination package: https://github.com/odoo/enterprise/blob/2b887d094c66be7aebd92fbf735b1852f5dde4b5/stock_barcode/static/src/models/barcode_picking_model.js#L2296-L2299 and will call ```action_put_in_pack``` from the packaging model: https://github.com/odoo/enterprise/blob/2b887d094c66be7aebd92fbf735b1852f5dde4b5/stock_barcode/static/src/models/barcode_picking_model.js#L2301-L2306 In ```action_put_in_pack``` will create a new packaging and put it as a the new destination package, but since the ```previous_dest_package``` (saved in db) was itself, he will [erase the link](https://github.com/odoo/odoo/blob/cda011dc8590773f6c3a26f4ae9d5242a3147024/addons/stock/models/stock_package.py#L354-L363) he just made. Which means that in our case, we created a package without linking it to anything. Even if we avoid the function to erase the destination package, since the destination package shown in barcode is the one from move line : https://github.com/odoo/enterprise/blob/d0d0a3cf4a02bf24cf502b533e494fe7ca155eb3/stock_barcode/static/src/components/line.js#L115-L117 It will not show the new package in barcode opw-5449729 Forward-Port-Of: odoo/enterprise#119758 Forward-Port-Of: odoo/enterprise#104876
The Turkish Central Bank exchange rate provider now uses the official selling rate instead of averaging buying and selling rates. This helps keep accounting and import valuations aligned with Turkish customs requirements and reduces foreign exchange mismatches.
Original PR description
## Short fix summary: The TCMB (Central Bank of Turkey) provider computed the exchange rate as an average of the buying and selling rates (`2 / (ForexBuying + ForexSelling)`). This is inaccurate for real accounting flows and does not follow Turkish customs regulation (Customs Law No. 4458, Art. 30), which requires the Central Bank's selling rate for goods import valuation. This now uses the selling rate (`ForexSelling`) only. task-6227500 I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/enterprise#122770
This fix keeps Knowledge file navigation working consistently after a Chrome browser change. It prevents an internal browser return-value change from altering how the app handles scroll actions, reducing the risk of small usability issues for users.
Original PR description
Since Chrome 150, scrolling methods like `scrollIntoView()` return a Promise instead of `undefined`. This commit adds block braces to ensure the action returns `undefined` and keeps the same behavior as before. Reference: - https://chromestatus.com/feature/5082138340491264 - https://chromium.googlesource.com/chromium/src/+/50f3e3d0a9bc02aad8b8161dbdd59046991dd2c7 runbot-941309 Forward-Port-Of: odoo/enterprise#123231 Forward-Port-Of: odoo/enterprise#123031
The Peru Profit and Loss report now includes Other Operating Income when calculating gross profit. This ensures gross profit, operating profit, pre-tax result, and net profit totals reflect all relevant income correctly.
Original PR description
**Steps to reproduce:** 1. Install `l10n_pe` and switching to the Peru company. 2. Create and post a journal entry with a line on account 7520000 (Other Operating Income). 3. Open the Profit and Loss report (PE). 4. The amount appears correctly under "Other operating income" (`PE_PNL_A_5`). 5. "Gross profit", "Operating profit" , "Result before taxes" and "Net Profit" do not change when this amount is added or removed. **Issue:** The "Other operating income" line is excluded from the Gross Profit calculation, and consequently from Operating Profit and every downstream total in the PE Profit and Loss report. **Why this happens:** Gross Profit (`PE_PNL_A_4`) balance expression uses the aggregation with formula `PE_PNL_A.balance - PE_PNL_A_3.balance`, which doesn't include `PE_PNL_A_5.balance` as a term opw-6283907 Forward-Port-Of: odoo/enterprise#123063
Mobile self-ordering now follows the same printing flow as kiosks, so preparation receipts are printed correctly after order changes. The update uses IoT Box websocket printing when mobile devices cannot use the local long-polling connection, reducing failed kitchen or preparation printouts.
Original PR description
Self ordering mobile now aligns on kiosk avoiding to update last order changes, which would prevent from printing preparation receipts. This is made possible by the IoT Box allowing to print receipts through websockets. We also take the opportunity to update the `iot_http` service in order to allow updating methods available on the service: it allows us adding a new method to disable longpolling for self ordering mobile, which would always fail, to end up using websocket (clients are not on the same network as the IoT Box). Forward-Port-Of: odoo/enterprise#121013
DHL deliveries could fail for customers or warehouses in regions whose province code was only one character, such as Barcelona. The DHL connector now sends the longer official province code where needed, helping shipments validate successfully.
Original PR description
Steps:
- Install delivery_dhl_rest
- Create a new customer with barcelona as address
- Create a new Delivery
- Set DHL
- Validate de delivery
- Validation error #/customerDetails/receiverDetails/postalAddress/provinceCode: expected minLength: 2, actual: 1
DHL requires `provinceCode` to be at least 2 characters. Several countries in `res.country.state` data use single-character codes (e.g. ES: B, M, A…; AR: C, B, S…; CN: 京, 沪…). This caused API validation errors when shipping from or to addresses in those regions.
Add `PROVINCE_CODE_MAP`, a dict keyed by `(country_ISO2, state_code)`, mapping each offending code to its ISO 3166-2 form (e.g. ('ES', 'B') -> 'ES-B'). Both `_get_consignee_vals` and `_get_shipper_vals` now look up the map before sending `provinceCode`, falling back to the raw code for countries not in the map.
links: https://developer.dhl.com/api-reference/mydhl-api-dhl-express#shipments
opw-6341745
Forward-Port-Of: odoo/enterprise#122138Grid views now display the user-friendly label for grouped selection values when opening details from the magnifier. This avoids confusing internal codes such as "non_billable" appearing in list titles, making navigation clearer for users.
Original PR description
When grouping a grid view by a selection field and clicking on the cell magnifier, the list title showed the technical name (e.g. non_billable) instead of the display name (e.g. "Non Billable"). This commit adds a condition specifically for selection fields, ensuring that their display names are used. task-5980035 Forward-Port-Of: odoo/enterprise#122303 Forward-Port-Of: odoo/enterprise#120894
The French Intrastat export wizard now opens only the journal entries related to missing required Intrastat values. This prevents users from being sent to a full list of entries, making it easier to find and correct the specific records blocking the export.
Original PR description
Steps to reproduce: 1. Have a French company with intrastat report module installed 2. Create and validate a bill to another EU country, without filling out at least one of the required intrastat fields 3. Go to the intrastat report, and export it as XML DEBWEB2 4. In the export wizard, click on the internal links on the warning messages Issues: 1. In the Intrastat report in French localization, when there are missing values detected in the export, the Export Wizard shows internal links that lead to every journal entries - instead of showing only the relevant entries. The warning banner on the report uses the action action_invalid_code_moves which has a domain to limit what is shown on the view form. However in the method _fill_value_errors there was no domain. opw-6215339 Forward-Port-Of: odoo/enterprise#117997
This fix ensures website dynamic snippets use the correct filter settings regardless of the order in which modules were installed. It helps prevent generated website content or product snippets from pointing to the wrong data source, improving consistency for customers using website generation features.
Original PR description
Our default dynamic snippets filter ids are set based on the order that we install our modules. This can cause issues if the user installs their modules in a different order. To fix this, we need to update the data-filter-id value to the correct value of the DB. To be able to do this, we also change the regex replacement to use lxml instead since it's much simpler. Lxml part from 799f83575e162eb683cfaebb4eb602ccc1fbe466. Forward-Port-Of: odoo/enterprise#123143 Forward-Port-Of: odoo/enterprise#122671
Fixes a crash when users click the restriction fields for users or resources on appointment slots. The broken filter was removed because it did not provide useful filtering and prevented the form from working correctly.
Original PR description
Clicking the "Restrict to User" or "Restrict to Resources" field on a slot crashed with:
invalid input syntax for type integer: "appointment_type_id.staff_user_ids"
The field domain was a quoted string instead of a list, so it was passed through as a literal value. Remove the domain: it never filtered anything and only broke the form.
opw-6349497
Forward-Port-Of: odoo/enterprise#122651Blank U.S. checks now print the same stub lines as pre-printed checks, making payment details clearer and more complete. The check bottom layout was also adjusted so blank checks fit on a single page instead of spilling onto two pages.
Original PR description
See individual commits. task-6359599 Forward-Port-Of: odoo/enterprise#123144
Fixed an issue where the Balance Sheet report could crash after adding a custom Journal Item field in Studio and filtering by analytic account. This improves reliability for accounting users working with analytic accounting and customized journal item data.
Original PR description
Steps to reproduce ================== - Activate Analytic Accounting. - Go to Accounting > Accounting > Reconcile. - Open Studio. - Add a new many2many field. - Set Journal Item as the related model. - Go to Reporting > Balance Sheet. - Select an analytic account. => RecursionError: maximum recursion depth exceeded Cause of the issue ================== Calling `self.env['account.move.line'].fields_get()` will cause a recursion error. `account.report::_prepare_lines_for_analytic_groupby()` calls `account.move.line::_where_calc()` which in turns calls _prepare_lines_for_analytic_groupby again Solution ======== It turns out we don't actually need to retrieve the groupable attribute, thus bypassing the error. opw-6129149 Forward-Port-Of: odoo/enterprise#122244 Forward-Port-Of: odoo/enterprise#116251
Fixed an error that could appear when a chatbot tries to hand a conversation to an operator but none are available in the live chat channel. Instead of showing an error, the chat now handles this case cleanly, improving reliability for website visitors and support teams.
Original PR description
When a live chat channel has no operators configured and the chatbot script ends with a Forward to operator step, triggering that step causes a traceback. Steps to reproduce the error: - Install…
When a live chat channel has no operators configured and the chatbot script ends with a Forward to operator step, triggering that step causes a traceback. Steps to reproduce the error: - Install ``im_livechat`` module with demo data - Go to Live Chat > Configuration > Chatbots > Create a new chatbot > Add script > Step Type: Question > Set answers > Save > Add script > Step Type: Forward to operator > Only If: Set one of the above answers > Save - Go to Live chat > Channel > Click the configure channel on YourWebsite.com > Remove the operators > Save - Go to the chatbot > test > select the configured answer Traceback: ```py StopIteration ``` https://github.com/odoo/odoo/blob/8791cdcd89ea3cb56b1fac63b3e2ffbd2956a912/addons/im_livechat/controllers/chatbot.py#L65-L70 When the chatbot script reaches a Forward to operator step while no operator is configured in the live chat channel, no chatbot message is created. As a result, the generator iterates over an empty iterator, and the ``next()`` call raises a ``StopIteration`` exception, causing a traceback during the conversation. sentry-7435424405 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274615 Forward-Port-Of: odoo/odoo#261726
Pakistan’s currency symbol (PKR) will now appear before the amount instead of after it. This aligns displays with local market practice and helps make invoices and prices look more familiar to users in Pakistan.
Original PR description
This commit updates the symbol position of Pakistan's currency (PKR) to `before amount`, as previously it was displayed `after amount`, which is not the market practice; as shown over [here](https://drive.google.com/file/d/14pcqXTZSR0BygBXGvgQ6oCLRW0-4dYUh/view?usp=drive_link). This is a backport of [PR](https://github.com/odoo/odoo/pull/269204) task-6236452 Forward-Port-Of: odoo/odoo#271499
This update adjusts how an invoice import test checks partner bank account retrieval, using a smaller prebuilt XML sample instead of a generated file. It makes the test more reliable and easier to maintain without changing the business behavior of invoice imports.
Original PR description
Move the partner retrieval bank account number test to the `test_ubl_import_bis3_invoice_be_retrieve_partner.py` file and use a partial XML instead of a generated XML. Forward-Port-Of: odoo/odoo#274591 Forward-Port-Of: odoo/odoo#269995
Installing the Uruguay localization on demo databases no longer fails with a validation error. The change removes a redundant setting that was being applied twice, which avoids an error on journals that already contain validated entries.
Original PR description
**Issue:** Installing `l10n_uy` on a demo database raises a ValidationError from the `check_use_document` constraint since 19.3+. The error occurs because `ir_module.py:write()` re-applies…
**Issue:** Installing `l10n_uy` on a demo database raises a ValidationError from the `check_use_document` constraint since 19.3+. The error occurs because `ir_module.py:write()` re-applies module-specific template data to all companies with a matching chart template after installation. At that point, `demo_company_uy` already exists with `chart_template='uy'` and posted demo invoices, so `_load_data` ends up writing `l10n_latam_use_documents=True` to a journal that has validated entries. This write was previously suppressed by `_pre_reload_data`, which unconditionally removed journals from the data when found by xmlid. Commit 056b8e38ff84 (saas-19.3) narrowed that protection to only apply when `'type' in journal_data`. Since the module-filtered data never includes `type` (that field comes from `_get_account_journal` in the base `account` module, excluded by the module filter), the journal is no longer protected and the write triggers the constraint. **Versions:** 19.3+ **Fix:** remove `l10n_latam_use_documents=True` from `_get_uy_account_journal`. `l10n_latam_invoice_document` already sets this field for all LATAM companies via `_get_latam_document_account_journal`; l10n_uy was setting it redundantly. Task id: [6354499](https://www.odoo.com/odoo/project/49/tasks/6354499)
Invoices sent through Nilvera could get stuck with an intermediate status and stop being checked too early. This change keeps those invoices in the follow-up process until Nilvera provides the final result, reducing cases where documents appear permanently unresolved.
Original PR description
## Short fix summary:
Nilvera reports `Unknown` as a normal, transient `StatusCode` value (their own e-Archive API docs
list the enum as `unknown`/`waiting`/`succeed`/`error`) right after a document is sent, before their
daily batch resolves the final status. But `_cron_nilvera_get_invoice_status`'s search domain only
matches `l10n_tr_nilvera_send_status in ('waiting', 'sent')`, so once an invoice lands on `unknown` it
is never polled again — even after Nilvera later resolves the real status on their side. This adds
`unknown` to that domain so these invoices keep getting polled until Nilvera reports a final status.
task-6328589
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#274494
Forward-Port-Of: odoo/odoo#274311This update prevents the attendance timesheet report from breaking when custom fields with common names are added to employee records. It makes the database query more precise so the report continues to load correctly in customized databases.
Original PR description
In `hr_timesheet_attendance_report`, the SQL query was using unqualified columns (e.g. `date` instead of `ts.date`)
It was not an issue in standard, but if a customer adds a `date` or `check_in` column to `hr_employee`, the query becomes ambiguous and fails.
To solve the issue, we explicitly qualify `ts.date` and `hr_attendance.check_in`.
upg-4445460
```python
File "/home/odoo/src/odoo/19.0/addons/hr_timesheet_attendance/report/hr_timesheet_attendance_report.py", line 24, in init
self.env.cr.execute("""CREATE OR REPLACE VIEW %s AS (
File "/home/odoo/src/odoo/19.0/odoo/sql_db.py", line 440, in execute
self._obj.execute(query, params)
psycopg2.errors.AmbiguousColumn: column reference "date" is ambiguous
LINE 44: AND date <= CURRENT_DATE
```
Forward-Port-Of: odoo/odoo#274482
Forward-Port-Of: odoo/odoo#274341This change restores the earlier behavior for warehouse validation so all affected stock moves are correctly re-checked after reservations are released and reassigned. It prevents some items from being skipped in the process, which helps keep packing and reservation status accurate.
Original PR description
This reverts commit 5d70f75f1d27577ee4e2121497ce477cfa6cda53. `free_reservation` is called once per move line to validate. The goal is to unlink potential move lines that have the same reservation. After finding them, a force re-reservation is triggered. The idea of the previous commit was to call `check_entire_pack` (caused by the re-reservation) only once and not at each move line `free_reservation`. The issue is the stock move that has been unreserved then re-reserved are lost in the process and only the picking that had at least one move line validated are actually calling `check_entire_pack`. 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#273813 Forward-Port-Of: odoo/odoo#273658
The Print button on customer invoices is being restored to its previous primary position. This corrects a confusing change so the most important action is again presented clearly, while keeping the secondary style only for cases where the document has already been sent.
Original PR description
In task-6269645, the print button on customer invoices was set to secondary instead of primary. This is a mistake and it's confusing, so it's being reverted in this commit because it only needs to be secondary if the move is sent. task-6357618 Forward-Port-Of: odoo/odoo#273953
The website title form now uses a format that the editor can modify, so text alignment can be changed directly again. This fixes an issue where a preset styling class prevented users from editing the alignment in the web editor.
Original PR description
`s_title_form` comes with the `text-center` utility class which forbids edition through the web_editor, it needs to use inline-style instead. task-6149380 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#263334
This change prevents website visitors from being signed out unexpectedly when they browse pages after starting a live chat. It ensures the system only applies guest session tracking to users who are not already logged in, avoiding disruptions for authenticated customers and staff.
Original PR description
Before this commit, browsing any page of the website while having a guest cookie set would log out the user after a few seconds. Steps to reproduce: 1. While logged out start a live chat on "/contactus" (or get a guest cookie in any other way). 2. Log in as Marc Demo 3. Open "/contactus" (or any other website page) 4. Refresh after a few seconds -> logged out This happens since [1], which refactored the visitor page tracking. In said change, the override of `track` in `website_livechat` adds the guest to the request context (using `force_guest_env`) if the guest cookie is found. This is done to correctly connect the guest and visitor records, but will log out an authenticated user that has the guest cookie. This commit fixes the issue by only forcing the guest env if the user is not authenticated. [1] https://github.com/odoo/odoo/pull/247438 task-6369344
The View Meeting button in the attendee calendar popover was not responding when clicked. This fix makes it correctly find the related calendar meeting so users can open the meeting details again.
Original PR description
The "View Meeting" button of an activity in the attendee calendar popover did nothing when clicked. Its `onViewMeeting` callback is passed the activity's `calendar_event_id`, which is a record, but the handler treated it as an event id: it interpolated the record into the `.fc-event[data-event-id=...]` selector and used it to index `model.records`. Both lookups therefore missed, the `el && record` guard was never satisfied, and no meeting popover opened. Read the numeric id off the record (`calendarEvent.id`) so the event element and its record are found and the meeting popover opens.
This update corrects a permission check so standard users can no longer trigger access to a field they should not see. It prevents errors and keeps record-tracking information properly restricted, improving stability and access control.
Original PR description
Field is not accessible to standard users. Task-6368820 Part of Task-3704380
This update corrects how eco cheques are calculated for Belgian payroll cases. It helps ensure employees receive the right benefit amounts, especially when employment dates change.
Original PR description
Forward-Port-Of: odoo/odoo#267657
This update adjusts a system setting in the account EDI proxy client so it is neutralized for demo use rather than left in a test-oriented state. It helps ensure the environment matches how the feature is actually used, reducing confusion and avoiding incorrect configuration assumptions.
Original PR description
The system parameter is already brought to demo in account_peppol module. But the current users are not for pdp. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272462
This update adds a backup system for payment confirmations in the Mollie POS module. Previously, clients experienced delays or had to manually confirm payments when the standard websocket connection wasn't working. Now, a regular check-in process ensures payments are confirmed within 5 seconds, providing a smoother customer experience.
Original PR description
Due to the unreliability of the bus during peak server times, clients were missing the websocket payment confirmations from the backend. This meant they had to use the Force Done button to confirm the payment. This commit adds a polling mechanism similar to that used for Viva.com, which polls the backend directly every 5 seconds to check the status of the payment. This means that instead of being blocked, the client should only experience at most a 5 second delay, even when the websocket isn't working. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274713
This update fixes an issue where credit notes were imported with incorrect negative values for prices and taxes, leading to inaccurate calculations. The fix ensures that credit note imports now correctly reflect positive price and tax amounts, aligning with standard refund processing. This improves the accuracy of financial reporting.
Original PR description
Steps to reproduce: 1. Install l10n_be and switch to BE company 2. Upload the XML document (found in ticket chatter) into the Accounting application as a Credit Note. Issue: - The line is imported as…
Steps to reproduce: 1. Install l10n_be and switch to BE company 2. Upload the XML document (found in ticket chatter) into the Accounting application as a Credit Note. Issue: - The line is imported as a negative value which is corrected with a rounding line. - The 6% tax rate is applied to the negative invoice line, resulting in a negative tax amount being deducted from the total (e.g., 449.32 + (-26.96) = 422.36) instead of being added (449.32 + 26.96 = 476.28) Expected behavior: price_unit, quantity and the related tax amounts should all be positive, matching a normal in_refund/out_refund line. Why this happens: - In `_import_ubl_invoice_line_add_price_unit_quantity_discount`, `BaseQuantity` was multiplied by file_document_sign, unlike `PriceAmount` from the same node which is left untouched. This flips price_quantity to -1, which later flips price_unit to negative when `price_unit = price_subtotal / price_quantity`. opw-6310442 Forward-Port-Of: odoo/odoo#271148
This update ensures that the Registration Desk view automatically refreshes whenever the Registration Summary dialog is closed, regardless of the method used (Escape key, clicking outside, or the 'Close' button). This prevents outdated attendee information from appearing in the Kanban and List views, providing a more accurate and up-to-date experience for users.
Original PR description
**Current behavior before PR:** Closing the Registration Summary dialog by pressing **Escape** or clicking outside the dialog does not refresh the Registration Desk view. As a result, the attendee state is not reflected until the view is manually reloaded. **Desired behavior after PR is merged:** The Registration Desk view is refreshed whenever the Registration Summary dialog is closed, regardless of whether it is closed using the **Close** button, by pressing **Escape**, or by clicking outside the dialog. This ensures the attendee information is always updated in both the Kanban and List views. Task - [#6333829](https://www.odoo.com/odoo/project.task/6333829) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272699
This update resolves a bug in the activity counter within the avatar card tour. The issue stemmed from incorrect timezone handling during activity scheduling, leading to inaccurate counts. By adjusting the activity deadlines, the counter now functions correctly across all timezones, ensuring accurate tracking.
Original PR description
The avatar card tour asserts the systray activity counter, which only counts activities whose state is today or overdue. The test scheduled its activities without an explicit deadline, so…
The avatar card tour asserts the systray activity counter, which only counts activities whose state is today or overdue. The test scheduled its activities without an explicit deadline, so activity_schedule fell back to context_today on the class environment, whose superuser has tz Europe/Brussels with demo data. When the test runs between 22:00 and 00:00 UTC, that deadline is tomorrow from a UTC point of view. The state of an activity is however computed in the timezone of its assigned user, and hr_user is created without one, falling back to the server date (UTC). Its activities were therefore planned instead of today, the counter stayed empty and the tour timed out. The admin iteration kept passing because demo data gives admin the same Brussels timezone as the environment that computed the deadline, which is why only half the runs failed (both occurrences at 23:56 and 23:31 UTC). Schedule the activities with a deadline one week in the past instead: an old deadline is overdue in every timezone, whatever timezone the scheduling environment or the assigned user has, making the counter deterministic at any time of the day. https://runbot.odoo.com/odoo/error/941407 Forward-Port-Of: odoo/odoo#274679
This update resolves an issue preventing users from attaching files or dragging images into scheduled messages. The fix ensures that both file uploads and drag-and-drop functionality now work correctly, streamlining the process of creating scheduled messages with attachments.
Original PR description
Steps to reproduce: 1. Create a log note or send a message on any record and open the full composer. 2. Set a date in the future to schedule the message for later and click schedule. 3. Click on the…
Steps to reproduce: 1. Create a log note or send a message on any record and open the full composer. 2. Set a date in the future to schedule the message for later and click schedule. 3. Click on the "Edit" button of the newly scheduled message. 4. Try dragging and dropping an image into the body, or add a file as an attachment using the button. Issue: - Dragging and dropping an image into the form does nothing. - Trying to add a file as an attachment using the button triggers a traceback: `TypeError: Cannot read properties of undefined (reading 'resId')` Why this happens: - The failure when adding explicit file attachments occurs because the `model` and `res_id` fields were omitted from the `mail.scheduled.message` form view layout in the commit 3b985d2. Without these field declarations, the `mail_composer_attachment_selector` widget cannot determine the record metadata parameters, causing the upload to crash. - The failure of the drag-and-drop mechanism occurs because the scheduled message edit view uses the default `FormController` class instead of `MailComposerFormController` which is used in the `mail_compose_message` view. Consequently, the underlying `useCustomDropzone` is never instantiated on the view, leaving drop events unhandled. Fix: 1. Specify `js_class="mail_composer_form"` to the scheduled message form view definition tag to handle drag and drop, as well as adding the missing fields 2. Assign the `resIds` variable based on the message type since it is defined as `res_id` instead of `res_ids` in `mail.scheduled.message`. opw-6273260 Forward-Port-Of: odoo/odoo#268515
This update resolves an issue where deleting a field in a model caused an access error, even if the field wasn't directly linked to the website. The fix ensures the search for used fields is performed with elevated permissions, preventing the error and allowing field deletion to proceed smoothly.
Original PR description
# How to reproduce - Install the Website module - Install another module that has atleast one model with one html field, sanitize=Flase or sanitize_form=False and groups - Remove the field's group…
# How to reproduce - Install the Website module - Install another module that has atleast one model with one html field, sanitize=Flase or sanitize_form=False and groups - Remove the field's group from the current user - Enable dev mode - Go to Settings > Technical > Database Structure > Models - Pick any model (e.g. sale.order.line) - Add a field to that model & Save - Delete the added field & Save > Note : Significantly harder to reproduce since : https://github.com/odoo/odoo/commit/9a336bbb94b0a4266d84f7554c024c3abd2d1e7c I am not sure a field as mentionned in the steps exists # The problem An access error is raised for the module wich access rights were removed, even if the module is not linked in any way with the picked model # Cause of the issue Deleting the field will endup calling the `unlink()` method of `BaseModel` on the `ir.model.fields` record. This function triggers all `@api.delete` methods defined on the model : https://github.com/odoo/odoo/blob/5538132d9d14c4cc5031fc50ac0388ad2ab0fc92/odoo/models.py#L4548-L4552 This will call the this method : https://github.com/odoo/odoo/blob/5538132d9d14c4cc5031fc50ac0388ad2ab0fc92/addons/website/models/website_form.py#L153-L154 That was introduced by : https://github.com/odoo/odoo/commit/c0a827519844ec43537e4487f6abe358bb82ba9a Which prevents a field from being deleted if it is actively used in any website form. But this method does a search on every model return by `_get_html_fields` which may contains models that are not accessible by the user, so we get an access error. # Proposed solution Since `_check_if_used_in_website_form` should perform the same independently from the user, we can do the search in sudo opw-6231951 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274332 Forward-Port-Of: odoo/odoo#265781
This update resolves an issue where the counted inventory quantity in the Physical Inventory module was incorrectly resetting to zero after each line edit. The previous code automatically updated the quantity on blur, regardless of user input. This change ensures the quantity is only reset after the user manually enters a value, improving data accuracy for inventory counts.
Original PR description
Versions -------- - 19.0+ Steps ----- 1. Open Physical Inventory; 2. click on a line; 3. click on a different line. Issue ----- Counted quantity automatically gets set to 0. Cause ----- Commit 3187030 changed the counted quantity widget to enable mutli-line edit. Part of this was done by ignoring the `onInput` hook, and always updating the counted quantity `onBlur`, making it so that the value is set to zero when clicking away, regardless of manual input. Solution -------- Use a `hasInput` state which gets set to `true` on user input. If not `true`, don't update the counted quantity on blur. opw-6365084 Forward-Port-Of: odoo/odoo#274364
This update significantly speeds up the calculation of future holiday timesheets, particularly when many holidays are defined. The change optimizes how timezone conversions are handled, reducing processing time and preventing timeouts for complex scenarios. This improves the user experience and system stability.
Original PR description
**Problem:** When creating a new employee, the future timesheets due to public holidays are computed. If the number of public holidays is large (i.e. if the user creates them for each year, several…
**Problem:** When creating a new employee, the future timesheets due to public holidays are computed. If the number of public holidays is large (i.e. if the user creates them for each year, several years in the future), then it takes excessively long and the action may not complete. **Cause:** The pytz method `localize` and comparing times with non-static timezones is done repeatedly and unnecessarily which becomes costly with more records. **Solution:** Only localize the time when absolutely necessary (determining the date of the leave in the calendar timezone). **Performance Stats:** |Record count|Time before|Queries before|Time after|Queries after| |------------|-----------|--------------|----------|-------------| |100 |3.1s |393 |0.8s |117 | |1,000 |22.3s |2,090 |1.5s |183 | |10,000 |Timeout |N/A |6.7s |541 | opw-6087422 Forward-Port-Of: odoo/odoo#271690 Forward-Port-Of: odoo/odoo#263953
This update resolves an issue where the Avco report incorrectly calculated unit costs for products with specific costing methods. The fix corrects how the report parses data from product categories, ensuring accurate cost reporting. This prevents missing cost lines in the report for products using the Avco method.
Original PR description
## Problem If the `property_cost_method` on a product category defaults to the value in `ir_default`, the query that builds the avco report will fail to properly parse the default value. This is specifically due to the defaults in the `json_value` column being stored as varchar, so strings are surrounded with quotation marks. ## Solution We will adjust the query in the avco report to unpack the `json_value` field as text correctly, stripping it of its quotation marks. ## Steps to reproduce (runbot 19.3) 1. In settings, set the default costing method to avco or fifo 2. Create a product, and set the category to one of the default ones (like 'Goods'). Do not set a cost 3. Create a PO for the product, and receive 1 unit at $10 4. Head to Inventory > Reporting > Stock, and look up the new product. Click on the unit cost, and notice that there is no line for the receipt opw-6331178
This update corrects a display issue in the journal entry preview. Previously, omitting currency information resulted in incorrect amounts being shown in both the credit and debit columns. Now, the preview accurately reflects the credit value, ensuring consistent and reliable financial reporting.
Original PR description
In _move_dict_to_preview_vals(), when no currency is provided, the credit column falls back to the line's debit value, so any caller omitting currency_id would show the debit amount in both columns of the journal entry preview. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274594 Forward-Port-Of: odoo/odoo#269335
A recent update caused the website builder to freeze when attempting to load images with undefined source URLs. This bug resulted in slow updates to the website. This fix resolves the issue by preventing the builder from stalling when an image source is missing.
Original PR description
Commit [1] introduced a `headResponseCache` for images' src, later used in commit [2], which introduced `getFetchedMimetype`. While the former guards against an empty/undefined src, it is not the…
Commit [1] introduced a `headResponseCache` for images' src, later used in commit [2], which introduced `getFetchedMimetype`. While the former guards against an empty/undefined src, it is not the case of the latter. `headResponseCache.read`, which runs a `fetch`, is called within a try/catch, but it is still awaited: with an undefined src, it returns a 404 after stalling the thread for at least 1s. The bug can be seen from the website builder: - Drop a text/image snippet - Open your dev tools on the "network" tab - Click on the image => a failed fetch (404) appears and blocks the builder from being updated quickly. It happens because the element (in this case the `section` of the snippet) is neither an `img`, nor an element with a parallax, nor an element with a background-image, and `getImageSrc` returns an undefined src. [1]: https://github.com/odoo/odoo/commit/bf377f3d1c58aaeb39624700b3e4754d7a6d384b [2]: https://github.com/odoo/odoo/commit/b96a0769eeecd2e6ec14cc7a73105f8dfdb8842e task-6247171 Forward-Port-Of: odoo/odoo#274641
This update resolves a problem where users couldn't delete expenses that had attached files. The fix ensures that expenses with attachments can now be successfully deleted, preventing data loss and improving the overall user experience. This change was made as part of our ongoing commitment to stability and reliability.
Original PR description
To reproduce: - Create an expense - Add an attachment - Try to delete the expense --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274710
This update corrects a problem where receipt templates were being generated incorrectly in the backend. The change replaces a specific code directive ('t-esc') with a more appropriate one ('t-out'), ensuring consistent and accurate receipt generation for all users. This resolves a technical error that could have resulted in incorrect receipts.
Original PR description
Receipt template is also generated in the backend, so we need to use t-out instead of t-esc. runbot error: 941385
This update fixes an error in how Odoo calculates the standard price for products tracked using FIFO inventory. Previously, the calculation was incorrect due to a flaw in how lot quantities were processed, leading to inaccurate cost and price figures. This change ensures accurate FIFO pricing for products.
Original PR description
This PR is needed for the fix of https://github.com/odoo/odoo/pull/272411 **Problem:** lot's standard price are not correct when the product is fifo and move have different values and multiple lots…
This PR is needed for the fix of https://github.com/odoo/odoo/pull/272411 **Problem:** lot's standard price are not correct when the product is fifo and move have different values and multiple lots **Steps to reproduce:** - product fifo tracked and valued by lots - 20 IN @ 100 (all in lot 1) - 10 IN @ 10 (5 in lot 1 and 5 in lot 2) - on the product form click on the lot/serial number smart button and select lot 1 **Current behavior:** the average cost of lot1 is 64 back on the product form the standard price is 55 **Expected behavior:** the average cost of lot 1 should be 20 * 100 (from move1) + 5 * 10 (from move 2) / 25 = 2050 / 25 = 82 the standard price of the product should be 2100 / 30 = 70 **Cause of the issue:** Because the product is fifo, to compute the avg_cost of the lot we call _run_fifo() https://github.com/odoo/odoo/blob/456026b5ef99388b1cf5bdd78cee8d1ad3d51304/addons/stock_account/models/stock_lot.py#L47 which calls _run_fifo_get_stack() to get the fifo stack specific to this lot. https://github.com/odoo/odoo/blob/456026b5ef99388b1cf5bdd78cee8d1ad3d51304/addons/stock_account/models/product.py#L545 Issue 1) run_fifo_get_stack() stores the on hand quantity (for the lot if a lot is given as param) in fifo_stack_size and, as long as there is moves and fifo_stack_size>0, adds move (starting from the last one in date) to the stack and removes the quantity of the move from fifo_stack_size. It then returns the moves stack and the remaning quantity on the first move of the stack (for the rest we know it's the full quantity) https://github.com/odoo/odoo/blob/456026b5ef99388b1cf5bdd78cee8d1ad3d51304/addons/stock_account/models/product.py#L612-L618 Inside run_fifo_get_stack(), to do this, because we're only considering the quantities from this specific lot we should only remove the quantity from the move that went in lot, but currently we're removing the quantity from the entire move. https://github.com/odoo/odoo/blob/456026b5ef99388b1cf5bdd78cee8d1ad3d51304/addons/stock_account/models/product.py#L615-L618 So, at the first iteration of the while loop (for the move with 10 quantities), instead of doing fifo_stack_size(25) -= 5, we do fifo_stack_size(25) -= 10 The next move is the last one, so it's the one on which remaining_qty_on_first_stack_move will be based on. remaining_qty_on_first_stack_move will be the minimum between the move's quantity and the fifo_stack_size. So because the fifo_stack_size is now wrongfully 15 instead of 20 that's the value that will be returned by _run_fifo_get_stack. So inside run_fifo(), qty_on_first_move will be 15 instead of 20 https://github.com/odoo/odoo/blob/456026b5ef99388b1cf5bdd78cee8d1ad3d51304/addons/stock_account/models/product.py#L545 Issue 2) Another issue is that inside _run_fifo when calling _get_valued on the move, we don't use the lot parameter. So we use the entire quantity of the move instead of the quantity specific to the lot. https://github.com/odoo/odoo/blob/b07ff5843ee87741b293d9e67f72a77a2ed2ed88/addons/stock_account/models/product.py#L561-L562 And we use the full value of the move instead of the pro rata of the value for the quantity specific to the lot As a consequence, inside _run_fifo the computation for the fifo_cost will be 15 (because of issue1) * 100 $ [first iteration of the while loop] \+ 10 (because of issue 2) * 10$ [second iteration of the while loop] = 1600$ Instead of 20 *100 + 5 *10$ = 2050$ Therefore the avg_cost of the lot is wrong and the standard price of the product will also be false. side note: those two issues balance each other if the price unit of the moves are the same needed for PR of opw-6311341 Forward-Port-Of: odoo/odoo#273728
This update fixes a bug where the 'Offline UI' in Discuss would disable navigation and key features when the server connection was lost. Now, users can continue to access and interact with channels and thread actions even without an active internet connection, improving usability and data access.
Original PR description
Before this commit, when losing connection to the server, the "Offline UI" introduced in [1] would disable all buttons in Discuss. This prevents navigating the Discuss channels, even if we potentially have local knowledge of the messages in those channels. It also prevents using Thread actions like: - Channel Members - Pinned Messages - Attachments - Threads Which may also only need data that is available locally. This commit fixes the issue by marking the appropriate buttons as available offline (`data-available-offline`), which prevents the Offline UI service from disabling them. [1] https://github.com/odoo/odoo/pull/229492 task-6185454 Forward-Port-Of: odoo/odoo#274778 Forward-Port-Of: odoo/odoo#273122
This update resolves an issue where the product expiry warning displayed incorrect information before a lot was fully created. Previously, the system would show 'False, False' instead of the correct lot name. Now, the system uses information from the receipt to ensure the warning displays the correct product and lot name, improving data accuracy.
Original PR description
From saas-18.4, the expiration confirmation wizard can be triggered not only from expired lots, but also from stock move lines whose `removal_date` has passed. For incoming receipts, tracked products…
From saas-18.4, the expiration confirmation wizard can be triggered not only from expired lots, but also from stock move lines whose `removal_date` has passed. For incoming receipts, tracked products use the `lot_name` field when the user is entering the lot. The corresponding `lot_id` is only created later once the receipt is validated. As a result, it is possible for the expiration confirmation wizard to be displayed before the lot exists. In this situation, it attempts to display the product and lot information using `lot_id`, which is still empty, causing the message to show "False, False" instead of the actual lot name entered by the user. It should use the move line information as a fallback when no `lot_id` has been created yet so it still displays the correct product and lot name. Steps to reproduce 1. Enable Product Expiry. 2. Create a storable product with: - Tracking: By Lots - Use Expiration Date: enabled - Removal Time > 0 3. Create a receipt for the product. 4. Open Detailed Operations. 5. Enter a new lot number in the Lot/Serial Number field. 6. Ensure the removal date is in the past and validate the receipt. Related Tickets: opw-6303140 Forward-Port-Of: odoo/odoo#273970 Forward-Port-Of: odoo/odoo#273143
This update ensures that attachments added to email templates used for scheduling messages correctly link to the scheduled message record. Previously, attachments weren't properly associated, leading to access issues when viewing scheduled messages from different user accounts. This change resolves a bug that prevented proper attachment linking.
Original PR description
**Problem:** When scheduling a message using an email template with custom attachments, those attachments will not have their `res_model` and `res_id` updated to relate to the scheduled message…
**Problem:** When scheduling a message using an email template with custom attachments, those attachments will not have their `res_model` and `res_id` updated to relate to the scheduled message record. This can lead to access errors. **Cause:** When composing a message using an email template with attachments, those attachments are created with their `res_model` and `res_id` values corresponding to the mail composer record. However, when scheduling a message, only attachments with no `res_id` value (or a value of 0) are updated to correspond to the scheduled message record. https://github.com/odoo/odoo/blob/77b180e8251fb8019e0034e1c2f485fd2c34ea4e/addons/mail/wizard/mail_compose_message.py#L1198-L1201 https://github.com/odoo/odoo/blob/30ca89b9e0d3c43d019167ec2de816c263f4bb92/addons/mail/models/mail_scheduled_message.py#L86 **Purpose:** Modify the `mail.scheduled.message` override of `create` to not require an attachment have no `res_id` value to be properly updated. **Steps to Reproduce in Runbot:** 1. Add an attachment to an email template. 2. Open a mail composer using that email template, then schedule the message for later. 3. Attempt to view the scheduled message with a different user. More specific example flow: 1. Add an attachment to the Sales: Send Quotation email template. 2. Create a Quotation and send it with the Send by Email button, selecting Send Later instead of Send. 3. Attempt to view the Quotation with a different user. opw-6293587 Forward-Port-Of: odoo/odoo#272261
This update resolves a validation error that occurred when deleting and recreating move lines during subcontracting production recording. The change prevents cache invalidation and ensures correct data updates, improving the reliability of the subcontracting process.
Original PR description
**Issue** In subcontracting, deleting a raw move line and adding a new one in the same editing flow can lead to a validation error during production recording. **Steps to reproduce** - Create a…
**Issue** In subcontracting, deleting a raw move line and adding a new one in the same editing flow can lead to a validation error during production recording. **Steps to reproduce** - Create a subcontracting product with a comp A - Create and confirm a purchase order of that product (with the subcontracting partner) - Open the associated delivery - Open the move details (hamburger button) - Delete the move line linked to the comp A - Create a new move line for a comp B with a quantity of 1 - Record the production -> A validation error occurs: the mandatory field `product_uom_id` is not set. **Cause** The regression comes from this commit: https://github.com/odoo/odoo/commit/54f10b56f577ad9ed5575bd396dba7d20d22fc2e While assigning `move_raw_ids`, the inverse method is triggered: https://github.com/odoo/odoo/blob/9267b2d1a9b2d2d6a33eceab07d572406c68c723/addons/mrp_subcontracting/models/mrp_production.py#L34 At this stage, newly added lines are still virtual records (`line`): https://github.com/odoo/odoo/blob/9267b2d1a9b2d2d6a33eceab07d572406c68c723/addons/mrp_subcontracting/models/mrp_production.py#L38 The previous implementation directly unlinked removed move lines (see commit https://github.com/odoo/odoo/commit/54f10b56f577ad9ed5575bd396dba7d20d22fc2e): https://github.com/odoo/odoo/blob/9267b2d1a9b2d2d6a33eceab07d572406c68c723/addons/mrp_subcontracting/models/mrp_production.py#L40-L43 Which will eventually flush and invalidate all the cache: https://github.com/odoo/odoo/blob/0e78b4fd2ab904f2e12107cb6ff7cc11d512259f/odoo/models.py#L4666 And since `line` is a virtual record (not in db), its associated values will be reset, among those, `product_uom_id`. Later, when the move line is reassigned: https://github.com/odoo/odoo/blob/0e78b4fd2ab904f2e12107cb6ff7cc11d512259f/addons/mrp_subcontracting/models/mrp_production.py#L49 https://github.com/odoo/odoo/blob/0e78b4fd2ab904f2e12107cb6ff7cc11d512259f/odoo/models.py#L5223-L5228 the validation fails because the virtual line no longer contains the required values. **Additional note** An alternative could have been using Command but since this line: https://github.com/odoo/odoo/blob/0e78b4fd2ab904f2e12107cb6ff7cc11d512259f/addons/mrp_subcontracting/models/mrp_production.py#L42 can not be converted to: `Command.set([line.id for line in lines])` because `lines` may also contain virtual records. This causes an invalid quantity for the move. Indeed, even if the command operator would update the quantity on the `move_line` correctly, it won't for the quantity of the `move` because of its associated compute method: https://github.com/odoo/odoo/blob/26ba95ac1c5bbb24975efb1a6f53c1ab47b61532/addons/stock/models/stock_move.py#L399-L400 that relies on `.ids`, which is `[]` on virtual records. Therefore, keep the change minimal. opw-6133281 Forward-Port-Of: odoo/odoo#270436 Forward-Port-Of: odoo/odoo#263058