Daily updates from Odoo
Tuesday, August 18, 2026
40 changes · saas-19.4
Resolved issues and error corrections
The timesheet assistant now handles timesheet data more reliably when entries are removed or were created elsewhere. This helps prevent errors and avoids unintended changes to previously processed assistant events.
Original PR description
## [FIX] timesheet_grid: avoid altering consumedEvents Before this commit, the objects inside consumedEvents attribute are altered because we keep the reference of those objects. This commit avoid altering the consumed events. ## [FIX] timesheet_grid: use recordsByStart instead of records Forward-Port-Of: odoo/enterprise#127698
Starting a timesheet timer from a task now correctly carries over the task's sales order item, so the resulting timesheet remains billable as expected. This prevents missing billing information and ensures the Billable option is visible immediately when relevant.
Original PR description
**Problem:** Starting the timer on a task that is linked to a sale order item produces a timesheet that is not linked to it, and the Billable toggle is missing from the timer. **Steps to reproduce:**…
**Problem:** Starting the timer on a task that is linked to a sale order item produces a timesheet that is not linked to it, and the Billable toggle is missing from the timer. **Steps to reproduce:** 1. Install Timesheets and Sales 2. Open a task whose Sales Order Item is set 3. Start the timer from the Timesheets systray 4. Save it and open the resulting timesheet **Current behavior:** The Sales Order Item is empty. The Billable toggle only appears after removing and re-adding the task in the timer. **Expected behavior:** The timer is billable on the task's sale order item as soon as it is opened. **Cause of the issue:** `_get_timesheet_pre_filled_form_data` returns only `project_id` and `task_id`. The timer form merges that pre-fill over `timesheet_default_values`, which `lazy_session_info` computes once per session from `account.analytic.line.new()` - a record with no project and no task, so `so_line`, `allow_billable` and `has_available_so` are all `False` in it. Because the pre-fill carries none of those keys, they keep the task-independent session values: the timesheet stays unlinked from the sale order item, and the Billable toggle stays hidden since it is displayed from `has_available_so`. **Fix:** The pre-fill endpoint is the only place that knows which task the timer is being opened on, so it is where the task-dependent values have to be resolved. Reading them off a new timesheet built with that project and task keeps the endpoint generic - it returns whatever `_get_aw_timesheet_fields_specification` declares, so the sale fields stay owned by sale_timesheet_enterprise rather than being named in timesheet_grid. Dropping the session defaults instead was rejected: they are also the only source of `date`, `user_id` and `company_id` for the timer record, and removing them makes saving fail on the required Date field. opw-6423577 Forward-Port-Of: odoo/enterprise#127800
Fixed an issue where payroll correction batches could be created under the wrong active company when correcting a paid payslip for an employee in another company. Correction batches now use the company of the payslips they contain and avoid mixing payslips from different companies, improving accuracy in multi-company payroll processing.
Original PR description
Steps to reproduce: - Have an employee in company B, with a paid payslip - Log in with company A active (company B allowed but not selected) - Open the employee's paid payslip and click "Correct" The refund and correction payslips are computed in company B (their company follows the employee), but the pay run created for them by the wizard has no explicit company and falls back to the active company A. Set the pay run's company from the payslips it contains, and group the payslips by company as well as by structure so that a batch never mixes companies. task-6428755 Forward-Port-Of: odoo/enterprise#126064
Odoo now correctly recognizes valid Brazilian NF-e invoice XML files even when the main invoice tag has no attributes. This prevents eligible vendor bills from being silently skipped during import, improving reliability for Brazilian accounting workflows.
Original PR description
### Issue before this commit: Certain valid Brazilian NF-e (electronic invoice) XML files fail to import because the system silently ignores them during the initial EDI recognition phase. ### Steps to reproduce the issue: 1. Download Accounting and l10n_br_edi 2. Go to Vendor > Bills 3. Try to import both xmls in the ticket 4. One of the two will not be imported correctly ### Cause of the issue: https://github.com/odoo/enterprise/blob/3ed1721b702555e96c9774969927f6517e855704/l10n_br_edi/models/account_move.py#L819-L827 This function relies on a strict byte string search for b"<NFe " while it's also correct if the tag is only `<NFe>`. ### Reason to introduce the fix: To make the initial NF-e file recognition more robust and compliant with standard XML namespace rules, ensuring Odoo successfully processes all valid Brazilian invoices regardless of attribute formatting. opw-6402843 Forward-Port-Of: odoo/enterprise#127851 Forward-Port-Of: odoo/enterprise#126881
An internal accounting test was adjusted to match a related platform fix in grouped data reading. This helps keep automated checks reliable without changing day-to-day user workflows.
Original PR description
The fix at https://github.com/odoo/odoo/pull/281911 adds bin_size: tru in the web_read_group. This commit adpats an accounting test as a consequence Forward-Port-Of: odoo/enterprise#127994 Forward-Port-Of: odoo/enterprise#127638
The IoT device list now keeps pagination working when users open a device record. This prevents navigation issues in larger device lists and makes managing IoT devices smoother.
Original PR description
Since #72351, the pagination on IoT devices was broken due to how we were getting to the full device form when clicking on a record. We now change the override to use the existing method from the framework `switchToForm` which handles it better. opw-6058532 Forward-Port-Of: odoo/enterprise#127215 Forward-Port-Of: odoo/enterprise#126486
The timesheet menu has been adjusted to display more cleanly on mobile devices. This makes it easier for users to view and enter timesheet information from phones without dealing with a clunky interface.
Original PR description
In this commit, we improve the display of the timesheet systray in mobile view as it was clunky. task-6332208 Forward-Port-Of: odoo/enterprise#122491
Completed kitchen orders are now removed from the Order Status Display instead of reappearing under "Almost There." This keeps customer-facing order progress accurate and avoids confusion after an order has finished preparation.
Original PR description
Steps to reproduce ------------------ - Open a PoS session, the Kitchen Display, and the Order Status Display. - Create an order and send it to the Kitchen. - Process the order through all the stages until it reaches the final (completed) stage. Issue ----- - Once the order reaches the completed stage, it reappears in the "Almost There" section of the Order Status Display instead of being removed. Cause ----- - The applied domain fetched all kitchen orders, including completed ones. The display logic only distinguishes whether an order is in the second last preparation stage than show it as "Ready", all other orders are shown as "Almost There". As a result, completed orders fall back into the "Almost There" section.. Fix --- - Updated the domain to fetch only active kitchen orders and exclude completed ones from the Order Status Display. Task: 6394865
The Timesheet Assistant now combines selected suggestions that share the same rule template into one more natural description. This avoids repetitive text separated by semicolons, making generated timesheet entries easier to read and understand.
Original PR description
In this task, we improved the Timesheet Assistant by merging descriptions generated from the same rule templates. When multiple suggestions are selected that use the same rule template, the assistant now combines them into a single timesheet description instead of joining with ';' Example: Rule template: `Discussing with $1` Before: Discussing with A; Discussing with B After: Discussing with A and B Task-6348575 Forward-Port-Of: odoo/enterprise#127109 Forward-Port-Of: odoo/enterprise#126157
The payment registration flow now uses the bank account selected in the wizard when none is set directly on the invoice or move. This helps ensure customers can see and use the “Pay Now” option when a valid bank account is available, reducing payment friction.
Original PR description
Before this commit, it could happens that when we don't put a partner_bank_id on the move it self. The "pay now" button was never displayed. It was because partner_bank_ids was only checking the value from the move and not the wizard. Now if there is no partner_bank_id in the wizard.batches then we look at the value in the wizard and we use it. task-6374002 Forward-Port-Of: odoo/enterprise#123759
International DPD shipments through Sendcloud now include the recipient tax number in the required customs details. This prevents delivery validation failures and adds safer fallback values for required customs fields when creating new deliveries.
Original PR description
### This is a revision of #119399 which had to be reverted. Original issue ----- Deliveries cannot be validated using DPD with Sendcloud, users get an error. Steps to reproduce ----- - Set up…
### This is a revision of #119399 which had to be reverted. Original issue ----- Deliveries cannot be validated using DPD with Sendcloud, users get an error. Steps to reproduce ----- - Set up Sendcloud DPD - Create a SO - Interntional customer - Some VAT number - Some product - Add sendcloud delivery - Confirm SO - Validate the linked picking > Error: “The receiver VAT number is missing; please provide it to continue” Issue's cause ----- Tax numbers should be included in the `customs_information` field of the request as per the API https://sendcloud.dev/api/v2/parcels/create-a-parcel-or-parcels#body-one-of-0-parcel-customs-information-tax-numbers For the `vat_label` field, we have to force the language to English in the context because the field is translated by default, but sendcloud only accepts the english names (eg French "TVA" is not accepted, expected value is "VAT"). https://github.com/odoo/odoo/blob/d1d1610332a1596d026fb0a42ec236d1a79c71cc/odoo/addons/base/models/res_country.py#L75 Revert cause ----- The vat_label field is marked for translation (translate=True) https://github.com/odoo/odoo/blob/d1d1610332a1596d026fb0a42ec236d1a79c71cc/odoo/addons/base/models/res_country.py#L75 So if the user has the DB in french for example, we are sending "TVA" instead of "VAT" in the name field. Other issues ----- - We need to provide an actual fallback for `customs_invoice_nr`. As it stands, if we create a new delivery it cannot be validated because Sendcloud doesn't accept for the field to be empty. - Same for `name`, we need to provide an actual fallback. ----- Ticket: opw-6250860 Forward-Port-Of: odoo/enterprise#127425 Forward-Port-Of: odoo/enterprise#124245
Restaurant preparation display cards now show the assigned course name, such as “Main Course,” instead of only a course number when course allocation is enabled. This makes kitchen preparation screens clearer and reduces confusion for staff handling multi-course orders.
Original PR description
Before this commit: ====================== Prep cards showed the course index, like T1 - C2, even when course allocation was enabled. After this commit: ==================== Prep cards show the allocated course name, like T1 - Main Course, when course allocation is enabled. Task-6421309
Fixes a timer issue where task or ticket timers could jump between values, show negative seconds, or track the wrong duration after repeated stop, start, and page reload cycles. This improves confidence in recorded work time by ensuring only the active timer on screen updates the displayed elapsed time.
Original PR description
A running task/ticket timer can sometimes stutter, show negative values, and track the wrong elapsed time. ### Steps to reproduce On a record with a timesheet timer (for example, a Field Service…
A running task/ticket timer can sometimes stutter, show negative values, and track the wrong elapsed time. ### Steps to reproduce On a record with a timesheet timer (for example, a Field Service task): 1. Start the timer and let it run for about 15-20 seconds. 2. Stop it and confirm the dialog. 3. Start it again. This creates a new `timer_start`. 4. Reload the page. The timer starts jumping every second between two different values. As it keeps running, it can even show negative values such as `00:00:-57`. If the problem does not appear right away, repeat steps 2-4 a few times. It usually shows up after a few stop/start/reload cycles. ### Cause The timer shown in the button bar is the `timer_start_field` widget. It starts a `setInterval` that updates a shared `TimerReactive` object once per second. While a form is loading, Odoo renders it several times in a row (for example: a first render, another when the chatter is loaded, and another when the record data comes back from the server). Rendering a form builds all of its fields to produce the display, so each of these renders creates its own `timer_start_field`. Odoo keeps and mounts only the render that ends up on screen; the earlier ones are thrown away before being mounted. The interval is started while the field renders, from the record observer set up in `setup`, before the field is mounted. So the fields that are later thrown away also start an interval. Those intervals keep running for the rest of the session. Each one updates the same shared `TimerReactive` object using the `timer_start` it was created with. As long as every instance has the same `timer_start`, they all write the same value and the problem stays hidden. After the timer is stopped and started again, the old instances keep the old `timer_start` while the mounted one uses the new one. Every second they overwrite each other's value, so the timer jumps between two different elapsed times. When the instance with the newer `timer_start` writes right after one with an older start, it tries to show a smaller elapsed time than what is already there, and the subtraction in `TimerReactive` produces a negative number of seconds. ### Fix Move the per-second timer update into a `useEffect`. The effect only runs after the field is mounted, and Owl automatically cleans it up when the field is unmounted or when `timer_start` or `timer_pause` change. This means fields that are destroyed before they are mounted never start an interval, so only the mounted field updates the shared timer. `onRecordChange` no longer starts or stops the interval. It only updates the displayed timer value to match the current record. opw-6209405 Forward-Port-Of: odoo/enterprise#128071 Forward-Port-Of: odoo/enterprise#126242
Belgian payroll now correctly splits a new long sickness leave after the first 30 days, even when it occurs during a relapse window but is not marked as related to the previous illness. This helps ensure payroll calculations classify sick leave consistently and avoid overcounting the initial sick leave category.
Original PR description
Steps to reproduce: - Create a STO for an employee of more than 30 days -> this period is split in 30 days STO and x days SGS. - Within the relapse period, create a second STO of more than 30 days and leave the relapse field empty (which is fine if the second STO is not related to the first sickness) -> the period should be split after the first 30 days just like the first STO, but it remains an STO for the whole duration. task-6296152
The call debrief timeline now keeps transcript markers aligned with the timeline direction in right-to-left language settings. This provides a more consistent and understandable call review experience for users working in languages such as Arabic or Hebrew.
Original PR description
The call debrief timeline was not properly adapted for RTL layouts. In Community, the timeline is forced to always render in LTR, as it represents the physical progression of time moving forward. To ensure consistency, this commit makes transcript markers within the timeline always render in LTR as well. task-6445011 Requires: - https://github.com/odoo/odoo/pull/280218
Creating appraisal goal templates no longer assigns them to a specific employee by default. This ensures templates created from the library or saved from an employee goal remain visible and reusable in the template list.
Original PR description
Steps to reproduce: - Go to Appraisals > Configuration > Goals > Library and create a goal template. - Or open an employee goal and click "Save as Template". - The created goal template is not visible in the goal template list view. Reason: An employee was set by default on new goal templates upon creation. Solution: Do not assign a default employee when creating a goal template. Task-6443339
The vehicle salary tab now shows the Benefit In Kind amount with the correct yearly label and keeps the label aligned next to the value. This avoids confusion when reviewing vehicle-related salary costs, with no changes to calculations or stored data.
Original PR description
The Benefit In Kind (BIK) amount on the vehicle model salary tab was labelled "/ month" while the underlying value is yearly, mismatching the other fields (CO2 fee, Cost, Total Cost) correctly suffixed. The unit label div was also missing the o_hr_narrow_field class used everywhere else in the module, so the unit label stretched to the far right of the row instead of sitting next to the value. Purely visual fix, no behavior or data change. task-6428222
The Timesheet Assistant now identifies time spent in spreadsheets as spreadsheet work instead of grouping it under Documents. This gives users more accurate timesheet suggestions and reduces manual corrections.
Original PR description
## Behavior Before the Commit When a user spent time working in a spreadsheet, the Timesheet Assistant grouped this time under "Working in Documents". This occurred because "Spreadsheet" is a module rather than an application, and the Assistant only creates suggestions for applications. As a result, it attempted to fall back to an application and frequently defaulted to the "Documents" application. ## New Expected Behavior After this Commit: When a user spends time in a spreadsheet, the Timesheet Assistant now generates a "Working in Spreadsheet" suggestion. This is enabled by a dedicated list of non‑application modules that allows specific modules to be recognized even when they are not applications. task-[6438536](https://www.odoo.com/odoo/project/4105/tasks/6438536) Forward-Port-Of: odoo/enterprise#126390
Users can now add AI-generated images to Documents without encountering a crash. The fix also improves handling for attachments that are not linked to another business record, making document creation more reliable in similar cases.
Original PR description
Bug === Since 907f1029e8abb38c1ddd3fa9493fa866322706ca a crash happen when we generate image with AI, and try to add it to documents. Task-6453430
The scheduled cleanup for AI embeddings now correctly detects records created with deprecated embedding models. This helps ensure outdated AI data is refreshed as intended, reducing maintenance issues without changing the user workflow.
Original PR description
The embedding model deprecation cron starts by searching for chunks that are embedded using a model that hasn't been deprecated. However, due to an optimization in the ORM (optimize_type_selection),…
The embedding model deprecation cron starts by searching for chunks that are embedded using a model that hasn't been deprecated. However, due to an optimization in the ORM (optimize_type_selection), the search doesn't return any result. A domain that uses the 'not in' operator is converted by the optimization to use the 'in' operator and the values are replaced by the difference between the available selection values (self._selection) and the values in the domain. So, given that the selection values of the embedding_model field are the non deprecated embedding models and the search domain of the cron is ['embedding_model', 'not in', non deprecated embedding models] , the final domain will be ['embedding_model', 'in', non deprecated models list - non deprecated models list] = ['embedding_model', 'in', []] which will retrieve nothing from the DB because all records have an embedding_model. The main issue is that the optimization assumes that the available selection values are static. However, an old embedding model can be deprecated and a new one added which makes the optimization fail. So, the selection values are changed to be computed dynamically which will be skipped by the optimization.
Currently, trying to create a link via typing a url throws traceback in Safari. This happens because after merging commit [1], `addStep` is called in `onBeforeInputPrepareConvertToLink` but from saas-19.4 `addStep` function is replaced by `commit`. This PR aims to replace `addStep` to `commit` to prevent traceback. [1]: https://github.com/odoo/odoo/commit/e5af0308b3fb87abb04de07c8554658e892ee03e --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/subm
Original PR description
Currently, trying to create a link via typing a url throws traceback in Safari. This happens because after merging commit [1], `addStep` is called in `onBeforeInputPrepareConvertToLink` but from saas-19.4 `addStep` function is replaced by `commit`. This PR aims to replace `addStep` to `commit` to prevent traceback. [1]: https://github.com/odoo/odoo/commit/e5af0308b3fb87abb04de07c8554658e892ee03e --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
On a database that was not created with demo data, registering for Peppol using the "Odoo Demo ID" identification fails instead of activating the local demo/training registration. The registration wizard correctly shows "Demo" as the EDI mode once that identifier is selected, but submitting the registration still reaches out to the real Peppol Access Point, which the wizard cannot connect to, so the registration is blocked. Steps to reproduce: ------------------- * Create a new database wit
Original PR description
On a database that was not created with demo data, registering for Peppol using the "Odoo Demo ID" identification fails instead of activating the local demo/training registration. The registration…
On a database that was not created with demo data, registering for Peppol using the "Odoo Demo ID" identification fails instead of activating the local demo/training registration. The registration wizard correctly shows "Demo" as the EDI mode once that identifier is selected, but submitting the registration still reaches out to the real Peppol Access Point, which the wizard cannot connect to, so the registration is blocked. Steps to reproduce: ------------------- * Create a new database without demo data (e.g. a regular Odoo Online database, not a /trial one) * Install Accounting, open the Peppol registration wizard * Select "Odoo Demo ID" as the Electronic Address Scheme and fill in the other required fields * Click on Register > Observation: The wizard displays "Demo" as the EDI mode, yet registering raises "Failed to connect to Odoo Peppol Proxy." Manually creating the system parameter `account_peppol.edi.mode` with the value "demo" makes the registration succeed, but that parameter is only ever set automatically when the module's demo data is loaded. Why the fix: ------------ The wizard's displayed `edi_mode` correctly resolves to "demo" for the Odoo Demo ID EAS, but `handle_demo` - which decides whether `_can_connect`/`_create_connection` run against the local mock or the real Peppol proxy - recomputed the mode itself without passing that EAS along, so it could only detect demo mode via the system parameter or an already existing proxy user. Made `handle_demo` pass the record's own `peppol_eas` as `temporary_eas`, aligning its detection with what the wizard already displays. opw-6448276
Before this commit and since the new read_group (which fetches records from open groups server side), images were loaded as base64, overloading the return payload and potentially triggering overload errors (MemoryError) This was because the bin_size = true context key was forgotten. After this commit, images are not loaded as base64 thanks to that context key Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged:
Original PR description
Before this commit and since the new read_group (which fetches records from open groups server side), images were loaded as base64, overloading the return payload and potentially triggering overload errors (MemoryError) This was because the bin_size = true context key was forgotten. After this commit, images are not loaded as base64 thanks to that context key 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#282562 Forward-Port-Of: odoo/odoo#281911
Before this commit, deleting a record leaves its id in the relations of a record deleted before it in the same update, and reading one of those relations hands out an entry for a record that is gone. This happens because an update takes a deleted record out of the relations that hold it, but forgets it as soon as it is deleted. However, deleting a record is what queues the deletion of the records it holds: `channelMembers` carries `onDelete: (r) => r.delete()`, so the members of a thread are
Original PR description
Before this commit, deleting a record leaves its id in the relations of a record deleted before it in the same update, and reading one of those relations hands out an entry for a record that is gone. This happens because an update takes a deleted record out of the relations that hold it, but forgets it as soon as it is deleted. However, deleting a record is what queues the deletion of the records it holds: `channelMembers` carries `onDelete: (r) => r.delete()`, so the members of a thread are deleted once the thread is already forgotten, and their ids stay in its `onlineMembers`. This commit fixes the issue by keeping the records deleted by an update known until it ends. Forward-Port-Of: odoo/odoo#282621 Forward-Port-Of: odoo/odoo#281983
**Problem**: When creating a percentage down payment with a Brazilian tax, the divide by zero error occurs because the factor contains a zero value. **Steps to reproduce**: 1. Set up Brazil localization with ```l10n_br_avatax```, ```l10n_br_edi```. 2. Create a service product with ```LC116``` code set up. 3. Create a sales order with the customer has ```Automatic Tax Mapping (Avalara Brazil)``` in their Fiscal Position. 4. Add the service product to the sales order and confirm it. 5. Cr
Original PR description
**Problem**: When creating a percentage down payment with a Brazilian tax, the divide by zero error occurs because the factor contains a zero value. **Steps to reproduce**: 1. Set up Brazil localization with ```l10n_br_avatax```, ```l10n_br_edi```. 2. Create a service product with ```LC116``` code set up. 3. Create a sales order with the customer has ```Automatic Tax Mapping (Avalara Brazil)``` in their Fiscal Position. 4. Add the service product to the sales order and confirm it. 5. Create a percentage down payment for the sales order. 6. Divide by zero error occurs. **Fix**: Check if the ```sum_of_factors``` is zero and if so, set the ```delta_factor``` to zero. before ```normalize_results['plus_sum_of_factors'] / normalize_results['sum_of_factors']``` opw-6446986 Forward-Port-Of: odoo/odoo#281571
Steps to reproduce: - Make sure you have 2 or more languages installed - Install events and website app - For the website, apply the languages you have in settings - Make sure website default language is different from backend language - Create an event - Go to the event's website page through the smart button - Switch the language of the frontend to one that is different from backend - Translate (alter) the description for this language - Click on the outlook icon to download the ics
Original PR description
Steps to reproduce: - Make sure you have 2 or more languages installed - Install events and website app - For the website, apply the languages you have in settings - Make sure website default…
Steps to reproduce: - Make sure you have 2 or more languages installed - Install events and website app - For the website, apply the languages you have in settings - Make sure website default language is different from backend language - Create an event - Go to the event's website page through the smart button - Switch the language of the frontend to one that is different from backend - Translate (alter) the description for this language - Click on the outlook icon to download the ics Current Behavior: The ics will contain a description based on the backend's chosen language Expected Behavior: The ics will contain a description based on the website's chosen language. If there's no website, it will use the browser's language Clarification: The current behavior is confusing for users as they will expect the ics to match the description of the current webpage which is dependent on the frontend's selected language. This current behavior also contradicts the google calendar behavior which grabs the description based on the frontend's selected language. The endpoints for retrieving the ics file now utilize the website=True flag to take advantage of IrHttp._match which will automatically set the context language to be the frontend's rather than the backend language. We want the ics file to be in the frontend language for visual consistency. Also, send_email_reminder function does not require with context for this reason opw-6235320 Forward-Port-Of: odoo/odoo#280520 Forward-Port-Of: odoo/odoo#271968
**Steps to reproduce** - Open any MO - Click on 'catalog' - Click on the three dots button of any product - Try to edit -> Traceback: `"product.product"."state" field is undefined.` **Cause** The `move_raw_ids` field on the MO form sets a context with `form_view_ref: 'mrp.view_mrp_stock_move_operations'`, so that editing a component's `stock.move` line in place opens that dedicated view: https://github.com/odoo/odoo/blob/20db2910d16fa6ebd07794fb9ed3fb1c8f57b0fc/addons/mrp/views/mrp_prod
Original PR description
**Steps to reproduce** - Open any MO - Click on 'catalog' - Click on the three dots button of any product - Try to edit -> Traceback: `"product.product"."state" field is undefined.` **Cause** The…
**Steps to reproduce** - Open any MO - Click on 'catalog' - Click on the three dots button of any product - Try to edit -> Traceback: `"product.product"."state" field is undefined.` **Cause** The `move_raw_ids` field on the MO form sets a context with `form_view_ref: 'mrp.view_mrp_stock_move_operations'`, so that editing a component's `stock.move` line in place opens that dedicated view: https://github.com/odoo/odoo/blob/20db2910d16fa6ebd07794fb9ed3fb1c8f57b0fc/addons/mrp/views/mrp_production_views.xml#L411-L417 The 'Catalog' button lives inside that same field, and calls `action_add_from_catalog_raw`, which delegates to the mixin's `action_add_from_catalog`: https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/addons/product/models/product_catalog_mixin.py#L17 Which conserve the `'form_view_ref'` from context: https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/addons/product/models/product_catalog_mixin.py#L28 and that action requests its form view with `view_id=False`: https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/addons/product/models/product_catalog_mixin.py#L25 Which will try to load `view_mrp_stock_move_operations`, since: https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/odoo/addons/base/models/ir_ui_view.py#L3002-L3005 `_get_view` never checks that the resolved view's `model` matches the model it was asked for, so it returns `view_mrp_stock_move_operations`for the `product.product` model: https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/addons/mrp/views/stock_move_views.xml#L41 https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/addons/stock/views/stock_move_views.xml#L128 `state` doesn't exist on `product.product`, hence the crash. opw-6433620 Forward-Port-Of: odoo/odoo#279811
When using multiple devices sharing draft orders, a race condition can happen where one device reuses another device's empty synced draft order. This leads to duplicate UUIDs, which triggers automatic order merging in `sync_from_ui` on the server and clears the table association. To prevent this: - Filter out synced orders (`!order.isSynced`) in `getEmptyOrder()`, `createOrderIfNeeded()`, and `setTable()` when looking for reusable empty orders. - This ensures each terminal only reuses its o
Original PR description
When using multiple devices sharing draft orders, a race condition can happen where one device reuses another device's empty synced draft order. This leads to duplicate UUIDs, which triggers automatic order merging in `sync_from_ui` on the server and clears the table association. To prevent this: - Filter out synced orders (`!order.isSynced`) in `getEmptyOrder()`, `createOrderIfNeeded()`, and `setTable()` when looking for reusable empty orders. - This ensures each terminal only reuses its own locally created, unsynced empty orders, guaranteeing unique UUIDs per device session. task-id: 6296661 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#269551
`cbc:RoundingAmount` was the sum of `raw_total_excluded` over the non-fixed taxes plus the sum of `raw_tax_amount` over all the taxes, so the base of the line was counted once per non-fixed tax, inflating the line total. `cbc:TaxableAmount` was taken from the tax details of each grouping key, which is the base of that specific tax not the net amount of the line expected by JoFotara. Both amounts are now read from the base line tax details (`raw_total_included` and `raw_total_excluded`), so
Original PR description
`cbc:RoundingAmount` was the sum of `raw_total_excluded` over the non-fixed taxes plus the sum of `raw_tax_amount` over all the taxes, so the base of the line was counted once per non-fixed tax, inflating the line total. `cbc:TaxableAmount` was taken from the tax details of each grouping key, which is the base of that specific tax not the net amount of the line expected by JoFotara. Both amounts are now read from the base line tax details (`raw_total_included` and `raw_total_excluded`), so they describe the line itself regardless of the number of taxes set on it. The document level `cbc:TaxableAmount` keeps using the aggregated tax details. Even tho the problems were hidden because in Jordan, a line wouldn't have more than 1 percent tax + 1 fixed tax, it emerged during the development of the fix in this PR: https://github.com/odoo/odoo/pull/279335 Forward-Port-Of: odoo/odoo#281262
Fix for the runbot nightly error 944444. bug: The website configurator crashed entirely if one theme snippet's template was missing, instead of just skipping that snippet. steps: Only visible while running `test_01_configurator_translation` fix: Catch MissingError, alongside ValueError, when generating and rendering configurator snippet content. task-6325919
Original PR description
Fix for the runbot nightly error 944444. bug: The website configurator crashed entirely if one theme snippet's template was missing, instead of just skipping that snippet. steps: Only visible while running `test_01_configurator_translation` fix: Catch MissingError, alongside ValueError, when generating and rendering configurator snippet content. task-6325919
Prior to this commit, the call debrief timeline was not properly adapted for RTL layouts. The playhead and timestamp were positioned incorrectly, making the component unusable in RTL. This commit forces the timeline to always render in LTR, as it represents the physical progression of time moving forward. task-6445011 Requires: - https://github.com/odoo/enterprise/pull/127038 | Before | After | |--------|--------| | <img width="1920" height="698" alt="image" src="https://github.com/
Original PR description
Prior to this commit, the call debrief timeline was not properly adapted for RTL layouts. The playhead and timestamp were positioned incorrectly, making the component unusable in RTL. This commit…
Prior to this commit, the call debrief timeline was not properly adapted for RTL layouts. The playhead and timestamp were positioned incorrectly, making the component unusable in RTL. This commit forces the timeline to always render in LTR, as it represents the physical progression of time moving forward. task-6445011 Requires: - https://github.com/odoo/enterprise/pull/127038 | Before | After | |--------|--------| | <img width="1920" height="698" alt="image" src="https://github.com/user-attachments/assets/780f6e83-b67d-44bc-8b00-8e92048ff13b" /> | <img width="1390" height="463" alt="Screenshot 2026-08-11 at 08 51 00" src="https://github.com/user-attachments/assets/8a2a833c-2164-40ed-a4c7-006b7ca5bec4" /> | | <img width="1385" height="418" alt="Screenshot 2026-08-04 at 15 02 05" src="https://github.com/user-attachments/assets/f0c75f29-e459-4ee7-976d-3674ae76ff81" /> | <img width="1384" height="479" alt="Screenshot 2026-08-11 at 08 51 22" src="https://github.com/user-attachments/assets/f60b98c5-5a33-4d01-8521-0f2f3bdd6424" /> | --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Currently, with images enabled for quotation reports and if the product name is long enough, it overflows through the column border a bit before breaking to the next line. This seems to be caused by some incompatibility between wkhtmltopdf and d-flex, adding `col` to the product name container forces it to respect column boundaries. This commit also adds `text-break` as having a long uninterupded string in the description would end up stretching the column and making the next ones disapear
Original PR description
Currently, with images enabled for quotation reports and if the product name is long enough, it overflows through the column border a bit before breaking to the next line. This seems to be caused by…
Currently, with images enabled for quotation reports and if the product name is long enough, it overflows through the column border a bit before breaking to the next line. This seems to be caused by some incompatibility between wkhtmltopdf and d-flex, adding `col` to the product name container forces it to respect column boundaries. This commit also adds `text-break` as having a long uninterupded string in the description would end up stretching the column and making the next ones disapear. <img width="811" height="266" alt="image" src="https://github.com/user-attachments/assets/90043c88-d03f-4324-9935-e669f6a256ab" /> <img width="964" height="431" alt="image" src="https://github.com/user-attachments/assets/2b8e8a6c-6fd5-4813-8160-d94bbb5490d1" /> ### Steps to reproduce: - Enable Display Product Images in the setting - Create a product with an image - Create a quotation with said product and add a description that is long enough to break to the next line - Print said quotation - You will notice that before the line breaks it will overflow to the next column (you might need to tweak the description a bit by removing or adding a few lines) opw-6367076 Forward-Port-Of: odoo/odoo#275978
Improves UX by deselecting when clicking the header checkbox in list views instead of selecting all. WHY: Fits the UX of almost all other software and saves one click :) Since @34b9af762fa2, the header checkbox of a list view renders in its indeterminate state ([-]) when part of the displayed records are selected. When doing that feature, we thought about the "escape" case to deselect all, but not the click header checkbox case. This PR fixes that. task-6237965
Original PR description
Improves UX by deselecting when clicking the header checkbox in list views instead of selecting all. WHY: Fits the UX of almost all other software and saves one click :) Since @34b9af762fa2, the header checkbox of a list view renders in its indeterminate state ([-]) when part of the displayed records are selected. When doing that feature, we thought about the "escape" case to deselect all, but not the click header checkbox case. This PR fixes that. task-6237965
Clicking the internal-link arrow on `work_entry_type_id` (Time Off Type) implicitly saved the `hr.leave` record before opening the related form dialog, since `Many2One.openRecordInDialog()` calls `willOpenRecordInDialog()`, which defaults to `record.save()`. For a new leave request, this triggered `hr.leave.create()`'s validation logic, auto-confirming (or even auto-approving) the request before the user had finished the wizard. This premature save was also the root cause of two related bugs,
Original PR description
Clicking the internal-link arrow on `work_entry_type_id` (Time Off Type) implicitly saved the `hr.leave` record before opening the related form dialog, since `Many2One.openRecordInDialog()` calls…
Clicking the internal-link arrow on `work_entry_type_id` (Time Off Type) implicitly saved the `hr.leave` record before opening the related form dialog, since `Many2One.openRecordInDialog()` calls `willOpenRecordInDialog()`, which defaults to `record.save()`. For a new leave request, this triggered `hr.leave.create()`'s validation logic, auto-confirming (or even auto-approving) the request before the user had finished the wizard. This premature save was also the root cause of two related bugs, both fixed as a side effect: closing the Time Type dialog threw `TypeError: Cannot read properties of undefined (reading 'focus')` (the save re-rendered the parent, leaving `Many2One`'s `onClose` pointing at a stale component), and that crash aborted the promise chain before it could reload the calendar, leaving a newly-created leave request missing from view until a manual refresh. Adds a `many2one_no_save` field widget overriding `willOpenRecordInDialog` to skip the save, applied to `work_entry_type_id` on the base `hr_leave_view_form` so all inheriting views are covered. task-6452848
Currently, an error occurs when updating the quantity of an SOL in the portal. **Steps to Reproduce:** - Install the `sale_management` module. - Go to `Settings` and enable `Pricelists` under Pricing. - Go to `Sales` > `Orders` > `Orders` and create a `sale order`. - Add an `order line` of type `Section`, click the `three-dot` menu on the line, and click `Set Optional`. - Add another line using `Add a line` and enter only the `description` in that line. - Save the `order` and clic
Original PR description
Currently, an error occurs when updating the quantity of an SOL in the portal. **Steps to Reproduce:** - Install the `sale_management` module. - Go to `Settings` and enable `Pricelists` under…
Currently, an error occurs when updating the quantity of an SOL in the portal. **Steps to Reproduce:** - Install the `sale_management` module. - Go to `Settings` and enable `Pricelists` under Pricing. - Go to `Sales` > `Orders` > `Orders` and create a `sale order`. - Add an `order line` of type `Section`, click the `three-dot` menu on the line, and click `Set Optional`. - Add another line using `Add a line` and enter only the `description` in that line. - Save the `order` and click `Preview`. - `Increase` the quantity of the line using the + icon. `ValueError: Expected singleton: product.product()` After [recent commit], support for productless Sale Order Lines was added, allowing users to create quotation/invoice lines directly from descriptions without requiring a product. After [this commit], when updating the quantity of a productless SOL in the portal, the price is updated based on the pricelist [1]. While getting the pricelist price [2] for the SOL, the product is ensured on the SOL, which raises the error [3] because the SOL does not have a product. This commit ensures that the unit price is updated based on the pricelist only if the line contains a product. [recent commit]: https://github.com/odoo/odoo/commit/d069ce59e28fda2bc25fb89fd01ee7b988a30fa0 [this commit]: https://github.com/odoo/odoo/commit/86d40fe5c5ad32af0ceca7cd384b96a8a2f4adc3 [1]- https://github.com/odoo/odoo/blob/e940a655aba06adc784008b72a3917170fdb3dd0/addons/sale_management/controllers/portal.py#L68-L69 [2]- https://github.com/odoo/odoo/blob/e940a655aba06adc784008b72a3917170fdb3dd0/addons/sale/models/sale_order_line.py#L783 [3]- https://github.com/odoo/odoo/blob/e940a655aba06adc784008b72a3917170fdb3dd0/addons/sale/models/sale_order_line.py#L801 sentry-7665145989 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
***Steps to reproduce*:** * Install the `l10n_din5008` module. * Set up a German Company and set the fiscal location to Germany. * Select the German company and create a new Purchase Journal. * Enable **Self Billing** for the journal. * Create a vendor bill and print the PDF. ***Observed behavior*:** * The printed document displays the regular vendor bill header instead of the self-billing header. ***Cause*:** * The condition required to display the self-billing header was mi
Original PR description
***Steps to reproduce*:** * Install the `l10n_din5008` module. * Set up a German Company and set the fiscal location to Germany. * Select the German company and create a new Purchase Journal. * Enable **Self Billing** for the journal. * Create a vendor bill and print the PDF. ***Observed behavior*:** * The printed document displays the regular vendor bill header instead of the self-billing header. ***Cause*:** * The condition required to display the self-billing header was missing from the report template if self-billing is enabled. ***Fix*:** * Add the missing condition so that the self-billing header is displayed when **Self Billing** is enabled on the journal. * For reference, here is the [Document](https://www.gesetze-im-internet.de/ustg_1980/__14.html) link. Ticket [link](https://www.odoo.com/odoo/project.task/6281066) opw-6281066 Forward-Port-Of: odoo/odoo#282664 Forward-Port-Of: odoo/odoo#272857
## Description When a CI/NIE number fails the check digit validation, the raised error suggests `3:402.010-2 or 93:402.010-1 (CI or NIE)` as the expected format. This is misleading for two reasons: - The colon notation comes from Uruware's technical manual and is not how identity card numbers are written in Uruguay: users write `3.402.010-1` or plain digits (`34020101`). Real users facing the error keep reformatting a number whose check digit is simply wrong, believing the problem is the forma
Original PR description
## Description When a CI/NIE number fails the check digit validation, the raised error suggests `3:402.010-2 or 93:402.010-1 (CI or NIE)` as the expected format. This is misleading for two reasons: -…
## Description When a CI/NIE number fails the check digit validation, the raised error suggests `3:402.010-2 or 93:402.010-1 (CI or NIE)` as the expected format. This is misleading for two reasons: - The colon notation comes from Uruware's technical manual and is not how identity card numbers are written in Uruguay: users write `3.402.010-1` or plain digits (`34020101`). Real users facing the error keep reformatting a number whose check digit is simply wrong, believing the problem is the format. - The CI example shown is itself an **invalid** number: the check digit of `3.402.010` is `1`, not `2` — the module's own test suite (`l10n_uy/tests/test_check_vat.py`) uses `3:402.010-2` as an invalid sample. This was reported by an Uruguayan eCommerce user who lost sales because buyers interpreted the message as requiring an unusual format that does not exist in Uruguay. ## Change Replace the examples with valid numbers written in the notations actually used in Uruguay, and clarify that separators are optional — which is exactly what `_l10n_uy_ci_nie_is_valid()` already accepts (it strips `:. ,-` before checking the verification digit): ``` 3.402.010-1 or 34020101 (CI), 93.402.010-1 (NIE); dots and dash are optional ``` Only the `expected_format` string changes; the validation logic is untouched. Existing tests only assert the fixed part of the message (`The CI/NIE number.*does not seem to be valid`), so they are unaffected. Forward-Port-Of: odoo/odoo#282489
**Steps to reproduce:** 1. Install the sale module with demo data. 2. Open a sale order and add a combo product (e.g. "Office Combo"). 3. Note the displayed untaxed amount on the combo item lines (e.g. 160.00). 4. Switch the document tax mode from "Tax Excl." to "Tax Incl." using the toggle at the top of the Order Lines tab. **Issue:** - After switching to "Tax Incl." mode, the total on the sale order is higher than the previous untaxed amount (e.g. shows 266.00 instead of the 160.00).
Original PR description
**Steps to reproduce:** 1. Install the sale module with demo data. 2. Open a sale order and add a combo product (e.g. "Office Combo"). 3. Note the displayed untaxed amount on the combo item lines…
**Steps to reproduce:** 1. Install the sale module with demo data. 2. Open a sale order and add a combo product (e.g. "Office Combo"). 3. Note the displayed untaxed amount on the combo item lines (e.g. 160.00). 4. Switch the document tax mode from "Tax Excl." to "Tax Incl." using the toggle at the top of the Order Lines tab. **Issue:** - After switching to "Tax Incl." mode, the total on the sale order is higher than the previous untaxed amount (e.g. shows 266.00 instead of the 160.00). - The same switch works correctly on regular (non-combo) product lines **Expected behavior:** - the new total should equal the previous untaxed amount. **Why this happens:** - `_onchange_order_line` is triggered on every `order_line` change, including when switching `document_tax_mode` causes `price_subtotal` to update on existing lines. - The unconditional write to `product_uom_qty` and `discount` on all combo item lines was calling '_compute_price_unit` even when neither value had changed. - `_compute_price_unit` then called `_reset_price_unit` under the new `document_tax_mode`, which re-calculated the stored `price_unit` instead of leaving it untouched for `_compute_amount` to reinterpret correctly. **Fix:** - Only sync `product_uom_qty` and `discount` to the combo item lines whose current values actually differ from the parent line. When no value has changed, no write occurs, so `_compute_price_unit` is not called. opw-6427963
Steps: - Install l10n_ma module. - Go to Address page in portal. - Select Morocco country. - Set any random invalid value for ICE input. Issue: - It wont allow to save address and will not give any error or suggestion instead it raises validation error in logger. Cause: - Since recent [fix](https://github.com/odoo/odoo/pull/271969) invalid additional_identifier raises an error for invalid values and in portal we can't display those error directly. Fix: - Validate `additional_iden
Original PR description
Steps: - Install l10n_ma module. - Go to Address page in portal. - Select Morocco country. - Set any random invalid value for ICE input. Issue: - It wont allow to save address and will not give any error or suggestion instead it raises validation error in logger. Cause: - Since recent [fix](https://github.com/odoo/odoo/pull/271969) invalid additional_identifier raises an error for invalid values and in portal we can't display those error directly. Fix: - Validate `additional_identifier` in validate address values and and mark those identifier as invalid field for invalid values to give proper error message and not raise validation error. Also skip `_validate_identifier` for `no_vat_validation` context in creation since we already validate those identifier values.
Fix the function signature of the _reset_peppol_configuration function Step to reproduce: - Install l10n_fr_pdp and register a company on Peppol - When the _peppol_out_of_sync_disconnect_this_database will be called, it will call the _reset_peppol_configuration(soft=True), and since l10n_fr_pdp overrides this function but don't declare the soft parameter, it will raise a TypeError. opw-5728596 Forward-Port-Of: odoo/odoo#282763
Original PR description
Fix the function signature of the _reset_peppol_configuration function Step to reproduce: - Install l10n_fr_pdp and register a company on Peppol - When the _peppol_out_of_sync_disconnect_this_database will be called, it will call the _reset_peppol_configuration(soft=True), and since l10n_fr_pdp overrides this function but don't declare the soft parameter, it will raise a TypeError. opw-5728596 Forward-Port-Of: odoo/odoo#282763