Daily updates from Odoo
Monday, June 29, 2026
127 changes
7 changes
Resolved issues and error corrections
When a Google account is disconnected with the option to delete events, recurring calendar entries are now fully removed as well. This prevents leftover recurrence records from staying behind and keeps calendars cleaner and more consistent.
Original PR description
When we disconnect a google account with the delete events option enabled, we should also delete `calendar.recurrence` records, not just the base events Follow-up for odoo/odoo@ae52126522e3570e11845c3c04d0eb6b198b3848
This update corrects how taxes are handled and displayed across invoicing, purchasing, and sales screens. It also improves imported Italian invoices by preserving the correct tax mode, helping avoid inconsistent totals and reducing manual corrections.
Original PR description
- changing python constraint on document tax mode on account.move to SQL - removing custom component now resolved by just adding xml field required condition - specifying child type to target for css class overlap_badge_tab - style enhancements to the overlap_badge_tab - removing inconsistent rounding in purchase.order - adding document tax mode logic to account.tax compute_all method, adapted for sale_margin - adding missing document tax mode ‘tax_excluded’ setting to l10n_it_edi during account.move creation of imported invoices Following up: https://github.com/odoo/odoo/pull/251800
This fixes an issue in the website editor where changing theme styles could accidentally mark unrelated content as modified. As a result, saving after a style change no longer saves unrelated views, reducing unnecessary updates and helping keep site edits clean.
Original PR description
__Problem__ Since odoo/odoo@163a317c9d3, editing website styles would save some unrelated views. __To reproduce__ 1. Open the website builder. 2. Switch to the theme tab. 3. Edit a color. ⮕ Some unrelated elements become `o_dirty`. 4. Undo. ⮕ The elements remain `o_dirty`. 5. Save. ⮕ Views linked to those elements are unnecessarily saved. __Reason__ During normalization, we call `refreshInteractions` → `disableObserver` → `on_pending_mutations_staged_handlers`, which adds `o_dirty` via the `save_plugin`. __Quick fix__ Change the normalization sequence so that it runs first. At that point, there are no pending mutations yet, so `o_dirty` is not added. task-6333960
The Shopee connector now handles customer identifiers that are larger than the previously expected limit, preventing errors when creating new contacts. For cases where the ID is too large for the existing database field, Odoo stores and matches it using an alternate contact reference so Shopee orders keep syncing properly.
Original PR description
When the Shopee connector was introduced, Shopee documented `buyer_user_id` as an int32. We therefore store it in an `Integer` field, which maps to a PostgreSQL int32 column. However, feedback showed that Shopee can send ids exceeding the int32 bounds, leading to a traceback when creating new contacts. Shopee has since updated their documentation to confirm the field is actually an int64. Since we are in stable and cannot change the column type, this commit falls back to the `ref` field of the `res.partner` model to store and match Shopee contacts whose identifier doesn't fit within an int32. opw-6325948 See also: - master: https://github.com/odoo/enterprise/pull/121507 - upgrade: https://github.com/odoo/upgrade/pull/10578 Forward-Port-Of: odoo/enterprise#121707 Forward-Port-Of: odoo/enterprise#121498
This change ensures translation terms are only applied to website-specific editors, so Studio’s XML editor no longer picks up the default language from the first website in the database. It prevents confusing translation mismatches and keeps the special website behavior limited to the HTML/CSS editor where it belongs.
Original PR description
Problem: When opening the Studio XML editor when Website is installed, the translation terms corresponding to the Default Language of the first website in the database are used. This behavior should only be applied to the HTML/CSS Editor in Website. Purpose: Modify Website's override of get_related_views to only return translated views when called with a specific website in context. This is done here by adding a context flag, as to not interfere with customizations made in stable versions. This will be changed for master. Steps to Reproduce in Runbot: 1. Activate a non-English (US) language. 2. Add this language to the Website with the lowest ID in the database, then set it to the Default Language of the Website. 3. While in debug mode, enter Studio and navigate to a view that has translation terms (ex. Sale Order Form view), then open the XML editor. opw-5136124 Foward Port of https://github.com/odoo/odoo/pull/237000 Forward-Port-Of: odoo/odoo#263592
This update prevents errors when importing Shopee customers whose identifiers are larger than the system’s standard number range. If an ID is too large to store in the usual field, Odoo now uses an alternative customer reference so contact creation and matching can still work correctly.
Original PR description
When the Shopee connector was introduced, Shopee documented `buyer_user_id` as an int32. We therefore store it in an `Integer` field, which maps to a PostgreSQL int32 column. However, feedback showed that Shopee can send ids exceeding the int32 bounds, leading to a traceback when creating new contacts. Shopee has since updated their documentation to confirm the field is actually an int64. Since we are in stable and cannot change the column type, this commit falls back to the `ref` field of the `res.partner` model to store and match Shopee contacts whose identifier doesn't fit within an int32. opw-6325948 See also: - master: https://github.com/odoo/enterprise/pull/121507 - upgrade: https://github.com/odoo/upgrade/pull/10578 Forward-Port-Of: odoo/enterprise#121707 Forward-Port-Of: odoo/enterprise#121498
When a Google Calendar account is disconnected with event deletion enabled, the system now removes all related recurring schedule rules as well as the individual events. This prevents leftover recurrence data from blocking future resynchronization of the same account.
Original PR description
When we disconnect a google account with the delete events option enabled, we should also delete `calendar.recurrence` records, not just the base events. Broken flow: 1. Sync google calendar with some recurrent event on google 2. Reset account via the wizard 3. -> The events are removed, but the recurrent rule remains in the db 4. Resync with the same account 5. -> The recurrence is not created because it already exists on the database Follow-up for odoo/odoo@ae52126522e3570e11845c3c04d0eb6b198b3848
16 changes
Resolved issues and error corrections
Users can now sort project tasks by Planned Date in the portal without encountering an error. This restores a common sorting option and prevents the page from failing when customers review their tasks.
Original PR description
Currently, an error occurs when a user sorts tasks by Planned Date. **Steps to reproduce:** - Install the `project_enterprise` module with demo data. - Go to Projects in the portal (`/my/projects`),…
Currently, an error occurs when a user sorts tasks by Planned Date. **Steps to reproduce:** - Install the `project_enterprise` module with demo data. - Go to Projects in the portal (`/my/projects`), open any `project`, and sort the tasks by `Planned Date`. KeyError: 'order' After a [recent change], the sort order is retrieved from searchbar sortings. When sorting by Planned Date, it attempts to access the order key from the corresponding sorting configuration [1]. However, the planned_date_begin entry does not define an order key [2], which raises error when it tries to access order. This commit ensures that the order key is added with its value for planned_date_begin in searchbar sorting. [recent change]: https://github.com/odoo/odoo/commit/8be5dacf9fbfe8c23b04c876994bea2ce7cbb89a [1]: https://github.com/odoo/odoo/blob/2ae9b57b86cd0bc4816ff8ec207564631baa8ad6/addons/project/controllers/portal.py#L424 [2]- https://github.com/odoo/enterprise/blob/9dd3a9b2c09a6d23a3f71d3e531edd6d78b30277/project_enterprise/controllers/portal.py#L8-L11 sentry-7556050938 Forward-Port-Of: odoo/enterprise#121230 Forward-Port-Of: odoo/enterprise#121141
Flexible employees can now request a one-day time off on a public holiday when the time off type counts public holidays in the duration. This fixes cases where the request was wrongly rejected, making single-day and multi-day requests behave consistently.
Original PR description
Currently, flexible employees can request a multi-day leave spanning a public holiday when the leave type includes public holidays in duration. However, requesting the public holiday date alone is…
Currently, flexible employees can request a multi-day leave spanning a public holiday when the leave type includes public holidays in duration. However, requesting the public holiday date alone is rejected. ### **Steps to reproduce:** - Create a public holiday. - Create a time off type with "Public Holiday Included" enabled. - Select/create an employee with a flexible work schedule and its time zone must be same as admin. - Request a time off on the public holiday date only. ### **Observed Behavior:** The request is rejected because its duration is computed as 0 days. ### **Expected Behavior:** The request should be allowed and count as 1 day, consistent with the multi-day request behavior. ### **Root Cause:** At [1], a dedicated duration computation path is used for single-day leaves of flexible employees. This logic always retrieves overlapping public holidays and computes the leave duration based on the remaining intervals. As a result, a leave requested entirely on a public holiday is computed as 0 days, even when `include_public_holidays_in_duration` is enabled. [1]- https://github.com/odoo/odoo/blob/242f6d3cf7288853f163ac6986a3b7aa4279efaf/addons/hr_holidays/models/hr_leave.py#L436-L444 ### **Fix:** This commit ensures that the `include_public_holidays_in_duration` setting is taken into account when computing single-day leave durations for flexible employees **opw-6284768** Forward-Port-Of: odoo/odoo#272217 Forward-Port-Of: odoo/odoo#269743
This update fixes the data sent when cancelling documents through the Mexican e-invoicing service. It ensures the certificate is encoded correctly again, so cancellation requests complete successfully instead of failing.
Original PR description
Issue: https://drive.google.com/file/d/1-57iHrMoBo79EbUaWuwkJut_7FfyzeLY/view
`_sw_cancel` sent `pem_certificate.decode('UTF-8')` as `b64Cer`, which used to work because `fields.Binary` returned base64-encoded bytes. Since binary fields now return a `BinaryValue`, `.decode()` returns the raw PEM content instead of base64, breaking the SW cancel API call.
Use `.to_base64() " to properly base64-encode the certificate, matching the pattern already used for `b64Key " and in `_sw_sign ".
related change for binary fields: [41fe2eb](https://github.com/odoo/odoo/commit/41fe2ebdb9cc)
task-6319057This fix makes website popups reliably open and close even when users click the visibility toggle very quickly. It prevents the popup state from getting out of sync with the editor icon and avoids follow-up errors when adding nested popups.
Original PR description
__To reproduce__ 1. Drop a popup on the website. 2. Click twice rapidly on the popup show/hide toggle in the sidebar. => The popup visibility will be in an inconsistent state compared to the toggle's…
__To reproduce__ 1. Drop a popup on the website. 2. Click twice rapidly on the popup show/hide toggle in the sidebar. => The popup visibility will be in an inconsistent state compared to the toggle's eye icon. __Reason__ Bootstrap ignores any call to show/hide if the popup is still transitioning. __Fix__ - Set `_isTransitioning` to `false` to trick Bootstrap into firing the event regardless of its current state. - When hide/show are triggered in quick succession, the modal can enter an inconsistent state with `.show` class but `display: none` style (hide removes `.show` immediately, show restores it with `display: block`, then hide applies `display: none` after animation). Dispatching `transitionend` event before resetting `_isTransitioning` ensures Bootstrap completes its state transitions. __Note__ This commit also fixes `custom_popup_snippet`, which fails non-deterministically with the following error: `TypeError: Cannot read properties of undefined (reading 'after')` This error occurs when trying to add a popup inside another popup that never closes due to this bug. runbot-939039 Forward-Port-Of: odoo/odoo#269782
This change prevents a website error that could happen when adding a dynamic product block to a page. It makes the system skip a date check for placeholder products that do not have a publish date, so page editing works smoothly without interruption.
Original PR description
Currently, an error occurs when a user insert a dynamic product snippet onto website. **Steps to Reproduce:** - Install `website_sale` without demo data. - Go to `Website` > `eCommerce` > `Products`…
Currently, an error occurs when a user insert a dynamic product snippet onto website. **Steps to Reproduce:** - Install `website_sale` without demo data. - Go to `Website` > `eCommerce` > `Products` > `Ribbons`. - Open any ribbon and set `Assign` to `When New`. - Go to the `Website` > `Edit` > drag and drop the `Catalog` block, and select a `Dynamic Product` block (`Generic Product Template (customizable)` [ref](https://drive.google.com/file/d/1ZYeQA8DhfYAf_jbPwL-tZXaVi1YAyFbd/view?usp=drive_link)). `TypeError: unsupported operand type(s) for -: 'datetime.datetime' and 'bool'` After this [recent commit], when a user drops a dynamic product snippet, the system attempts to set the ribbon value [1]. If no product records are available, it falls back to using sample products [2]. These sample products are virtual records created with model.new() [3] [4] and are not stored in the database, so they do not have a publish date. When auto-assigned ribbon with the When New option assign, the system compares the ribbon's new period with the product's publish date. Since the sample products do not have a publish date, this comparison raises an error [5]. This commit ensures that the comparison between new period and publish date is only performed when the product has a valid publish date. [recent commit]: https://github.com/odoo/odoo/commit/f427f795c24ee37ee02302642b77bfc314a9ea43 [1]- https://github.com/odoo/odoo/blob/b70330df7dc2017ab592b075c8ad8e048f671d90/addons/website_sale/templates/snippets/product_snippet_template_data.xml#L52-L58 [2]: https://github.com/odoo/odoo/blob/b70330df7dc2017ab592b075c8ad8e048f671d90/addons/website/models/website_snippet_filter.py#L75-L78 [3]- https://github.com/odoo/odoo/blob/b70330df7dc2017ab592b075c8ad8e048f671d90/addons/website/models/website_snippet_filter.py#L210-L215 [4]: https://github.com/odoo/odoo/blob/b70330df7dc2017ab592b075c8ad8e048f671d90/addons/website_sale/models/website_snippet_filter.py#L62-L95 [5]: https://github.com/odoo/odoo/blob/b70330df7dc2017ab592b075c8ad8e048f671d90/addons/website_sale/models/product_ribbon.py#L121-L124 sentry-7528296918 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update prevents command shortcuts and markdown auto-formatting from triggering inside code blocks. It avoids errors when users type commands in code content and keeps code formatting from being changed unexpectedly.
Original PR description
### Steps to reproduce: - Go to ToDo. - Create a code block using `/code`. - Place the cursor inside the code block. - Type `/table` and select the table command. - A traceback occurs. ### Purpose of this PR: - Commands and markdown shorthands should not be available inside code blocks. However, typing `/` inside a `<pre>` opened the command palette, allowing structural commands such as `/table` to be executed and causing a traceback. Similarly, markdown shorthands such as `* ` and `1.` were still active, unexpectedly transforming code content into lists. ### This PR fixes the issue by: - Disabling the command palette when the cursor is inside a `<pre>` element. - Disabling markdown shorthands inside `<pre>` elements by registering an `are_shorthand_available_predicates` predicate. task-6292231 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#271796 Forward-Port-Of: odoo/odoo#269430
This fix prevents a traceback in Planning when the system tries to auto-plan or send schedules without start or end dates. It makes the workflow safer and avoids interruptions for users working with incomplete planning data.
Original PR description
Add a guarding condition to the auto-plan or send behavior to avoid doing those operations when no start/end dates are defined --- Task: 6312650 Forward-Port-Of: odoo/enterprise#121808
The text editor now keeps the correct text selection when a popover is closed by clicking back into the page. This prevents the editor from unexpectedly resetting the selection, which could otherwise affect formatting actions like text color changes.
Original PR description
When a popover is used on top of an iframe, when clicking outside the popover but inside the iframe to close the popover, the click away is detected through a blur event. The close during the click…
When a popover is used on top of an iframe, when clicking outside the popover but inside the iframe to close the popover, the click away is detected through a blur event. The close during the click away is used by some features to apply the change. Setting the text color in the editor might modify the DOM structure and therefore needs to readjust the selection to match the new DOM nodes. When confirming a color, or when applying intermediary changes this behavior is required. But when the color confirmation happens because of a click away, this selection gets on the way of the selection made by clicking away. The sequence of events is as follows: - pointerdown in iframe: sets the selection based on the click - main window blur: color gets applied, and selection gets reset - pointermove in iframe: expands selection to new position This commit avoids this unwanted reset by detecting the click away through a pointerdown on the iframe - thus making the selection adjustment before the default browser handling of the event actually sets the selection on the clicked position. Steps to reproduce: - Edit a website page - Drop a text snippet - Select a word - Set a custom color using the color area - so that the color picker remains open - Click (and hold) in the empty area after the text paragraph - Move pointer by a few pixels => The text anchor remained the one of the former selection, but the focus was set according to the pointer position Note that Firefox did not blur the main window, therefore this change actually also unifies the behavior of Chrome and Firefox. The discrepancy between Chrome and Firefox's handling of the blur event in this situation seems to be related to either prioritizing the DOM specifications or the HTML specifications - and the way the are interpreted. The DOM Standard [1] relies on UIEvents [2] which states: "A user agent MUST dispatch this event when an event target loses focus." The HTML Standard defines how to compute the focus [3] and relies on nested browsing contexts [4]. Typically, the simpler readable description for developers says: "document.hasFocus() Returns true if key events are being routed through or to document; otherwise, returns false. Roughly speaking, this corresponds to document, or a document nested inside document, being focused." [1]: https://dom.spec.whatwg.org/#biblio-uievents [2]: https://w3c.github.io/uievents/#event-type-blur [3]: https://html.spec.whatwg.org/multipage/interaction.html#focus-processing-model [4]: https://html.spec.whatwg.org/multipage/document-sequences.html#nested-browsing-contexts task-6268573 Forward-Port-Of: odoo/odoo#269742
This fix stops public holiday work entries from being counted twice when they overlap with worked-time leave, such as sandwich leave rules. It ensures the holiday is handled first so payroll work entries stay accurate and employees are not overcounted on the same day.
Original PR description
Issue: When work entries are generated from Attendances, a worked-time time off created by the Indian sandwich rule can overlap a public holiday and generate duplicate work entries for the same day.…
Issue: When work entries are generated from Attendances, a worked-time time off created by the Indian sandwich rule can overlap a public holiday and generate duplicate work entries for the same day. Steps to reproduce: - Create an employee with Work Entry Source set to Attendances - Use a flexible working schedule on the employee - Configure a public holiday on a scheduled day with work entry type (Paid time off) - Create a time off type with Count as set to Worked Time - Generate time off for the period so the public holiday entry exists (maybe a day before and a the public holiday and the day after) - Open Payroll > Work Entries (Observe the date of the public holiday will have more than 8h entry) Cause: In `_get_version_work_entries_values()`, calendar leaves are split by `hr_holidays` `time_type` into: - leaves: absences and public holidays - worked_leaves: worked-time time off For attendance-based contracts, both sets were turned into work entries without removing overlap between a public holiday and a worked-time leave on the same period. https://github.com/odoo/odoo/blob/3a088e23d3e563c39cdcb252edc8c7cc74981de4/addons/hr_work_entry/models/hr_version.py#L222-L226 For non-flexible calendar: Public holidays and worked-time leaves are both clipped to the static working schedule (e.g. 8h per working day). overlap was kept in both result sets. https://github.com/odoo/odoo/blob/3a088e23d3e563c39cdcb252edc8c7cc74981de4/addons/hr_work_entry/models/hr_version.py#L260 For flexible calendar: The one-day intervals are kept as the actual interval (often 00:00-23:59 for a public holiday). The worked-time on that day is schedule-shaped (e.g. 8h). Subtracting intervals on a full-day public holiday left a 16h fragment instead of removing the public holiday entry. https://github.com/odoo/odoo/blob/3a088e23d3e563c39cdcb252edc8c7cc74981de4/addons/hr_work_entry/models/hr_version.py#L242-L249 Solution: We need to make regular leaves take priority over worked-time leaves, compute the real regular leave intervals first, then remove those intervals from the worked-time leave intervals before work entries are created: - for fully flexible employees, subtract regular leaves from worked leaves; - for flexible calendars, keep one-day regular leaves as is and subtract them from worked-time leaves - for non-flexible attendance-based calendars, clip regular leaves on the static schedule, then subtract them from worked-time leaves clipped on the same schedule. This means that when a sandwich worked-time leave overlaps a public holiday, the public holiday consumes that period first. The overlapping part is then removed from `real_worked_leaves`, so no second worked-time entry is generated for the same public holiday period. opw-6237163 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/enterprise#121542 Forward-Port-Of: odoo/enterprise#119530
This change prevents public holidays and worked-time leave from being counted twice on the same day when work entries are generated from attendances. It ensures the public holiday is taken into account first, so payroll data stays correct and employees do not get duplicate time entries.
Original PR description
Issue: When work entries are generated from Attendances, a worked-time time off created by the Indian sandwich rule can overlap a public holiday and generate duplicate work entries for the same day.…
Issue: When work entries are generated from Attendances, a worked-time time off created by the Indian sandwich rule can overlap a public holiday and generate duplicate work entries for the same day. Steps to reproduce: - Create an employee with Work Entry Source set to Attendances - Use a flexible working schedule on the employee - Configure a public holiday on a scheduled day with work entry type (Paid time off) - Create a time off type with Count as set to Worked Time - Generate time off for the period so the public holiday entry exists (maybe a day before and a the public holiday and the day after) - Open Payroll > Work Entries (Observe the date of the public holiday will have more than 8h entry) Cause: In `_get_version_work_entries_values()`, calendar leaves are split by `hr_holidays` `time_type` into: - leaves: absences and public holidays - worked_leaves: worked-time time off For attendance-based contracts, both sets were turned into work entries without removing overlap between a public holiday and a worked-time leave on the same period. https://github.com/odoo/odoo/blob/3a088e23d3e563c39cdcb252edc8c7cc74981de4/addons/hr_work_entry/models/hr_version.py#L222-L226 For non-flexible calendar: Public holidays and worked-time leaves are both clipped to the static working schedule (e.g. 8h per working day). overlap was kept in both result sets. https://github.com/odoo/odoo/blob/3a088e23d3e563c39cdcb252edc8c7cc74981de4/addons/hr_work_entry/models/hr_version.py#L260 For flexible calendar: The one-day intervals are kept as the actual interval (often 00:00-23:59 for a public holiday). The worked-time on that day is schedule-shaped (e.g. 8h). Subtracting intervals on a full-day public holiday left a 16h fragment instead of removing the public holiday entry. https://github.com/odoo/odoo/blob/3a088e23d3e563c39cdcb252edc8c7cc74981de4/addons/hr_work_entry/models/hr_version.py#L242-L249 Solution: We need to make regular leaves take priority over worked-time leaves, compute the real regular leave intervals first, then remove those intervals from the worked-time leave intervals before work entries are created: - for fully flexible employees, subtract regular leaves from worked leaves; - for flexible calendars, keep one-day regular leaves as is and subtract them from worked-time leaves - for non-flexible attendance-based calendars, clip regular leaves on the static schedule, then subtract them from worked-time leaves clipped on the same schedule. This means that when a sandwich worked-time leave overlaps a public holiday, the public holiday consumes that period first. The overlapping part is then removed from `real_worked_leaves`, so no second worked-time entry is generated for the same public holiday period. opw-6237163 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#271637 Forward-Port-Of: odoo/odoo#268527
This fix restores the standalone Timesheet icon when Attendance from the backend is turned off. Users can once again log hours directly from the toolbar instead of losing access to the timesheet action.
Original PR description
Steps to reproduce: - Install Attendance + Timesheets - Disable "Attendances from Backend" - Timesheet icon disappears from toolbar Current behavior: Bridge module permanently removes the `timesheet…
Steps to reproduce: - Install Attendance + Timesheets - Disable "Attendances from Backend" - Timesheet icon disappears from toolbar Current behavior: Bridge module permanently removes the `timesheet systray` from the `registry` and merges it inside the attendance dropdown. When `attendance_from_systray` is off, the dropdown hides itself and takes the timesheet icon with it, leaving no way to log hours from the toolbar. Expected behavior: Timesheet icon should remain accessible when attendance from systray is disabled. Issue: `registry.remove()` at module load made the timesheet icon impossible to restore. Fix: Keep the timesheet systray registered. Patch TimesheetSystray to read `display_systray from lazy_session` on startup and show itself standalone when attendance from systray is off. The template no longer replaces nodes. Header and total_hours are toggled via `position="attributes"` (`t-if="!props.signInOut"`, a prop only passed by the attendance dropdown), and a self-contained dropdown-only footer with the Check out button is added after total_hours. Ref: odoo/enterprise#104347 task-6285068 Forward-Port-Of: odoo/enterprise#119840
This fix prevents text formatting from being applied outside the editable area in the website builder and translation editor. As a result, changes like font size now save correctly instead of being lost after saving the page.
Original PR description
Commit e0cf601ab6fc1d7cb007fcb16ee4bb6097ba6fdd added a condition to apply format on the parent of fully selected unsplittable nodes. But this could lead to format applied outside of `contenteditable=true` nodes in some cases of website builder. This commit adds a condition to only apply format outside the unsplittable if it is not the node with `contenteditable`. Steps to reproduce: - In event page, open website builder - Double click on "Starts" in the sidebar inside the event page - Change font size - Save - Bug: the changed font size is not save (a span with the font class was created outside the savable element) ### - In translate mode - Select a whole translation span - Change font size - Save - Bug: the changed font size is not save (the font class was added on the translation span) task-6308352 Forward-Port-Of: odoo/odoo#271888 Forward-Port-Of: odoo/odoo#270401
GSTR-2B matching now correctly handles vendor bills issued in foreign currencies. This prevents bills from being incorrectly flagged as partially matched when the GST portal reports amounts in INR, improving the accuracy of GST reconciliation.
Original PR description
**Steps to reproduce:** * Install the **l10n_in_reports** module. * Go to **Accounting → Configuration → Settings**, and enable **Multi-Currencies**. * Activate a foreign currency (e.g., USD) and set…
**Steps to reproduce:** * Install the **l10n_in_reports** module. * Go to **Accounting → Configuration → Settings**, and enable **Multi-Currencies**. * Activate a foreign currency (e.g., USD) and set an exchange rate. * Create a new vendor bill for an Indian vendor, setting the currency to USD. * Add lines to the bill and apply IGST/GST taxes, then confirm the bill. * Go to **Accounting → Reporting → GST Return Period** and initiate GSTR-2B matching for the period corresponding to the bill (using a valid JSON payload where the amounts are correctly reported in INR). **Observed behavior:** * The vendor bill is incorrectly marked as "Partially matched" instead of "Fully matched", accompanied by an exception stating that the total amount as per GSTR-2B does not match. **Cause:** * The GSTR-2B data fetched from the GST portal always reports values in the company's base currency (INR). * The `match_bills` method was directly comparing the GSTR-2B INR amounts ( `bill_total` and `bill_taxable_value`) against the bill's `amount_total` and `amount_untaxed` fields. * Because these fields return values in the document's foreign currency (e.g., USD), the mismatch triggers an exception and flags the bill as partially matched. **Fix:** * Modified the matching logic to compare GSTR-2B values against `abs(amount_total_signed)` and `abs(amount_untaxed_signed)`. * This ensures that the amounts evaluated during reconciliation are always correctly converted and compared in the company's base currency (INR). opw-6311097 Forward-Port-Of: odoo/enterprise#121677 Forward-Port-Of: odoo/enterprise#120967
Neutralized databases will now use the test VIES VAT validation service instead of accidentally contacting the live production service. This reduces the risk of unintended external requests while keeping custom endpoint settings unchanged.
Original PR description
Neutralized databases don't always have demo data, so they talk to production VIES endpoint. With this change, the production endpoint is replaced with the test one. If some database has a custom endpoint, nothing is done. @moduon MT-15142 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272036
This fix prevents key Marketing Automation items created from templates, such as the confirmed contacts list and related server action, from disappearing during a module upgrade. It matters because users can now upgrade the app without losing template-based setup they already created.
Original PR description
When a user chooses the "Double Opt-in" template (or others using `_create_records_with_xml_ids`), the system creates records like the "Confirmed contacts" list and a server action. It also adds an…
When a user chooses the "Double Opt-in" template (or others using `_create_records_with_xml_ids`), the system creates records like the "Confirmed contacts" list and a server action. It also adds an `ir.model.data` entry for them under the module, but sets `noupdate=False`. The problem is that during a module upgrade, Odoo's cleanup process (`_process_end`) deletes any record marked `noupdate=False` if it wasn't "reloaded" from a file. Since these records are created manually in the code and aren't in any XML file, they never get reloaded, so Odoo just deletes them and the records they point to. By setting `noupdate=True` when these rows are created, we tell the system to leave them alone during upgrades. Since these only exist at runtime and don't have a data file, we don't need to worry about them being updated later anyway. The helper was added in https://github.com/odoo/enterprise/commit/fd98660e352512e0b74d91958707687978154833. Steps to reproduce: 1. Install Marketing Automation 2. Go to Campaigns > New and pick the "Double Opt-in" template, then save 3. Confirm the "Confirmed contacts" mailing list and server action exist 4. Go to Apps and Upgrade Marketing Automation 5. Try to find the mailing list and server action again => They disappear after the upgrade. Ticket [link](https://www.odoo.com/odoo/project.task/6140191) opw-6140191 Forward-Port-Of: odoo/enterprise#115444
Opening the full message composer no longer crashes when a recipient is only known by email and not linked to a contact. The system now ignores those unresolved recipients in the expanded composer, keeping the messaging flow stable and consistent with the simpler send-message flow.
Original PR description
The full composer builds default_partner_ids by doing recipient.persona.id over allRecipients. When a recipient is not linked to a partner, the chatter input inserts it with persona: false here:…
The full composer builds default_partner_ids by doing recipient.persona.id over allRecipients. When a recipient is not linked to a partner, the chatter input inserts it with persona: false here: https://github.com/odoo/odoo/blob/28c11a068b2ecf521f8ba1da271ebee24f5345ae/addons/mail/static/src/core/web/recipients_input.js#L151 false.id is undefined, so the wizard gets a bad id and clicking the recipient tag crashes with `Invalid ids list: false`. The persona is false because /mail/partner/from_email returns no partner for unresolved emails when the user does not have base.group_partner_manager. That matches the gate _prepare_post_data already uses for the simple post flow: https://github.com/odoo/odoo/blob/28c11a068b2ecf521f8ba1da271ebee24f5345ae/addons/mail/controllers/thread.py#L153,L155 The full composer just was not handling that case. Filter out persona-less recipients from default_partner_ids so the wizard only sees real partner ids. The full composer now drops them the same way the simple post path already does. Steps to reproduce: 1. As Administrator, create a user with the Sales group only (no Contact Creation). 2. Log in as that user. 3. Open a CRM lead whose Email is set but Customer is empty. 4. Click Send message. 5. Click the expand icon to open the full composer. 6. Click the recipient tag in the To field. => Recipient shows as Unnamed and clicking it raises an RPC error. Ticket [link](https://www.odoo.com/odoo/project.task/5974081) opw-5974081 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#271422 Forward-Port-Of: odoo/odoo#251780
22 changes
Resolved issues and error corrections
This fix prevents the translation dialog in Studio report editing from crashing in databases where the default English language is not installed. It ensures the base language is always included, so users can translate report content without interruption.
Original PR description
Steps: - Install web_studio in a db initialized with en_UK only (db init --languages en_UK db_name) - Install `fr_FR` language after - Open any report in studio report editor - Edit sources - Click on "FR" logo (translate option) - Traceback `_get_base_lang()` hardcodes 'en_US' as the source language for field translations. When a database is initialized without en_US (e.g. using en_UK), that code is absent from `_get_active_langs()`. The `_get_view_translations` controller would then build a languages dict where no entry has `is_base=True`, leaving `baseLang` undefined on the frontend and crashing the XML translation dialog at: https://github.com.com/odoo/enterprise/blob/fc37919036a365babb4ab7e39c8347b0069d4206/web_studio/static/src/client_action/report_editor/report_editor_xml/translate_xml.xml#L14 After this commit, `base_lang` record is added to the list used in languages opw-6239938
This update prevents text selection from being reset when a popover, such as the color picker, is closed by clicking back inside the editor. As a result, users keep the expected cursor and selection position, making text editing more reliable and consistent across browsers.
Original PR description
When a popover is used on top of an iframe, when clicking outside the popover but inside the iframe to close the popover, the click away is detected through a blur event. The close during the click…
When a popover is used on top of an iframe, when clicking outside the popover but inside the iframe to close the popover, the click away is detected through a blur event. The close during the click away is used by some features to apply the change. Setting the text color in the editor might modify the DOM structure and therefore needs to readjust the selection to match the new DOM nodes. When confirming a color, or when applying intermediary changes this behavior is required. But when the color confirmation happens because of a click away, this selection gets on the way of the selection made by clicking away. The sequence of events is as follows: - pointerdown in iframe: sets the selection based on the click - main window blur: color gets applied, and selection gets reset - pointermove in iframe: expands selection to new position This commit avoids this unwanted reset by detecting the click away through a pointerdown on the iframe - thus making the selection adjustment before the default browser handling of the event actually sets the selection on the clicked position. Steps to reproduce: - Edit a website page - Drop a text snippet - Select a word - Set a custom color using the color area - so that the color picker remains open - Click (and hold) in the empty area after the text paragraph - Move pointer by a few pixels => The text anchor remained the one of the former selection, but the focus was set according to the pointer position Note that Firefox did not blur the main window, therefore this change actually also unifies the behavior of Chrome and Firefox. The discrepancy between Chrome and Firefox's handling of the blur event in this situation seems to be related to either prioritizing the DOM specifications or the HTML specifications - and the way the are interpreted. The DOM Standard [1] relies on UIEvents [2] which states: "A user agent MUST dispatch this event when an event target loses focus." The HTML Standard defines how to compute the focus [3] and relies on nested browsing contexts [4]. Typically, the simpler readable description for developers says: "document.hasFocus() Returns true if key events are being routed through or to document; otherwise, returns false. Roughly speaking, this corresponds to document, or a document nested inside document, being focused." [1]: https://dom.spec.whatwg.org/#biblio-uievents [2]: https://w3c.github.io/uievents/#event-type-blur [3]: https://html.spec.whatwg.org/multipage/interaction.html#focus-processing-model [4]: https://html.spec.whatwg.org/multipage/document-sequences.html#nested-browsing-contexts task-6268573 Forward-Port-Of: odoo/odoo#269742
This change prevents an error that could occur when users add properties to a record, especially when other fields depend on those properties. The system now waits for the property data to finish loading before showing it, so the action completes reliably instead of failing.
Original PR description
Description of the issue/feature this PR addresses: This error occurs when a model has properties and a `computed` field or `onchange` method depends on them. `record.update()` is asynchronous. When…
Description of the issue/feature this PR addresses: This error occurs when a model has properties and a `computed` field or `onchange` method depends on them. `record.update()` is asynchronous. When an onchange or computed field is triggered, an additional request is sent to the server, increasing the time required to complete the update. See: https://github.com/odoo/odoo/blob/727fe7412bb37c1664106625e248264d2aab6809/addons/web/static/src/model/relational_model/record.js#L1207-L1211 However, `PropertiesField` is rendered before the `update` is completed. See: https://github.com/odoo/odoo/blob/727fe7412bb37c1664106625e248264d2aab6809/addons/web/static/src/views/fields/properties/properties_field.js#L86 As a result, the property labels are not yet available and the following traceback is raised: `TypeError: Cannot read properties of undefined (reading 'getRootNode') ` After this commit, the update is awaited before rendering PropertiesField, ensuring that the property labels are available. **Steps to reproduce:** 1. Install the example module. [project_task_property.zip](https://github.com/user-attachments/files/29138424/project_task_property.zip) 2. Open or create a project task. 3. From the Action menu, click `Add Properties`. The error is raised. <img width="1520" height="956" alt="image" src="https://github.com/user-attachments/assets/a3051ddf-5af0-4d3a-8ff7-c3fb4f7a69d2" /> TT63331 @Tecnativa @pedrobaeza --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272571 Forward-Port-Of: odoo/odoo#271082
This change makes the “undo reconciliation” action bypass extra validation checks so the reconciliation line can be reset correctly. It helps users reverse a reconciliation without getting blocked by checks that are not needed in this flow.
Original PR description
When undoing the reconciliation from the "undo reconciliation" button. We want to bypass all the checks to be able to reset the line. no task id
This fix prevents the website editor from switching to mobile view when users zoom their browser to 150% and click Edit. It keeps the sidebar within the expected width so the editing experience remains consistent on desktop screens.
Original PR description
Steps to reproduce: - Open a website page on Chrome/Firefox. - Set the browser zoom to 150%. - Click Edit. => The website preview switches to mobile view. Before this commit, [1] changed the sidebar to `content-box`, so the `border-start` class added one pixel to `$o-we-sidebar-width`. The sidebar then reached 289px instead of 288px, which made the preview fall below the desktop breakpoint at 150% zoom. After this commit, the sidebar width subtracts the Bootstrap border width so its total width stays at 288px and edit mode keeps the desktop view. [1]: https://github.com/odoo/odoo/commit/181b870c3c6cf28c3eac59a16c36b94ae11ae927 task-6251151 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272074
This fix ensures that the delivery and billing addresses chosen during checkout are kept when the cart is refreshed in the background. It prevents the order from silently reverting to a default company address, avoiding confusion and extra corrections after payment.
Original PR description
Steps to reproduce: =================== 1. Add several delivery addresses & billing addresses 2. Add a product to the cart and go to checkout. 3. Select a specific delivery address and a different…
Steps to reproduce: =================== 1. Add several delivery addresses & billing addresses 2. Add a product to the cart and go to checkout. 3. Select a specific delivery address and a different invoice address. 4. Pay and click "Skip" immediately on that page. 5. Open the resulting sales order. => The delivery address is reset to the company's first delivery child instead of the one selected during checkout. Root cause: =========== `partner_shipping_id` and `partner_invoice_id` are stored computed fields (compute + store + readonly=False) that depend on `partner_id`. Any write that includes `partner_id`, even writing the same value, retriggers the compute and overwrites a manually selected address with the result of `partner_id.address_get()`. `_get_and_cache_current_cart` resurrects the customer's draft cart when it is no longer referenced in the session and re-runs `_update_address(partner, ['partner_id'])` on it to refresh the pricelist and fiscal position. Clicking "Skip" runs `sale_reset()`, which clears the session cart key while the order is still draft, so the next cart access takes that abandoned-cart branch and the redundant `partner_id` write discards the selected delivery/invoice address. Waiting a few seconds lets the order reach the 'sale' state first, so the draft search no longer matches and the address is kept, which is why the issue is timing dependent. Fix: ==== In `_update_address`, when partner_id is written, keep the delivery and invoice addresses already set on the cart if they still belong to the new partner's company (same `commercial_partner_id`) by writing them in the same `write()` so the recompute does not override them. Addresses that do not belong to the new partner are still recomputed to the partner's defaults. opw-6267188 Forward-Port-Of: odoo/odoo#270300
This change prevents portal users from hitting an access error when viewing Knowledge articles that contain item lists with author or last-edited information. It ensures shared articles load correctly for external users instead of failing or hiding content.
Original PR description
Problem: Since saas-19.2 (99f38be260c3c5523306e4ffcb4cf18436d40568), portal users crash when opening a Knowledge article containing items with "Created by" or "Last edited by" columns. Cause: Portal…
Problem: Since saas-19.2 (99f38be260c3c5523306e4ffcb4cf18436d40568), portal users crash when opening a Knowledge article containing items with "Created by" or "Last edited by" columns. Cause: Portal users are restricted to their own res.users record. Reading create_uid and last_edition_uid of internal users raises an AccessError. This was not raised in 19.0. Specifically, the `many2one_avatar_user` field widget defines `write_date` in `relatedFields`, which forces the RPC to read the `write_date` field of the target user. Since portal users cannot read other users' records, it raises an AccessError. Solution: Only include `write_date` in the field widget's `relatedFields` if the current user is an internal user. For portal users, `relatedFields` will be empty, avoiding the AccessError. Steps to reproduce: 1. Create a Knowledge article. 2. Add an "Item list" element. 3. Add some items to the list. 4. Share the article with a portal user. 5. Open the article as the portal user. 6. Observe that only the list header is visible and the items are not displayed. opw-6199714 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update prevents font and style changes from being applied outside the editable area when users edit website content. It ensures formatting is saved correctly in website builder and translation editing, avoiding cases where changes were previously lost after saving.
Original PR description
Commit e0cf601ab6fc1d7cb007fcb16ee4bb6097ba6fdd added a condition to apply format on the parent of fully selected unsplittable nodes. But this could lead to format applied outside of `contenteditable=true` nodes in some cases of website builder. This commit adds a condition to only apply format outside the unsplittable if it is not the node with `contenteditable`. Steps to reproduce: - In event page, open website builder - Double click on "Starts" in the sidebar inside the event page - Change font size - Save - Bug: the changed font size is not save (a span with the font class was created outside the savable element) ### - In translate mode - Select a whole translation span - Change font size - Save - Bug: the changed font size is not save (the font class was added on the translation span) task-6308352 Forward-Port-Of: odoo/odoo#271888 Forward-Port-Of: odoo/odoo#270401
Neutralized databases will now use the test VAT validation service instead of accidentally contacting the live production service. This reduces the risk of sending test data to real external systems while keeping custom service settings unchanged.
Original PR description
Neutralized databases don't always have demo data, so they talk to production VIES endpoint. With this change, the production endpoint is replaced with the test one. If some database has a custom endpoint, nothing is done. @moduon MT-15142 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272036
This update corrects how half-day time off is calculated for French companies, especially for part-time employees whose schedule does not match the company schedule. It ensures leave durations are shown accurately and prevents certain leaves from being extended incorrectly when they end in the morning.
Original PR description
**Steps to reproduce** - Use a french company with `l10n_fr_hr_holidays` installed - Change the duration type of the time off type set as the "Company Paid Time Off Type" in the French Time Off…
**Steps to reproduce** - Use a french company with `l10n_fr_hr_holidays` installed - Change the duration type of the time off type set as the "Company Paid Time Off Type" in the French Time Off Localization settings to "Half-Day" - Company Working Schedule: - Attendance on a day from 10 to 19, Day Period: Full Day - Part-time employee Working Schedule: - Attendance on the same day from 11 to 12, Day Period: Morning - Attendance on the same day from 13 to 19, Day Period: Afternoon - Create a full day time off for the part time employee on that day, using the time off type set as the "Company Paid Time Off Type" (start am, end pm) -> Excepted: time off duration is 1 day -> Actual: time off duration is 0.89 day **Change** Now that `request_unit_half` of a leave is a simple related to the `request_unit` of the leave type, it becomes important to not rely on a call to `_get_durations` using the company's calendar to compute the leave's duration, as it may not be fully accurate when the company's working hours and employee's working hours are not aligned. Continuation of 05e71eb206eb02a8d15708e6fb532a732a767d6d `_get_fr_date_from_to` is also adapted to take into account multi-day leaves ending in the morning while the employee works in the afternoon (in which case it should not be extended in case the employee doesn't work the next day). opw-6000011 Forward-Port-Of: odoo/odoo#269890 Forward-Port-Of: odoo/odoo#253059
The Mexican e-invoicing document now shows the correct payment amount and exchange rate when a payment is made in a foreign currency. This prevents incorrect payment information from appearing after updating payments on an invoice, helping ensure the CFDI matches the real transaction.
Original PR description
The rate and payment amount shown on the CFDI document generated after updating payments was wrong when the payment was made in a foreign currency. Steps to reproduce: ------------------- * Create a journal that use USD as currency and set the rate to 20 MXN for 1 USD * Create an invoice in MXN and make sure it is set to PPD * Add any product to the invoice for 300$ and post it * Send the invoice to CFDI (a first document should be generated) * Create a payment of 15 USD in the new journal and reconcile it with the invoice * Go back to the invoice and click on "Update payments" to generate the second CFDI document > Observation: The payment document shows an amount of 300 USD with a rate of 1 instead of 15 USD with a rate of 20. Why the fix: ------------ We make sure to use the amount from the statement line when there is one. opw-5974519 Forward-Port-Of: odoo/enterprise#121410 Forward-Port-Of: odoo/enterprise#115779
The message composer now ignores email-only recipients that are not linked to a contact record when opening the full composer. This prevents a crash when users click those recipient tags and ensures the message form behaves consistently with the simpler send flow.
Original PR description
The full composer builds default_partner_ids by doing recipient.persona.id over allRecipients. When a recipient is not linked to a partner, the chatter input inserts it with persona: false here:…
The full composer builds default_partner_ids by doing recipient.persona.id over allRecipients. When a recipient is not linked to a partner, the chatter input inserts it with persona: false here: https://github.com/odoo/odoo/blob/28c11a068b2ecf521f8ba1da271ebee24f5345ae/addons/mail/static/src/core/web/recipients_input.js#L151 false.id is undefined, so the wizard gets a bad id and clicking the recipient tag crashes with `Invalid ids list: false`. The persona is false because /mail/partner/from_email returns no partner for unresolved emails when the user does not have base.group_partner_manager. That matches the gate _prepare_post_data already uses for the simple post flow: https://github.com/odoo/odoo/blob/28c11a068b2ecf521f8ba1da271ebee24f5345ae/addons/mail/controllers/thread.py#L153,L155 The full composer just was not handling that case. Filter out persona-less recipients from default_partner_ids so the wizard only sees real partner ids. The full composer now drops them the same way the simple post path already does. Steps to reproduce: 1. As Administrator, create a user with the Sales group only (no Contact Creation). 2. Log in as that user. 3. Open a CRM lead whose Email is set but Customer is empty. 4. Click Send message. 5. Click the expand icon to open the full composer. 6. Click the recipient tag in the To field. => Recipient shows as Unnamed and clicking it raises an RPC error. Ticket [link](https://www.odoo.com/odoo/project.task/5974081) opw-5974081 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#271422 Forward-Port-Of: odoo/odoo#251780
This fix ensures that Marketing Automation templates such as Double Opt-in keep their linked records, like the confirmed contacts list and server action, after an app upgrade. It prevents these important template-generated items from disappearing, so campaigns continue to work as expected.
Original PR description
When a user chooses the "Double Opt-in" template (or others using `_create_records_with_xml_ids`), the system creates records like the "Confirmed contacts" list and a server action. It also adds an…
When a user chooses the "Double Opt-in" template (or others using `_create_records_with_xml_ids`), the system creates records like the "Confirmed contacts" list and a server action. It also adds an `ir.model.data` entry for them under the module, but sets `noupdate=False`. The problem is that during a module upgrade, Odoo's cleanup process (`_process_end`) deletes any record marked `noupdate=False` if it wasn't "reloaded" from a file. Since these records are created manually in the code and aren't in any XML file, they never get reloaded, so Odoo just deletes them and the records they point to. By setting `noupdate=True` when these rows are created, we tell the system to leave them alone during upgrades. Since these only exist at runtime and don't have a data file, we don't need to worry about them being updated later anyway. The helper was added in https://github.com/odoo/enterprise/commit/fd98660e352512e0b74d91958707687978154833. Steps to reproduce: 1. Install Marketing Automation 2. Go to Campaigns > New and pick the "Double Opt-in" template, then save 3. Confirm the "Confirmed contacts" mailing list and server action exist 4. Go to Apps and Upgrade Marketing Automation 5. Try to find the mailing list and server action again => They disappear after the upgrade. Ticket [link](https://www.odoo.com/odoo/project.task/6140191) opw-6140191 Forward-Port-Of: odoo/enterprise#115444
This fix prevents the restaurant setup from creating the same Belgian “Take out” tax and fiscal position more than once when a branch uses the POS scenario. As a result, branches can be created successfully and reuse the existing tax settings instead of failing during setup.
Original PR description
When creating a restaurant with the POS scenario on a company with the Belgian accounting package, we automatically create a new 'Take out' fiscal position and a 'Take out' 6% tax. But when creating a branch for the company and using the same scenario, it will duplicate the 'Take out' fiscal position, and then fail to create because it will try to create a duplicate Tax with the same name and then odoo will complain. After the fix the scenario checks whether the fiscal position and tax already exist on the company, and if they do, it doesn't recreate them. This should let branches use the scenario and then they can use the already existing taxes and FPs. Task [link](https://www.odoo.com/odoo/project.task/5502948) Task-5502948 Forward-Port-Of: odoo/odoo#245848
This update corrects how discounts are calculated in the point of sale sales details report when refunded items are included. It ensures refunded discounted lines no longer distort the reported discount total, making the figures more accurate for business review.
Original PR description
The sales details report computes a line discount as `original_price - price_subtotal_incl`. On a refund line the quantity is negative, so `original_price` is negative, while `price_subtotal_incl` is stored positive. Subtracting the two then inflates the discount instead of cancelling it, understating "discount_amount" by `2 * price_subtotal_incl` for every refunded discounted line. opw-6281752 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270592
We fixed an issue in Planning where searching by skill could hide employees who had the skill but no scheduled slots in the selected time period. Now those employees appear correctly in search results, making skill-based staffing searches more reliable.
Original PR description
Issue: ---------------------------------------- Doing a search on skills, the employees with the skill but no slot in the time frame aren't shown. Steps to reproduce:…
Issue:
----------------------------------------
Doing a search on skills, the employees with the skill but no slot in the time frame aren't shown.
Steps to reproduce:
----------------------------------------
- Install `planning_hr_skills`
- Make sure an employee has the skill "English" and no slots
- Open Planning and type "English" in the search bar, click to search on Skills
- The employee does not show up
Cause:
----------------------------------------
Using the search view on skills, a filter on `resource_ids` is in the domain with a `OR`:
https://github.com/odoo/enterprise/blob/5d6d16aae43ffda1532cfa4b06fdcddd7e5b0fcc/planning_hr_skills/views/planning_slot_views.xml#L9
Then a new filter on `resource_ids` is added [here](https://github.com/odoo/enterprise/blob/5d6d16aae43ffda1532cfa4b06fdcddd7e5b0fcc/planning_hr_skills/models/planning_slot.py#L14-L43) to do the search on the skill names: `[('resource_ids', 'in', matching_resource_ids)]`.
The domain is then something like this:
`['&', ('resource_ids', 'in', matching_resource_ids), '|', ('resource_ids', '=', False), ('employee_skill_ids', 'ilike', 'English')]`.
Since fbf8b2ac67c71ca0abfc75df543069696bd2d29b the resulting domain passes through `filter_map_domain()`. `filter_map_domain()` will only keep the leaves on `resource_ids` and the default `AND` will be used between them resulting in:
`['&', ('resource_ids', 'in', matching_resource_ids), ('resource_ids', '=', False)]`
which fetches no resources.
So `_group_expand_resource_ids()` doesn't expand.
Solution:
----------------------------------------
Instead of adding the new leaf to retrieve the resources with the right skills, we replace the leaf on `employee_skill_ids`. This ensures the `OR` operations are kept by `filter_map_domain()`.
opw-6296755This fix preserves more precision when calculating overtime durations, so very small time differences are not lost before pay is computed. It helps ensure overtime amounts are calculated more accurately and consistently.
Original PR description
Overtime duration computed as fractional hours was rounded to 3 decimal places before being stored on the overtime line. Since 1 decimal hour = 3600 seconds, this gives only 3.6 seconds of precision and the rounding can go in the wrong direction due to floating-point representation. The fix consists in replacing the duration rounding to 4 decimals when building overtime work entries so stored durations keep sub-second precision needed for money computation. task-6212231 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#268889
The Planning / Timesheets Analysis report now correctly counts planning slots tied to duration-based calendars. This fixes missing entries in reports, so managers get more accurate workload and timesheet analysis.
Original PR description
Issue: ---------------------------------------- The Planning / Timesheets Analysis report doesn't include most of the planning slots if they have a calendar based on duration. Steps to reproduce:…
Issue: ---------------------------------------- The Planning / Timesheets Analysis report doesn't include most of the planning slots if they have a calendar based on duration. Steps to reproduce: ---------------------------------------- - Have a 1 day planning slot (8h-17h) for an employee with a calendar based on duration. - Planning > Reporting > Planning / Timesheets Analysis - The slot is not included in the report Cause: ---------------------------------------- The update of how calendars work in saas-19.2 included the `duration_based` option but the query was not adapted. It still checks if the slot start time is lower than the calendar end time on that day: ```sql F.start_datetime < (d.date::date + (A.hour_to || ' hour')::interval) AND F.end_datetime > (d.date::date + (A.hour_from || ' hour')::interval) ``` But for duration-based calendars `hour_from` and `hour_to` are stored as `0.0`. So `F.start_datetime < (d.date::date + (A.hour_to || ' hour')::interval)` evaluates to False. Solution: ---------------------------------------- If the calendar has `duration_based` to True we only check the date. opw-6217643
This change improves how overtime hours are stored so they keep enough precision for payroll calculations. It prevents small rounding errors from affecting the amount paid to employees.
Original PR description
Overtime duration computed as fractional hours was rounded to 3 decimal places before being stored on the overtime line. Since 1 decimal hour = 3600 seconds, this gives only 3.6 seconds of precision and the rounding can go in the wrong direction due to floating-point representation. The fix consists in replacing the duration rounding to 4 decimals when building overtime work entries so stored durations keep sub-second precision needed for money computation. task-6212231 Forward-Port-Of: odoo/enterprise#119721
This fix prevents the system scheduler from automatically reassigning subcontracting receipt moves. As a result, when users manually adjust the receipt quantity, it will stay as entered instead of being reset later, avoiding incorrect receipt quantities in subcontracting workflows.
Original PR description
**Issue** Quantity on subcontracted receipts could be overridden after running the scheduler. **Steps to reproduce** - Create a subcontracting product - Create a PO for 10 units of that product - Go…
**Issue** Quantity on subcontracted receipts could be overridden after running the scheduler. **Steps to reproduce** - Create a subcontracting product - Create a PO for 10 units of that product - Go to receipt and open Subcontracting Productions' - Change the quantity to 5 - Enable debug mode. - Run Inventory/Operations/Procurement: run scheduler - Return to the receipt -> The receipt quantity is reset to 10 instead of remaining at 5. **Cause** Since the refactor introduced in commit: https://github.com/odoo/odoo/commit/fc66e2d4eb638f1486e69cd5920f02c787055da1, subcontracting receipt moves are no longer automatically picked when the production quantity is modified. In particular, this test case protects that behavior: https://github.com/odoo/odoo/blob/f7fb0a941d6bac39b7057db36f57be079559912c/addons/mrp_subcontracting/tests/test_subcontracting.py#L1629-L1631 When the scheduler runs: https://github.com/odoo/odoo/blob/f7fb0a941d6bac39b7057db36f57be079559912c/addons/stock/models/stock_rule.py#L731 it computes the moves to assign: https://github.com/odoo/odoo/blob/f7fb0a941d6bac39b7057db36f57be079559912c/addons/stock/models/stock_rule.py#L706-L710 However, subcontracting moves are still included in the assignment domain: https://github.com/odoo/odoo/blob/f7fb0a941d6bac39b7057db36f57be079559912c/addons/stock/models/stock_rule.py#L680-L688 https://github.com/odoo/odoo/blob/f7fb0a941d6bac39b7057db36f57be079559912c/addons/mrp/models/stock_rule.py#L127-L129 As a result, they are reassigned if they are not already picked: https://github.com/odoo/odoo/blob/f7fb0a941d6bac39b7057db36f57be079559912c/addons/stock/models/stock_move.py#L1931-L1937 opw-6229668 Forward-Port-Of: odoo/odoo#270837
This update stops command shortcuts and auto-formatting from working inside code blocks. It helps users keep code text unchanged and avoids errors when editing code content.
Original PR description
### Steps to reproduce: - Go to ToDo. - Create a code block using `/code`. - Place the cursor inside the code block. - Type `/table` and select the table command. - A traceback occurs. ### Purpose of this PR: - Commands and markdown shorthands should not be available inside code blocks. However, typing `/` inside a `<pre>` opened the command palette, allowing structural commands such as `/table` to be executed and causing a traceback. Similarly, markdown shorthands such as `* ` and `1.` were still active, unexpectedly transforming code content into lists. ### This PR fixes the issue by: - Disabling the command palette when the cursor is inside a `<pre>` element. - Disabling markdown shorthands inside `<pre>` elements by registering an `are_shorthand_available_predicates` predicate. task-6292231 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#271796 Forward-Port-Of: odoo/odoo#269430
This fix ensures that when users open manufacturing order statistics, they are shown the correct set of manufacturing orders. It prevents confusion caused by seeing the wrong records, making it easier to review production activity accurately.
Original PR description
* Following https://github.com/odoo/odoo/pull/261438 (forward-ported to 19.0 in #265323) we also need to show correct MOs when view from statsbutton Forward-Port-Of: odoo/odoo#265195 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#269201
24 changes
Resolved issues and error corrections
This update restores the company address on Colombian invoices when using the Folder or Wave layouts. It also preserves the invoice title layout so PDF exports remain readable and complete.
Original PR description
Issue: On Wave and Folder layout, the address of the company doesn't appear on invoices. Steps to reproduce: - In a Colombian company, - Set company layout to Folder - Add a long tag line, - Create an invoice, - Send it to DIAN - Export to PDF Current behavior: - Company address is missing in the header Cause: Tag line + logo and address take 100% of the display width. However, loca add a QR code on the left, so it takes QR Code + 100% width. Therefore, address was out of the PDF. Moreover, for Folder layout, some resizing was done and as soon as there was a tag_line, the `rem` was downsized, allowing the invoice title: "Factura Electrónica de Venta SETP/*\*\*/\*\*\*\*" to be displayed entirely. The fix of the previous issue stopped the resizing, then the invoice title got overridden by the QR Code (same as without tag_line before this fix). opw-6239030
Imported bill quantities are now kept at the correct precision when matching them to purchase orders. This prevents small rounding differences that could make invoiced quantities off by a cent in some cases.
Original PR description
When importing an XML bill and linkin git to a purcahse order, the invoiced quantity may be computed incorrectly, due to a decimal precision mismatch. Steps to reproduce: - Import an XML bill having a line with quantity 1800.0 - Link to a purchase order with the same line Issue: The invoiced quantity will be computed with 1 cent difference (1800.01) Analysis: Because the system forced a decimal precision of 13 for 'Product Unit of Measure', quantity is imported as 1800.0000000000016. Later, when computing the invoiced quantity, the system round the quantity using 'UP' strategy, rounding the amount to 1800.01 opw-6194824 Forward-Port-Of: odoo/odoo#270916 Forward-Port-Of: odoo/odoo#266283
The website editor sidebar was adjusted so it no longer becomes one pixel wider than intended at certain browser zoom levels. This prevents the page preview from switching to mobile view when users click Edit at 150% zoom, keeping the editing experience consistent.
Original PR description
Steps to reproduce: - Open a website page on Chrome/Firefox. - Set the browser zoom to 150%. - Click Edit. => The website preview switches to mobile view. Before this commit, [1] changed the sidebar to `content-box`, so the `border-start` class added one pixel to `$o-we-sidebar-width`. The sidebar then reached 289px instead of 288px, which made the preview fall below the desktop breakpoint at 150% zoom. After this commit, the sidebar width subtracts the Bootstrap border width so its total width stays at 288px and edit mode keeps the desktop view. [1]: https://github.com/odoo/odoo/commit/181b870c3c6cf28c3eac59a16c36b94ae11ae927 task-6251151 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272074
This change prevents the text selection from being reset unexpectedly when a user closes the color picker by clicking elsewhere in the editor. It makes text editing behave more reliably and consistently across browsers, especially when applying text colors in website content.
Original PR description
When a popover is used on top of an iframe, when clicking outside the popover but inside the iframe to close the popover, the click away is detected through a blur event. The close during the click…
When a popover is used on top of an iframe, when clicking outside the popover but inside the iframe to close the popover, the click away is detected through a blur event. The close during the click away is used by some features to apply the change. Setting the text color in the editor might modify the DOM structure and therefore needs to readjust the selection to match the new DOM nodes. When confirming a color, or when applying intermediary changes this behavior is required. But when the color confirmation happens because of a click away, this selection gets on the way of the selection made by clicking away. The sequence of events is as follows: - pointerdown in iframe: sets the selection based on the click - main window blur: color gets applied, and selection gets reset - pointermove in iframe: expands selection to new position This commit avoids this unwanted reset by detecting the click away through a pointerdown on the iframe - thus making the selection adjustment before the default browser handling of the event actually sets the selection on the clicked position. Steps to reproduce: - Edit a website page - Drop a text snippet - Select a word - Set a custom color using the color area - so that the color picker remains open - Click (and hold) in the empty area after the text paragraph - Move pointer by a few pixels => The text anchor remained the one of the former selection, but the focus was set according to the pointer position Note that Firefox did not blur the main window, therefore this change actually also unifies the behavior of Chrome and Firefox. The discrepancy between Chrome and Firefox's handling of the blur event in this situation seems to be related to either prioritizing the DOM specifications or the HTML specifications - and the way the are interpreted. The DOM Standard [1] relies on UIEvents [2] which states: "A user agent MUST dispatch this event when an event target loses focus." The HTML Standard defines how to compute the focus [3] and relies on nested browsing contexts [4]. Typically, the simpler readable description for developers says: "document.hasFocus() Returns true if key events are being routed through or to document; otherwise, returns false. Roughly speaking, this corresponds to document, or a document nested inside document, being focused." [1]: https://dom.spec.whatwg.org/#biblio-uievents [2]: https://w3c.github.io/uievents/#event-type-blur [3]: https://html.spec.whatwg.org/multipage/interaction.html#focus-processing-model [4]: https://html.spec.whatwg.org/multipage/document-sequences.html#nested-browsing-contexts task-6268573 Forward-Port-Of: odoo/odoo#269742
Fixed an issue where the delivery and billing addresses chosen during checkout could be replaced later if the cart was refreshed in the background. This makes sure customers keep the address they selected, preventing incorrect order details when they finish buying quickly.
Original PR description
Steps to reproduce: =================== 1. Add several delivery addresses & billing addresses 2. Add a product to the cart and go to checkout. 3. Select a specific delivery address and a different…
Steps to reproduce: =================== 1. Add several delivery addresses & billing addresses 2. Add a product to the cart and go to checkout. 3. Select a specific delivery address and a different invoice address. 4. Pay and click "Skip" immediately on that page. 5. Open the resulting sales order. => The delivery address is reset to the company's first delivery child instead of the one selected during checkout. Root cause: =========== `partner_shipping_id` and `partner_invoice_id` are stored computed fields (compute + store + readonly=False) that depend on `partner_id`. Any write that includes `partner_id`, even writing the same value, retriggers the compute and overwrites a manually selected address with the result of `partner_id.address_get()`. `_get_and_cache_current_cart` resurrects the customer's draft cart when it is no longer referenced in the session and re-runs `_update_address(partner, ['partner_id'])` on it to refresh the pricelist and fiscal position. Clicking "Skip" runs `sale_reset()`, which clears the session cart key while the order is still draft, so the next cart access takes that abandoned-cart branch and the redundant `partner_id` write discards the selected delivery/invoice address. Waiting a few seconds lets the order reach the 'sale' state first, so the draft search no longer matches and the address is kept, which is why the issue is timing dependent. Fix: ==== In `_update_address`, when partner_id is written, keep the delivery and invoice addresses already set on the cart if they still belong to the new partner's company (same `commercial_partner_id`) by writing them in the same `write()` so the recompute does not override them. Addresses that do not belong to the new partner are still recomputed to the partner's defaults. opw-6267188 Forward-Port-Of: odoo/odoo#270300
This update corrects how one Luxembourg equity account is classified and simplifies the calculation used for the “Profit or loss brought forward” line in the balance sheet. It helps ensure the report shows the right figures more reliably for year-end accounting.
Original PR description
This commit addresses the account type for the current year earnings and simplifies the calculation for the "Result brought forward" line in the Luxembourg balance sheet reports.
Modifications:
* Changed the account type of account 142 ("Result for the financial year") from `equity_unaffected` to standard `equity`.
* Simplified the formula for the Balance Sheet line "Profit or loss brought forward" (codes `LU_BS_319` and `LU_BSABR_319`).
* The new formula simply targets the `14` accounts while explicitly excluding `142`.
Enterprise PR: odoo/enterprise#121891
Ticket [link](https://www.odoo.com/odoo/project.task/6059571)
opw-6059571
Forward-Port-Of: odoo/odoo#272362This fixes an issue where changing lot numbers on tracked products could unexpectedly alter quantities or drop reservations in stock operations. The update ensures lots stay linked correctly, quantities remain consistent, and reservations are adjusted in a predictable way when users edit delivery or receipt lines.
Original PR description
### Issue: Editing the `lot_ids` of a move of a `lot` tracked product from the picking form view leads to wildly unexpected results. This happens only with tracking by `lot` not by serial. ###…
### Issue:
Editing the `lot_ids` of a move of a `lot` tracked product from the picking form view leads to wildly unexpected results. This happens only with tracking by `lot` not by serial.
### Concrete Issue 1:
1. In the settings Enable "Lots and serial numbers", "Storage Locations"
2. Create a storable product P tracked by lots
3. Create and confirm a delivery for 5 units of P
4. Set the quantity of the move to 5 from the Form picking view > save
5. Create and set 2 lots: LOT1, LOT2 on the serial numbers field
6. Save
#### > The quantity of the move has been updated to 2, only the first lot is set and it has been for this quantity of 2
### Concrete Issues 2 and 3:
1. In the settings Enable "Lots and serial numbers", "Storage Locations"
2. Create a storable product P tracked by lots
3. Update the onhand quantity of P:
- 1 units of LOT001 in Shelf1
- 2 units of LOT001 in Shelf2
- 2 units of LOT002 in Stock
4. Create and confirm a delivery for 5 units of P
5. Remove LOT002 from the Serial numbers in the Form picking view > save
#### > The quantity of the move is updated to 1 (only the 1 unit of LOT001 from Shelf2 is kept)
5'. Remove LOT002 and put it back
#### > The quantity is updated from 5 to 2 if you save, only LOT001 is kept.
### Cause of the issue:
The `_onchange_lot_ids` and `_set_lot_ids` methods have been tailored to work appropriately only with `serial` tracking, updating the quantities considering a 1 to 1 quantity, lot matching:
https://github.com/odoo/odoo/blob/c9715982134220aa8fa525d0cf6a8d47eaeb6ed6/addons/stock/models/stock_move.py#L623-L645
However, for lot tracked product the situation is much more subtle to handle.
### Behavior after the fix:
Editing the `lot_ids` on tracked products should adapt the reservation following these rules:
- Existing move lines with a valid lot or lot name should be kept unchanged.
- Removing a lot should delete its related move lines and adjust the move quantity accordingly.
- Each newly assigned lot must be linked to at least one move line of the move.
If the move is expected to bypass reservation (e.g. receipts, final move of a production,...):
- Assignment should be performed, in priority, on an existing free move line.
- If no suitable free move line exists, a new move line should be created with the largest possible quantity that does not cause the total assigned quantity to exceed the move demand.
- If such a quantity cannot be assigned, the new move line should be created with a quantity of 1 in the product.uom_id.
If the move is expected to be reserved (e.g. internal transfer, deliveries,...):
- Each new lot should be assigned from an existing quants with the maximum available quantity to satisfy at best the remaining demand.
- If no available quantity can be assigned from existing quants, the lot should be assigned a minimum quantity of 1 in product.uom_id.
### Additional note on the fix:
Since move that do bypass reservation use a different detailed operation view relying on lot_names form move line rather than lot_id from existing quants it is important to set both the `lot_name` as well as the `lot_id` on move lines for the changes to be visible in the detailed operations view.
### Note:
The current fix populated records on which the `label_production_view_pdf` report was tested (by the test_report) highlighting a template error:
https://github.com/odoo/odoo/blob/a73428187112b3948a11810abae2a3c82c9c7bcd/addons/mrp/report/mrp_production_templates.xml#L187
The value provided to the t-field being something else than a field but rather an or close between two fields.
opw-6173914
opw-5881661
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#263037This change prevents text styling from being applied outside the editable area in the website editor. As a result, formatting changes such as font size are now saved correctly in website builder and translation views.
Original PR description
Commit e0cf601ab6fc1d7cb007fcb16ee4bb6097ba6fdd added a condition to apply format on the parent of fully selected unsplittable nodes. But this could lead to format applied outside of `contenteditable=true` nodes in some cases of website builder. This commit adds a condition to only apply format outside the unsplittable if it is not the node with `contenteditable`. Steps to reproduce: - In event page, open website builder - Double click on "Starts" in the sidebar inside the event page - Change font size - Save - Bug: the changed font size is not save (a span with the font class was created outside the savable element) ### - In translate mode - Select a whole translation span - Change font size - Save - Bug: the changed font size is not save (the font class was added on the translation span) task-6308352 Forward-Port-Of: odoo/odoo#271888 Forward-Port-Of: odoo/odoo#270401
This update fixes how a year-end earnings account is classified in Luxembourg accounting, so it is treated like a standard equity account. It also simplifies the balance sheet formula for carried-forward profit or loss, which helps ensure the report shows the correct amount more reliably.
Original PR description
This commit addresses the account type for the current year earnings and simplifies the calculation for the "Result brought forward" line in the Luxembourg balance sheet reports.
Modifications:
* Changed the account type of account 142 ("Result for the financial year") from `equity_unaffected` to standard `equity`.
* Simplified the formula for the Balance Sheet line "Profit or loss brought forward" (codes `LU_BS_319` and `LU_BSABR_319`).
* The new formula simply targets the `14` accounts while explicitly excluding `142`.
Community PR: odoo/odoo#272362
Ticket [link](https://www.odoo.com/odoo/project.task/6059571)
opw-6059571
Forward-Port-Of: odoo/enterprise#121891Scheduling a document activity from several records at once could fail with an error. The system now correctly checks each selected record, so users can schedule activities in bulk without interruption.
Original PR description
### Steps to reproduce: - Install 'Accounting' - From 'Accounting' > 'Journal Entries', Select multiple records from the list view. - Try to schedule a 'Document' activity. > Traceback: `KeyError:…
### Steps to reproduce:
- Install 'Accounting'
- From 'Accounting' > 'Journal Entries', Select multiple records from the list view.
- Try to schedule a 'Document' activity.
> Traceback: `KeyError: account.move(X, X)`
### Cause of Issue:
When scheduling an activity for multiple records, `_onchange_activity_user_id` calls `_mail_get_operation_for_mail_message_operation('create')` on the entire batch of records. This method returns a dictionary mapping *individual* records to their required operations. However, then it was attempted to retrieve the operation values using the whole thread (which now contains 2 records) as a key causing a `KeyError` since only individual records existed as keys. https://github.com/odoo/odoo/blob/337efb069f6cf2cb9478a970f075fd139c1e8e0a/addons/mail/wizard/mail_activity_schedule.py#L463
### Fix:
For each required operation type, filter the records requiring it from the `thread` recordset and evaluate `check_access` on those specific records, making access checking efficient.
opw-6290303
Forward-Port-Of: odoo/odoo#270277When an email from a mass mailing campaign bounces, the contact’s bounce count is now updated reliably. This fixes cases where bounce notifications came from a different sender address, which previously caused some bounces to be missed.
Original PR description
Previously, when a mailing campaign sent out an email to a mailing.contact, and that email bounced, the bounce would not increment the contact's bounce count. This commit makes it so that the bounce count is correctly updated when a mailing campaign sends an email that bounces. task-4893615 Forward-Port-Of: odoo/odoo#226362
This change stops private tasks from being used as parent tasks in projects. It helps avoid accidental exposure or linking of tasks that should remain restricted, keeping task hierarchies more consistent and secure.
Original PR description
In this commit, we ensure that private tasks can never be selected as parent tasks. task-5119141 Forward-Port-Of: odoo/odoo#272263 Forward-Port-Of: odoo/odoo#270795
Swiss sales invoices now generate a payment reference even when the customer is outside Switzerland or Liechtenstein, as long as a QR-IBAN is configured. This ensures the invoice PDF continues to display payment communication and bank details, making it easier for customers to pay correctly.
Original PR description
Issue: When an invoice (sales journal) uses "Switzerland" localization and the invoice is issued to a customer outside Switzerland/Liechtenstein, no payment reference is generated. This causes the…
Issue: When an invoice (sales journal) uses "Switzerland" localization and the invoice is issued to a customer outside Switzerland/Liechtenstein, no payment reference is generated. This causes the invoice PDF to hide payment communication and bank account details. Other localizations like Belgian companies, uses Belgian references, the reference is always generated regardless of customer country. Steps to reproduce: - Configure a Swiss company with a QR IBAN bank account - Set the sales journal Communication Standard to Switzerland - Create and confirm an invoice for a non swiss customer (US, BE) - Observe in the pdf and in the other info tab -> no payment reference or payment details Cause: `get_l10n_ch_qrr_number()` was using on `l10n_ch_is_qr_valid()`, which conflicts QR-bill printability (partner country, currency) with payment reference generation. When the customer is outside CH/LI, `l10n_ch_is_qr_valid()` is False and no QRR reference is generated. Solution: Decouple QRR reference generation from QR bill printability. The Swiss communication standard now generates a QRR format reference when a QR-IBAN is configured, regardless of customer's country or currency. opw-6222417 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#266861
This fix stops the Belgian POS restaurant setup from creating the same “Take out” tax and fiscal position more than once when a branch is created. It allows branches to reuse the existing settings instead of failing during setup, making restaurant rollout smoother for companies with multiple locations.
Original PR description
When creating a restaurant with the POS scenario on a company with the Belgian accounting package, we automatically create a new 'Take out' fiscal position and a 'Take out' 6% tax. But when creating a branch for the company and using the same scenario, it will duplicate the 'Take out' fiscal position, and then fail to create because it will try to create a duplicate Tax with the same name and then odoo will complain. After the fix the scenario checks whether the fiscal position and tax already exist on the company, and if they do, it doesn't recreate them. This should let branches use the scenario and then they can use the already existing taxes and FPs. Task [link](https://www.odoo.com/odoo/project.task/5502948) Task-5502948 Forward-Port-Of: odoo/odoo#245848
Images placed into device-shaped frames now keep the frame’s proportions instead of being forced into a square crop first. This makes tall or wide images fit more naturally and avoids unnecessary trimming, improving the final visual result for users.
Original PR description
Scenario: - insert image with ~ 1:2 ratio (height bigger than width) - set shape "iPhone `#2`" to that image Result: the image should fit without much cropping the 0.46:1 aspect ratio of the shape, but it is cropped 1:1 before being applied to it. Cause: when the image has a set aspect ratio, we are always cropping it to 1:1 aspect ratio in postProcessCroppedCanvas but it should be cropped to the shape aspect ratio as it was done in previous version. Fix: go back to what was done in saas-18.3 and apply the shape aspect ratio and not just square (1:1) aspect ratio. opw-5415137 Forward-Port-Of: odoo/odoo#250922
This fix prevents records created by Marketing Automation templates, such as the “Confirmed contacts” list and related actions, from being deleted during module upgrades. As a result, users who set up a template can upgrade the app without losing these campaign components.
Original PR description
When a user chooses the "Double Opt-in" template (or others using `_create_records_with_xml_ids`), the system creates records like the "Confirmed contacts" list and a server action. It also adds an…
When a user chooses the "Double Opt-in" template (or others using `_create_records_with_xml_ids`), the system creates records like the "Confirmed contacts" list and a server action. It also adds an `ir.model.data` entry for them under the module, but sets `noupdate=False`. The problem is that during a module upgrade, Odoo's cleanup process (`_process_end`) deletes any record marked `noupdate=False` if it wasn't "reloaded" from a file. Since these records are created manually in the code and aren't in any XML file, they never get reloaded, so Odoo just deletes them and the records they point to. By setting `noupdate=True` when these rows are created, we tell the system to leave them alone during upgrades. Since these only exist at runtime and don't have a data file, we don't need to worry about them being updated later anyway. The helper was added in https://github.com/odoo/enterprise/commit/fd98660e352512e0b74d91958707687978154833. Steps to reproduce: 1. Install Marketing Automation 2. Go to Campaigns > New and pick the "Double Opt-in" template, then save 3. Confirm the "Confirmed contacts" mailing list and server action exist 4. Go to Apps and Upgrade Marketing Automation 5. Try to find the mailing list and server action again => They disappear after the upgrade. Ticket [link](https://www.odoo.com/odoo/project.task/6140191) opw-6140191 Forward-Port-Of: odoo/enterprise#115444
The GIF integration in chat now uses Klipy instead of Tenor for its API key. This change is needed to keep GIF search working, since the old Tenor key will no longer be valid for the new service and Tenor itself is being phased out.
Original PR description
Tenor API will be terminated on June 30, 2026: https://developers.google.com/tenor/guides/quickstart This commit makes the Tenor API key input settings use a Klipy GIF API key instead of a Tenor GIF API key. To keep GIF working after this commit, the API key must necessarily be changed to a Klipy GIF API key, as the old Tenor API key would be considered as an invalid Klipy API key. Task-5491965 Upgrade: https://github.com/odoo/upgrade/pull/10516 Forward-Port-Of: odoo/odoo#272493 Forward-Port-Of: odoo/odoo#250113
The Planning app now calculates weekly hours using the actual first day of the week set in the system, instead of always relying on the locale default. This prevents employees with flexible schedules from showing more hours than their calendar allows when the week starts on a different day.
Original PR description
**Steps to reproduce** - Install planning - Switch to English (UK) and change the "First day of the week" to Sunday in the technical settings - Have an employee with a flexible schedule with a total of 40h/week, average 8h/day - In the planning app, after creating a shift to display the employee in the gantt view, notice that when hovering over the progress bar on the left, 48 worked hours are expected for the current week, which is more than what is defined in the employee's calendar **Cause** The displayed week, starting on Sunday, could accumulate more hours than the weekly cap due to the Sunday being part of another week with the locale default first day (Monday). opw-6110395 Forward-Port-Of: odoo/odoo#259600
This fix ensures subcontracted receipt quantities stay as manually adjusted, even after the inventory scheduler runs. It prevents the system from overwriting user changes and helps keep purchase and receipt quantities aligned with what was intended.
Original PR description
**Issue** Quantity on subcontracted receipts could be overridden after running the scheduler. **Steps to reproduce** - Create a subcontracting product - Create a PO for 10 units of that product - Go…
**Issue** Quantity on subcontracted receipts could be overridden after running the scheduler. **Steps to reproduce** - Create a subcontracting product - Create a PO for 10 units of that product - Go to receipt and open Subcontracting Productions' - Change the quantity to 5 - Enable debug mode. - Run Inventory/Operations/Procurement: run scheduler - Return to the receipt -> The receipt quantity is reset to 10 instead of remaining at 5. **Cause** Since the refactor introduced in commit: https://github.com/odoo/odoo/commit/fc66e2d4eb638f1486e69cd5920f02c787055da1, subcontracting receipt moves are no longer automatically picked when the production quantity is modified. In particular, this test case protects that behavior: https://github.com/odoo/odoo/blob/f7fb0a941d6bac39b7057db36f57be079559912c/addons/mrp_subcontracting/tests/test_subcontracting.py#L1629-L1631 When the scheduler runs: https://github.com/odoo/odoo/blob/f7fb0a941d6bac39b7057db36f57be079559912c/addons/stock/models/stock_rule.py#L731 it computes the moves to assign: https://github.com/odoo/odoo/blob/f7fb0a941d6bac39b7057db36f57be079559912c/addons/stock/models/stock_rule.py#L706-L710 However, subcontracting moves are still included in the assignment domain: https://github.com/odoo/odoo/blob/f7fb0a941d6bac39b7057db36f57be079559912c/addons/stock/models/stock_rule.py#L680-L688 https://github.com/odoo/odoo/blob/f7fb0a941d6bac39b7057db36f57be079559912c/addons/mrp/models/stock_rule.py#L127-L129 As a result, they are reassigned if they are not already picked: https://github.com/odoo/odoo/blob/f7fb0a941d6bac39b7057db36f57be079559912c/addons/stock/models/stock_move.py#L1931-L1937 opw-6229668 Forward-Port-Of: odoo/odoo#270837
Opening the full message composer could fail when a recipient was identified only by email and not linked to a contact. This fix ignores those incomplete recipients so the composer opens normally and users can continue sending messages without errors.
Original PR description
The full composer builds default_partner_ids by doing recipient.persona.id over allRecipients. When a recipient is not linked to a partner, the chatter input inserts it with persona: false here:…
The full composer builds default_partner_ids by doing recipient.persona.id over allRecipients. When a recipient is not linked to a partner, the chatter input inserts it with persona: false here: https://github.com/odoo/odoo/blob/28c11a068b2ecf521f8ba1da271ebee24f5345ae/addons/mail/static/src/core/web/recipients_input.js#L151 false.id is undefined, so the wizard gets a bad id and clicking the recipient tag crashes with `Invalid ids list: false`. The persona is false because /mail/partner/from_email returns no partner for unresolved emails when the user does not have base.group_partner_manager. That matches the gate _prepare_post_data already uses for the simple post flow: https://github.com/odoo/odoo/blob/28c11a068b2ecf521f8ba1da271ebee24f5345ae/addons/mail/controllers/thread.py#L153,L155 The full composer just was not handling that case. Filter out persona-less recipients from default_partner_ids so the wizard only sees real partner ids. The full composer now drops them the same way the simple post path already does. Steps to reproduce: 1. As Administrator, create a user with the Sales group only (no Contact Creation). 2. Log in as that user. 3. Open a CRM lead whose Email is set but Customer is empty. 4. Click Send message. 5. Click the expand icon to open the full composer. 6. Click the recipient tag in the To field. => Recipient shows as Unnamed and clicking it raises an RPC error. Ticket [link](https://www.odoo.com/odoo/project.task/5974081) opw-5974081 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#271422 Forward-Port-Of: odoo/odoo#251780
This fix ensures users see the right manufacturing orders when opening them from the statistics button. It prevents confusion by making the displayed records match the actual manufactured orders linked to the product.
Original PR description
* Following https://github.com/odoo/odoo/pull/261438 (forward-ported to 19.0 in #265323) we also need to show correct MOs when view from statsbutton Forward-Port-Of: odoo/odoo#265195 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#269201
This change prevents the point of sale from crashing when Adyen sends the same payment notification more than once. It makes the payment flow more resilient, so customers can complete card payments without unexpected errors caused by duplicate messages.
Original PR description
The following error is raised in the POS when paying with an Adyen terminal: ``` TypeError: Cannot read properties of undefined (reading 'uuid') at Proxy.handleAdyenStatusResponse ``` Adyen delivers…
The following error is raised in the POS when paying with an Adyen terminal: ``` TypeError: Cannot read properties of undefined (reading 'uuid') at Proxy.handleAdyenStatusResponse ``` Adyen delivers webhook notifications at-least-once, so the ADYEN_LATEST_RESPONSE event can fire several times for a single payment, running handleAdyenStatusResponse concurrently. After the await on get_latest_adyen_status, a previous (duplicate) notification may already have resolved the payment line, so getPendingPaymentLine no longer returns it and the subsequent line.uuid dereference crashes. opw-6237987 patched the same root cause on a single line by adding an optional chaining operator in isPaymentSuccessful, which only moved the crash to the next dereference. Fetch the pending line once at the start of handleAdyenStatusResponse and bail out when it is gone, so every dereference below is safe. The same guard is added to the remaining branches of _adyen_handle_response for consistency with the existing Reject branch. opw-6237987 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270880 Forward-Port-Of: odoo/odoo#269720
Shop Floor now correctly places manufacturing orders without a planned start date after those that are already scheduled. This makes the order shown in Shop Floor match the usual work order prioritization and avoids confusing list behavior for operators.
Original PR description
## Problem In shop floor, MOs with unplanned work orders get sorted before MOs that have planned operations, which contradicts the normal nulls last sorting for work orders. ## Solution We will…
## Problem In shop floor, MOs with unplanned work orders get sorted before MOs that have planned operations, which contradicts the normal nulls last sorting for work orders. ## Solution We will update the sorting logic in the MrpDisplay component to more gracefully handle falsy date_start values, sorting them to the end. ## Steps to reproduce (runbot 19) 1. Create 2 MOs with an operation (work order) involving a work center, we'll call them A and B. 2. Open Shop Floor and open the work center that the MOs' work orders belong to, and note they are ordered A, B (this is fine, neither are planned so the precedence falls back to id 3. Go back to MO B and plan it. This should give it precedence in Shop Floor 4. Under the work center in Shop Floor, note that the MOs are still ordered A, B, despite B's work order having a start date and A's work order not having one To further motivate this being unintended, you can go to Manufacturing > Operations > Work Orders, and you'll see MO B's work order sitting at the top of the list. opw-6303323 Forward-Port-Of: odoo/enterprise#121693
This update corrects a timing issue in planning where shifts on 24/7 schedules could be shown as one minute longer than they really were. As a result, allocated time in reports now matches the planned shift length, improving accuracy for customers relying on planning data.
Original PR description
**Problem:** On a round-the-clock (0h-24h) working schedule, shifts created from a shift template that spans more than one day get one extra minute added to their allocated time: an 8-hour shift…
**Problem:** On a round-the-clock (0h-24h) working schedule, shifts created from a shift template that spans more than one day get one extra minute added to their allocated time: an 8-hour shift shows 08:01. **Steps to reproduce:** 1. Create a working schedule with a 00:00 -> 24:00 attendance for every day (24h/day, "Full Day"). 2. Assign an employee to that schedule. 3. Create a multi-day-span shift template (e.g. 16:00 -> 00:00, 2 days). 4. Plan a shift for that employee using the template. 5. Observe the Allocated Time shows one minute more than expected (08:01). **Current behavior:** Allocated Time is one minute too long (e.g. 08:01 instead of 08:00), which throws off the customer's planning reports. **Expected behavior:** Allocated Time matches the template duration exactly (08:00). **Cause of the issue:** In `_calculate_start_end_dates`, the end of a multi-day-span shift is computed with `resource.calendar_id.plan_days(...)`. On a 0h-24h calendar each day ends at `time.max` (23:59:59.999999), so `plan_days` returns an end datetime carrying those stale seconds. The following `end.replace(hour=..., minute=...)` overwrites only the hour and minute, leaving `second=59, microsecond=999999`. The slot is therefore ~1 minute longer than intended, and `allocated_hours` rounds that up to 08:01. **Fix:** Resetting seconds and microseconds when rebuilding the end datetime keeps the slot aligned to the template's whole-minute boundary, regardless of how the underlying calendar represents the end of day. The hour/minute already come from the template, so the leftover sub-minute precision from `plan_days` is never meaningful and is what produces the drift. opw-6265238 Forward-Port-Of: odoo/enterprise#120219
1 change
Resolved issues and error corrections
This fix prevents Kenya PoS combo products from being treated as items that must be registered with eTIMS. As a result, customers can sell combos normally without validation warnings or blocked payments, while only the actual products inside the combo are sent for compliance.
Original PR description
Steps to reproduce ------------------ 1. Install l10n_ke_edi_oscu_pos. 2. Select the Kenyan company. 3. Enable eTIMS on the PoS. 4. Register the products inside a combo, but not the combo itself. 5.…
Steps to reproduce ------------------ 1. Install l10n_ke_edi_oscu_pos. 2. Select the Kenyan company. 3. Enable eTIMS on the PoS. 4. Register the products inside a combo, but not the combo itself. 5. Sell the combo in the PoS. Observation ----------- We see a warning that the combo must be registered to eTIMS, and the order can't be validated. What's happening ---------------- In the PoS a combo adds a 0 price parent line for the combo product, but the combo is not a real item to send to eTIMS, only the products inside it are, and (as per step 4) the combo is not registered. `checkEtimsFields` sees the combo as not registered, so it raises the warning in `showUnregisteredProductsWarning` and blocks the payment in `validateOrder`. Fix --- In the backend, we skip sending the parent combo line to eTIMS, and on the frontend, we make the combo parent line not need eTIMS registration, so the warning and the block don't apply to it. Note ---- Pushing onto `tax_ids` in the order widget makes the Kenyan receipt crash with "The 'tax_ids' array cannot be modified. Use the update method instead.", so we assign the taxes instead of pushing them. opw-6253306 Forward-Port-Of: odoo/enterprise#121831 Forward-Port-Of: odoo/enterprise#119362
9 changes
Resolved issues and error corrections
Users can now remove the currency selection in the payment register without triggering an error. This fixes a crash in the Argentine withholding flow and makes the payment process more reliable for affected companies.
Original PR description
When the user removes the currency from the payment register, a traceback is raised. Steps to reproduce the error: - Install ``l10n_ar_withholding`` module - Switch to ``(AR) Exento`` company - Create a new invoice > Confirm > Pay > Unset the currency Traceback: ```py ValueError: Expected singleton: res.currency() ``` https://github.com/odoo/odoo/blob/d98afdc08b46bf458eaa287ea882cc7663286a59/addons/l10n_ar_withholding/wizards/account_payment_register.py#L27 This line causes a traceback with an empty currency when the user removes the currency from the payment register. sentry-7362499567 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#255825
This fix ensures that when a dropship delivery is only partially completed, the inventory valuation is calculated using the delivered quantity instead of the originally planned quantity. As a result, accounting values now match the actual goods received and prevent overstated costs.
Original PR description
**Problem:** partial quantities in a dropship picking is not taken into account for the svl quantity (it's always the full initial quantity) **Steps to reproduce:** - create a storable product with…
**Problem:** partial quantities in a dropship picking is not taken into account for the svl quantity (it's always the full initial quantity) **Steps to reproduce:** - create a storable product with dropship route - set the category as avco auto - set a vendor in the purchase tab with a price of 10 - confirm a SO for a quantity of 2 - confirm the related PO with a unit price of 10 - on the dropship picking change the quantity to 1 - validate without backorder - click on the valuation smart button **Current behavior:** - the svls have quantities of 2 and -2 - the related amls have debit/credit of 20 **Expected behavior:** - the svls should have quantities of 1 and -1 - the related amls should have debit/credit of 10 **Cause of the issue:** when creating the svls we use the move's product_qty instead of its quantity https://github.com/odoo/odoo/blob/c97629d5efb82aed191de211593c539686cae65b/addons/stock_account/models/stock_move.py#L290 **fix:** product_qty is expressed in the uom of the product and quantity in the uom of the move so we need to add a uom conversion to the fix opw-6113031 Forward-Port-Of: odoo/odoo#271531 Forward-Port-Of: odoo/odoo#270823
Imported bill quantities are now handled with the correct precision when matched to a purchase order. This prevents small rounding errors that could slightly overstate the invoiced amount, such as turning 1800.0 into 1800.01.
Original PR description
When importing an XML bill and linkin git to a purcahse order, the invoiced quantity may be computed incorrectly, due to a decimal precision mismatch. Steps to reproduce: - Import an XML bill having a line with quantity 1800.0 - Link to a purchase order with the same line Issue: The invoiced quantity will be computed with 1 cent difference (1800.01) Analysis: Because the system forced a decimal precision of 13 for 'Product Unit of Measure', quantity is imported as 1800.0000000000016. Later, when computing the invoiced quantity, the system round the quantity using 'UP' strategy, rounding the amount to 1800.01 opw-6194824 Forward-Port-Of: odoo/odoo#270916 Forward-Port-Of: odoo/odoo#266283
This update fixes a bug in the rich text editor that could cause an error when users inserted a file and then used editor commands. It also improves how the editor keeps track of selections during paste actions, making editing more reliable and preventing unexpected interruptions.
Original PR description
### Steps to reproduce: - Insert a file using /file command and upload a file. - Type any powerbox command (e.g. /file, /media) - Press Enter to select the command - IndexSizeError: Failed to execute…
### Steps to reproduce: - Insert a file using /file command and upload a file. - Type any powerbox command (e.g. /file, /media) - Press Enter to select the command - IndexSizeError: Failed to execute 'setBaseAndExtent' on 'Selection': There is no child at offset 5. ### Description of the issue/feature this PR addresses: - makeSavePoint() of history_plugin uses preserveSelection() to save current selection. However, preserved selections remain registered in the cursor update mechanism and continue to be updated when the editor adjusts cursors during DOM changes. - During normalization, updateFeffs() updates all preserved cursors. Because the savepoint selection is stored using preserveSelection(), it is updated as well and can become inconsistent with the DOM state that the savepoint later restores, resulting in an IndexSizeError. ### Solution (backport-https://github.com/odoo/odoo/commit/1eb0e6de8f566d260ecb6c0dae3436e87ffe8436): - Update preserved cursors after steps restoration so they stay in sync with the restored DOM state. - Stage the selection in the clipboard plugin after `before_paste_handlers`, ensuring the selection reflects the final adjusted state before pasting. - Fix `selectFullText` to trigger `pointerup` to have a correct staged selection. task-6298761 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270085
Swiss invoices now generate a payment reference even when the customer is outside Switzerland or Liechtenstein. This ensures the invoice PDF and payment details stay visible, making it easier for customers to pay correctly.
Original PR description
Issue: When an invoice (sales journal) uses "Switzerland" localization and the invoice is issued to a customer outside Switzerland/Liechtenstein, no payment reference is generated. This causes the…
Issue: When an invoice (sales journal) uses "Switzerland" localization and the invoice is issued to a customer outside Switzerland/Liechtenstein, no payment reference is generated. This causes the invoice PDF to hide payment communication and bank account details. Other localizations like Belgian companies, uses Belgian references, the reference is always generated regardless of customer country. Steps to reproduce: - Configure a Swiss company with a QR IBAN bank account - Set the sales journal Communication Standard to Switzerland - Create and confirm an invoice for a non swiss customer (US, BE) - Observe in the pdf and in the other info tab -> no payment reference or payment details Cause: `get_l10n_ch_qrr_number()` was using on `l10n_ch_is_qr_valid()`, which conflicts QR-bill printability (partner country, currency) with payment reference generation. When the customer is outside CH/LI, `l10n_ch_is_qr_valid()` is False and no QRR reference is generated. Solution: Decouple QRR reference generation from QR bill printability. The Swiss communication standard now generates a QRR format reference when a QR-IBAN is configured, regardless of customer's country or currency. opw-6222417 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#266861
This update corrects how Odoo updates payment statuses when a reconciled bank statement is reversed. Grouped payments are now properly moved back to "In Process" along with other payments, so users can reconcile the statement again without inconsistent values or errors.
Original PR description
**Steps to reproduce:** - Install Accounting - Create an invoice: * Customer: Partner A * Total: 30.00 - Confirm the invoice - Create a second invoice for the same customer: * Customer: Partner A *…
**Steps to reproduce:** - Install Accounting - Create an invoice: * Customer: Partner A * Total: 30.00 - Confirm the invoice - Create a second invoice for the same customer: * Customer: Partner A * Total; 10.00 - Confirm the invoice - From the invoice list, select both invoice - Create payment: * Journal: Bank * Group Payments: [checked] * Amount: 40.00 - Create a third invoice for another customer: * Customer: Partner B * Total: 25.00 - Confirm the invoice - Register payment from the invoice - Create a fourth invoice for another customer: * Customer: Partner C * Total; 40.00 - Confirm the invoice - Register payment from the invoice - From the payment list, select all 3 payments and create batch payment - Validate the batch payment - From Accounting dashboard, open Bank journal - Create a new bank statement line of 105.00 - Match it with the batch payment At that point, the statement line is reconciled with the 4 invoices and the 3 payments are marked as paid. - Go to the fourth invoice - Reset it to draft - Change the price - Save When the amount of the invoice is changed, the statement line is unreconciled and all the payments should change state from "Paid" to "In Process". **Issue:** All the single payments have their state correctly changed to "In Process", except for the group payment for the 2 first invoices, which leads to undesired values when trying to reconcile the statement line with the batch payment again. **Cause:** When the statement is unreconciled, all the partial reconcile records are deleted and the state of the linked payments are updated to "In Process". The payments are retrieved by checking if there are linked to an account move present in the partial reconcile record and if the amount of the payment matches the amount of the partial reconcile record. In case of a group payment, there are 2 partial reconcile records for each invoice linked to the payment. Therefore, in that case, the amount doesn't match the amount of the payment because it matches the amount of one of the invoice. opw-6141089 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#269510
This change corrects how grouped customer payments are handled when a bank statement is later unreconciled. It ensures all related payments move back to the proper intermediate status, so the statement can be reconciled again without errors or inconsistent payment values.
Original PR description
**Steps to reproduce:** - Install Accounting - Create an invoice: * Customer: Partner A * Total: 30.00 - Confirm the invoice - Create a second invoice for the same customer: * Customer: Partner A *…
**Steps to reproduce:** - Install Accounting - Create an invoice: * Customer: Partner A * Total: 30.00 - Confirm the invoice - Create a second invoice for the same customer: * Customer: Partner A * Total; 10.00 - Confirm the invoice - From the invoice list, select both invoice - Create payment: * Journal: Bank * Group Payments: [checked] * Group Payments: [checked] * Amount: 40.00 - Create a third invoice for another customer: * Customer: Partner B * Total: 25.00 - Confirm the invoice - Register payment from the invoice - Install Accounting - Create an invoice: * Customer: Partner A * Total: 30.00 - Confirm the invoice - Create a second invoice for the same customer: * Customer: Partner A * Total; 10.00 - Confirm the invoice - From the invoice list, select both invoice - Create payment: * Journal: Bank * Group Payments: [checked] * Amount: 40.00 - Create a third invoice for another customer: * Customer: Partner B * Total: 25.00 - Confirm the invoice - Register payment from the invoice - Create a fourth invoice for another customer: * Customer: Partner C * Total; 40.00 - Confirm the invoice - Register payment from the invoice - From the payment list, select all 3 payments and create batch payment - Validate the batch payment - From Accounting dashboard, open Bank journal - Create a new bank statement line of 105.00 - Match it with the batch payment At that point, the statement line is reconciled with the 4 invoices and the 3 payments are marked as paid. - Go to the fourth invoice - Reset it to draft - Change the price - Save When the amount of the invoice is changed, the statement line is unreconciled and all the payments should change state from "Paid" to "In Process". **Issue:** All the single payments have their state correctly changed to "In Process", except for the group payment for the 2 first invoices, which leads to undesired values when trying to reconcile the statement line with the batch payment again. **Cause:** When the statement is unreconciled, all the partial reconcile records are deleted and the state of the linked payments are updated to "In Process". The payments are retrieved by checking if there are linked to an account move present in the partial reconcile record and if the amount of the payment matches the amount of the partial reconcile record. In case of a group payment, there are 2 partial reconcile records for each invoice linked to the payment. Therefore, in that case, the amount doesn't match the amount of the payment because it matches the amount of one of the invoice. opw-6141089 Forward-Port-Of: odoo/enterprise#120210
Neutralized databases will now use Odoo’s test VIES service instead of the live production endpoint by default. This helps prevent unintended requests to the real VAT validation service while keeping databases with custom settings unchanged.
Original PR description
Neutralized databases don't always have demo data, so they talk to production VIES endpoint. With this change, the production endpoint is replaced with the test one. If some database has a custom endpoint, nothing is done. @moduon MT-15142 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272036
Mailing campaign emails that bounce will now correctly increase the contact’s bounce count. This fixes cases where bounce notifications came from a different sender address, which previously caused the system to miss the bounce and undercount it.
Original PR description
Previously, when a mailing campaign sent out an email to a mailing.contact, and that email bounced, the bounce would not increment the contact's bounce count. This commit makes it so that the bounce count is correctly updated when a mailing campaign sends an email that bounces. task-4893615 Forward-Port-Of: odoo/odoo#226362
1 change
Resolved issues and error corrections
Records created when using Marketing Automation templates, such as the Double Opt-in mailing list and server action, will now remain available after a module upgrade. This prevents users from losing important campaign setup items during routine updates.
Original PR description
When a user chooses the "Double Opt-in" template (or others using `_create_records_with_xml_ids`), the system creates records like the "Confirmed contacts" list and a server action. It also adds an…
When a user chooses the "Double Opt-in" template (or others using `_create_records_with_xml_ids`), the system creates records like the "Confirmed contacts" list and a server action. It also adds an `ir.model.data` entry for them under the module, but sets `noupdate=False`. The problem is that during a module upgrade, Odoo's cleanup process (`_process_end`) deletes any record marked `noupdate=False` if it wasn't "reloaded" from a file. Since these records are created manually in the code and aren't in any XML file, they never get reloaded, so Odoo just deletes them and the records they point to. By setting `noupdate=True` when these rows are created, we tell the system to leave them alone during upgrades. Since these only exist at runtime and don't have a data file, we don't need to worry about them being updated later anyway. The helper was added in https://github.com/odoo/enterprise/commit/fd98660e352512e0b74d91958707687978154833. Steps to reproduce: 1. Install Marketing Automation 2. Go to Campaigns > New and pick the "Double Opt-in" template, then save 3. Confirm the "Confirmed contacts" mailing list and server action exist 4. Go to Apps and Upgrade Marketing Automation 5. Try to find the mailing list and server action again => They disappear after the upgrade. Ticket [link](https://www.odoo.com/odoo/project.task/6140191) opw-6140191 Forward-Port-Of: odoo/enterprise#115444
5 changes
Resolved issues and error corrections
When a new language is installed, the DIAN email templates will now be translated automatically. This fixes a gap that only affected languages added after the Colombian electronic invoicing module was already in place, so users will see the templates in their chosen language without extra manual work.
Original PR description
### Issue: When installing a new language on a database with `l10n_co_dian`, the DIAN email templates are not translated Languages already installed when `l10n_co_dian` is installed are correctly…
### Issue: When installing a new language on a database with `l10n_co_dian`, the DIAN email templates are not translated Languages already installed when `l10n_co_dian` is installed are correctly translated, as the copy happens at that moment Only languages installed afterwards are affected ### Cause: The DIAN email templates are created dynamically via `_create_dian_mail_templates` as copies of the `account` templates Because they are created at runtime, no translations exist for their XML IDs in the `l10n_co_dian` `.po` files The `TranslationImporter` finds nothing to import for them The fix hooks into `_load_module_terms` to sync translations from the source `account` templates to the DIAN ones whenever a new language is installed `TranslationImporter.save()` is used without `force_overwrite`, so existing translations on `noupdate` records are never replaced This is the standard way to push translations while preserving any manually modified values ### Notes: Languages installed before this fix are not updated Only newly installed languages will get the synced translations ### Steps to reproduce: - Install `l10n_co_dian` - Enable Developer Mode in Settings - Install any language (e.g. `fr_FR`) - Go in Settings > Technical > Email > Email Templates - Search for DIAN and open Invoice or Credit Note Before the fix, the DIAN invoice and credit note templates had untranslated name, description and body_html opw-6269979 Forward-Port-Of: odoo/enterprise#120855
Users can now sort project tasks by Planned Date in the portal without encountering an error. This resolves a broken sorting option so project teams can browse tasks more reliably.
Original PR description
Currently, an error occurs when a user sorts tasks by Planned Date. **Steps to reproduce:** - Install the `project_enterprise` module with demo data. - Go to Projects in the portal (`/my/projects`),…
Currently, an error occurs when a user sorts tasks by Planned Date. **Steps to reproduce:** - Install the `project_enterprise` module with demo data. - Go to Projects in the portal (`/my/projects`), open any `project`, and sort the tasks by `Planned Date`. KeyError: 'order' After a [recent change], the sort order is retrieved from searchbar sortings. When sorting by Planned Date, it attempts to access the order key from the corresponding sorting configuration [1]. However, the planned_date_begin entry does not define an order key [2], which raises error when it tries to access order. This commit ensures that the order key is added with its value for planned_date_begin in searchbar sorting. [recent change]: https://github.com/odoo/odoo/commit/8be5dacf9fbfe8c23b04c876994bea2ce7cbb89a [1]: https://github.com/odoo/odoo/blob/2ae9b57b86cd0bc4816ff8ec207564631baa8ad6/addons/project/controllers/portal.py#L424 [2]- https://github.com/odoo/enterprise/blob/9dd3a9b2c09a6d23a3f71d3e531edd6d78b30277/project_enterprise/controllers/portal.py#L8-L11 sentry-7556050938 Forward-Port-Of: odoo/enterprise#121230 Forward-Port-Of: odoo/enterprise#121141
This update prevents errors when Shopee sends customer identifiers that are larger than the system expected. If an ID is too large to fit in the usual field, it is now stored and matched in a different customer reference field so contacts can still be created correctly.
Original PR description
When the Shopee connector was introduced, Shopee documented `buyer_user_id` as an int32. We therefore store it in an `Integer` field, which maps to a PostgreSQL int32 column. However, feedback showed that Shopee can send ids exceeding the int32 bounds, leading to a traceback when creating new contacts. Shopee has since updated their documentation to confirm the field is actually an int64. Since we are in stable and cannot change the column type, this commit falls back to the `ref` field of the `res.partner` model to store and match Shopee contacts whose identifier doesn't fit within an int32. opw-6325948 See also: - master: https://github.com/odoo/enterprise/pull/121507 - upgrade: https://github.com/odoo/upgrade/pull/10578 Forward-Port-Of: odoo/enterprise#121707 Forward-Port-Of: odoo/enterprise#121498
This fix prevents the AI from appearing to create project tasks twice when a creation request hits an error. It now checks that the required information is valid before showing the preview or confirming the action, which makes the user experience more reliable and avoids duplicate-looking prompts.
Original PR description
This commit removes an issue where the LLM would retry on error when performing a creation which would give the impression that it created items twice. To do so, this commit now validates that the fields exists before calling the `create()` method, and before showing the preview to the user. Ensuring it avoids throwing an error after the message has been confirmed (resulting in the double preview). task-6229596 Forward-Port-Of: odoo/enterprise#118044
Debit notes for Colombian DIAN now send correctly without triggering an XML template error. The system no longer includes an unnecessary buyer reference on debit notes, which prevents the document from failing during submission.
Original PR description
Issue: Sending Debit Notes to a tax authority can cause the following error: "ValueError: The following child node is not defined in the template: DebitNote/cbc:BuyerReference" Steps to reproduce on…
Issue: Sending Debit Notes to a tax authority can cause the following error: "ValueError: The following child node is not defined in the template: DebitNote/cbc:BuyerReference" Steps to reproduce on any database with DIAN and Colombian localization: 1. Create a new "Sales" type journal. Then, check the checkbox “Nota de Debito”. 2. Find a res.partner with a ref field, or add a ref field to any partner. 3. Make an invoice using the partner found in step 2. Ensure it uses a tax. Confirm it. 4. Send that invoice to DIAN. 5. Create a Debit Note for that invoice. Use the journal created in step 1. 6. Add a product, price, and tax to the debit note. Confirm it. 7. Send the debit note to DIAN. Explanation: The `_add_invoice_header_nodes` method on the AccountEdiXmlUbl_21 model adds a BuyerReference node unconditionally. (See account_edi_xml_ubl_21.py.) But the DebitNote XML template does not include a BuyerReference element (see ubl_21_debit_note.py). This caused a ValueError when assembling the XML for debit note documents. Solution: The fix overrides this in the Colombian localization by clearing the BuyerReference value when the document type is "debit_note". That way, the node is omitted from the output. opw-6181039 Forward-Port-Of: odoo/enterprise#121458 Forward-Port-Of: odoo/enterprise#121422
1 change
Resolved issues and error corrections
Audio files shared through Documents will no longer open in public preview. This avoids broken playback in Chrome and keeps shared files consistent with the existing handling of video files, where preview is also disabled by default.
Original PR description
**Steps to reproduce:** - Install Documents app - Upload a sound file (mp3 for example, but the behavior is the same for other formats) - Share the document and copy the link - Log out - Paste the…
**Steps to reproduce:**
- Install Documents app
- Upload a sound file (mp3 for example, but the
behavior is the same for other formats)
- Share the document and copy the link
- Log out
- Paste the link
- Click on preview button
- Media is working fine on Firefox
- Media won't read on Chrome
```
Loading media from '' violates the following Content Security Policy directive: "default-src 'none'".
Note that 'media-src' was not explicitly set, so 'default-src' is used as a fallback.
The action has been blocked.
```
**Issue:**
Since [1] default CSP Headers are too strict for Chrome default media rendering, which breaks the file preview (and force user download).
This only impacts Chrome as they seem to render generate `<video><source>` elements to render the file which triggers a secondary request and fails due to the CSP constraint.
**Fix:**
Could re-apply the header fix of 17.4 (see [2]), but it seems better to block the preview of audio files as well by default (to match how we manage videos).
(Note: we don't want to be used as a media streaming platform)
[1] (set csp to none by default) https://github.com/odoo/odoo/commit/64beb80205dffe4b432c8b5813a3271b073fa85e
[2] (similar issue which was not fixed in 18.0+) https://github.com/odoo/enterprise/commit/a7af78eeb2b9763045a5bda8714172f5e7402df8
[3] (mp4 preview removed) https://github.com/odoo/enterprise/commit/ea88cf7c6d5f077b60fb59347659507fded0e2fe
opw-62350438 changes
Resolved issues and error corrections
This fix ensures that once an appointment is canceled, it no longer prevents the available capacity of a resource from being reduced. As a result, users can update resource availability without running into an error caused by bookings that are no longer active.
Original PR description
Steps to reproduce: 1. Install `appointment` 2. Create an appointment type using resources 3. Create a resource with capacity `4` 4. Book an appointment on that resource with reserved capacity `4` 5.…
Steps to reproduce: 1. Install `appointment` 2. Create an appointment type using resources 3. Create a resource with capacity `4` 4. Book an appointment on that resource with reserved capacity `4` 5. Try to reduce the resource capacity 6. Cancel the appointment 7. Try again to reduce the resource capacity Current behavior: - Reducing the resource capacity raises: `The capacity used can not be lesser than the capacity reserved` Issue: - Canceled appointments still prevent lowering the resource capacity, even though the booking is no longer active. Cause: - Canceling an appointment only archives the related `calendar.event`, it does not remove the corresponding `appointment.booking.line`. That booking line keeps its previous `capacity_reserved` value, while `capacity_used` is a stored computed field depending on the resource capacity. When the resource capacity is reduced after cancellation, the archived booking line is recomputed with the new lower resource capacity and this violates the SQL constraint Solution: - For inactive booking lines: Set `capacity_used = capacity_reserved` opw-6036053
Spreadsheets now block inserting pivots that rely on unsupported relationship fields, avoiding errors caused by non-numeric record IDs. Users will see the insert option disabled with a helpful message, so they know why the pivot cannot be added.
Original PR description
Prevent inserting a pivot into a spreadsheet when it contains groupbys on relations whose IDs are not numeric (e.g. account.root uses string IDs), as the pivot table engine does not support them. The insert button is now disabled with an appropriate tooltip. Task: 6023622
This update prevents the Balance Sheet report from crashing when an analytic account is selected. It removes an unnecessary check that was causing the system to loop until it hit a recursion error, improving report reliability for users working with analytic accounting.
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
This fix ensures card payments made in Point of Sale are marked as paid once the session is closed and the payment has been reconciled. It prevents customer payments from remaining stuck in the “In progress” status in Accounting, improving payment tracking and reducing manual follow-up.
Original PR description
**Steps to reproduce:** - Go to the PoS, make a sale, pay by card - Close the session - Go to the accounting module, customer, payment - The payment is stuck in the "In progress" stage **Why the fix:** The bank payments' state were never taken into account and never changed so it stayed in the 'in_process' state. The _compute_state() function in the account module that is supposed to take care of this does nothing for the bank payments from the PoS, because it only checks if the liquidity is equal to zero, which does not happen in this context. It should happen when we manually matching payments and that's how it's used in the Account module, but it's not compatible with the PoS session closure. We now change the payment's state to paid if the payment has been reconciled at the session's closure. opw-5438029
Instagram posts with images will now fail cleanly instead of causing the server request to crash when the network times out. This improves reliability and gives users a clearer failure message, including guidance to use a smaller image when the issue is likely caused by image size or processing time.
Original PR description
Making an Instagram containing an image can crash the server with an unhandled `ReadTimeout` instead of marking the post as failed. ### Cause When creating a media container, Odoo passes a URL pointing to its own server and Instagram fetches the image from it server-side before responding. The timeout therefore covers network latency, Instagram's download speed from the Odoo server, and image processing time, making it prone to being exceeded. When it is, `requests` raises a `ReadTimeout` which is unhandled, leading to a raw RPC error instead of a clean `state='failed'`. ### Fix Catch the network errors and mark the post as failed instead of letting them crash the request. Timeouts get a message suggesting a smaller image, since they are usually caused by Instagram fetching and processing a large image server-side. Any other request error falls back to a generic message. opw-6015997
The Czech VIES report XML now includes the taxpayer city and missing representative name fields when needed, and no longer includes the email address. This helps the report pass official validation and reduces the risk of filing errors with the tax authority.
Original PR description
**PROBLEM** For VIES report, the xml should not contains the email. The city of the tax payer is missing, and while it's not strictly require, it can modify the tax regime of the payer, so we need to include it in the xml. There is missing fields in the case the company is an individual (zast_jmeno, zast_prijmeni). **STEP TO REPRODUCE** 1. Create an invoice to a EU partner, don't forget to set the transaction code on the invoice line (unhide the field). 2. Go to the VIES reports, and generate the xml. 3. Upload it to https://mojedane.gov.cz/pmd/epo to validate and see the errors. documentation: https://mojedane.gov.cz/dpr/adis/idpr_pub/epo2_info/popis_struktury_detail.faces?zkratka=DPHSHV opw-6190983
The website language selector now includes descriptive alternative text for flag images when the flag is the only visual cue. This makes the selector easier to use with screen readers and gives search engines better context.
Original PR description
Steps to reproduce: 1. Enable the language selector in the website header. 2. Enable the "Inline" and "Flag" options. 3. Inspect the flag images rendered in the inline variant. Issue: Flag images in the list items have an empty `alt=""` attribute in "Flag only" mode, where the flag is the sole visual indicator of the language, making the selector inaccessible to screen readers and providing no context for search crawlers. Expected behavior: Inline + Flag should have a descriptive ALT tag since there is no adjacent text or code to identify the language, the flag is not decorative. opw-6246464
This fix makes the revenue shown in Timesheets > Reporting calculate correctly for prepaid service orders billed by days. It prevents revenue figures from jumping to incorrect values when time entries are added, and keeps discounts reflected properly in the report.
Original PR description
Steps to reproduce 1. Create a service product: Invoicing Policy = Prepaid/Fixed Price, Track Service = Timesheets, Unit of Measure = Days 2. Confirm a Sales Order with 2 days of that product at…
Steps to reproduce 1. Create a service product: Invoicing Policy = Prepaid/Fixed Price, Track Service = Timesheets, Unit of Measure = Days 2. Confirm a Sales Order with 2 days of that product at 800/day 3. Register 1 hour on the generated task 4. Open Timesheets > Reporting, add the "Timesheet Revenues" measure Issue `timesheet_revenues` in `timesheets.analysis.report` was computed per analytic line as `(SOL.price_subtotal / SOL.qty_delivered) * (unit_amount * sol_uom.factor / ts_uom.factor)` (https://github.com/odoo/odoo/blob/16f170619d9cc5fd86529a3f17e349da37607f73/addons/sale_timesheet/report/timesheets_analysis_report.py#L42-L44). `SOL.qty_delivered` is a stored float rounded to the day UoM precision (0.01d). For 1 hour timesheeted, qty_delivered = 1/8 = 0.125d rounds to 0.13d, so the formula yields (1600 / 0.13) × (1/8) = 1538.46 instead of the correct 100. Because `qty_delivered` is recomputed each time a timesheet is added, all existing rows shift their revenue figure with every new entry. Additionally, using `price_subtotal / qty_delivered` as the per-unit rate ignores any line discount: the rate derived from a discounted subtotal divided by a delivered quantity that differs from the ordered quantity is not the effective price per day. For prepaid lines, the effective per-unit rate is `price_subtotal / product_uom_qty` — the ordered quantity is stable and the subtotal already reflects any discount — multiplied by the timesheet hours converted to the SO line UoM. opw-6150555
6 changes
Resolved issues and error corrections
Users can now rename custom fields in Studio using labels written in scripts like Arabic without triggering an invalid technical name. This avoids a confusing error and makes field customization work more smoothly for multilingual teams.
Original PR description
Steps: - Install web_studio - Add any field (example char field) to any view - Rename it in arabic, example `السَّلَامُ عَلَيْكُمْ` - Error Custom field names cannot contain double underscores Webclient (view_editor_model) escape every non-alphabetic chars, so new label value contains nothing but a space which will be replaced by a _ this new label value will be concatenated to `x_studio_`. Resulting to the string `x_studio__`. A solution should be to prevent changing the technical name if the new label value (escaped) is empty. opw-6311027
This fix ensures the SAF-T export uses the proper account grouping code instead of cutting the value incorrectly. It prevents reports from showing the wrong grouping number, helping keep accounting exports aligned with official requirements.
Original PR description
Steps to reproduce: - change 1920 Banck account to 19204321 - go in general ledger and export to "SAF-T" Issue: The grouping code is 4321 Grouping code should match official grouping code. As a matter of fact the chart of account seems to match thos grouping account if we slice them correctly. opw-6285078
This change prevents the Spanish Veri*factu* e-invoicing module from running into memory or timeout issues during installation on databases that already contain accounting entries. It makes the setup process more reliable and reduces the risk of failed installs for existing customers.
Original PR description
### Description: When trying to install the module `l10n_es_edi_verifactu` on a database that already has moves, it is possible to encounter a timeout or a memory error. This is caused by the compute `l10n_es_edi_verifactu_state` and `l10n_es_edi_verifactu_clave_regimen`, both compute linked to the new model `l10n_es_edi_verifactu.document`. ### Reference: opw-6293590
This change removes an incorrect validation that blocked invoices when more than one pension fund tax was applied to the same line. It now matches the Italian electronic invoicing rules, so affected invoices can be sent without error.
Original PR description
### Steps to reproduce the issue: 1. Download Accounting and l10n_it_edi_withholding 2. Switch to IT company 3. Create 2 taxes with a Pension fund type set (in Advanced Options) 4. Create an invoice…
### Steps to reproduce the issue: 1. Download Accounting and l10n_it_edi_withholding 2. Switch to IT company 3. Create 2 taxes with a Pension fund type set (in Advanced Options) 4. Create an invoice and set on the same line the 2 taxes created 5. Click on send and print and see the error: Invoices must have at most one Pension Fund tax set per line. (even if it's not true) ### Cause of the issue: The following function check how many taxes we have per line but this limit is incorrect because it is accepted by the Italian electronic invoicing specifications to have also more than 1 tax. https://github.com/odoo/odoo/blob/bd095fe286930acc54d85bdf7f92af15569f5b82/addons/l10n_it_edi/models/account_move.py#L1268-L1273 ### Reference documentation: 1. [Art. 10 della Legge n. 183_2011, successivamente integrato dal D.L. n. 1_2012 (art. 9-bis)..pdf](https://github.com/user-attachments/files/29056003/Art.10.della.Legge.n.183_2011.successivamente.integrato.dal.D.L.n.1_2012.art.9-bis.pdf) 2. Following image: <img width="823" height="580" alt="estrattoEppi" src="https://github.com/user-attachments/assets/e79be16f-651e-467a-84f4-8400185ceea4" /> opw-6264685 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Helpdesk quick-create forms now only show customers that belong to the relevant company. This prevents users working across multiple companies from accidentally choosing a customer from the wrong one.
Original PR description
Steps to reproduce: - Create two companies (Company A and Company B) - Create one partner in each company - Enable both companies for the user - Open Helpdesk and go to the tickets Kanban view for a Company A team. - In the quick create form, the customer dropdown shows customers from Company B Issue: - Customers from other companies are visible in the customer field, Cause: - The partner_id field in the quick create view had no domain, so it displayed partners from all allowed companies. Solution: - Added a domain on partner_id in the ticket quick create form view. task-4971466
This fix prevents an error when users open the forecasted quantity view for a product template that does not yet have a variant. Instead of showing a traceback, the forecast button is hidden in this case, which keeps the product screen working normally.
Original PR description
This is a partial backport of 9d2bd55f4833171878972e043a831621f98653b0 Issue: --- Not having at least one variant created for a product template with dynamic attributes can cause issues as it's expected a product template to have at least one variant. To reproduce: 1- Create a dynamic attribute with values. 2- Create a product and without saving: - Enable track inventory. - Add the dynamic attributes and values. 3- Save the product. 4- Click on forecasted quantity smart button: - There is a traceback. Cause: --- This is caused because there is no variant created. In the steps, if you save the template once before adding dynamic attributes, a single variant will be created which allows it to work without issue. Fix: --- we can fix the TB by hiding the forecasted qty smart button, when there is no variant. opw-6300837