Wednesday, September 16, 2026
71 changes · master
Resolved issues and error corrections
Users can now open the Attachments menu even when an expense attachment belongs to a company that is not currently active. The fix prevents inaccessible expense records from being read, avoiding an access error in multi-company setups.
Original PR description
**Problem:** When any expense belonging to a given company has an attachment on it, an Access Error will occur when attempting to enter the Attachments Menu when that company is not active. **Cause:** In the `hr_expense` override of `_inaccessible_comodel_records`, all `hr.expense` records are browsed, then have their values read, even if they are not currently accessible. https://github.com/odoo/odoo/blob/f28aa8e7aa0e2c6fb97c841711800410a1da3b72/addons/hr_expense/models/ir_attachment.py#L22-L23 **Purpose:** Use `_filtered_access` to ensure that only currently accessible records are read. **Steps to Reproduce in Runbot:** 1. Add an attachment to an `hr.expense` record in the My Company (San Francisco) Company. 2. Activate a different company and disable My Company (San Francisco), then attempt to open the Attachments menu in the Settings App. opw-6517249 Forward-Port-Of: odoo/odoo#286845
The Point of Sale customer list now loads more smoothly when there are many customers. Menu controls for each customer are only prepared when needed, reducing freezes on slower devices without changing the user experience.
Original PR description
Opening the customer list with a few hundred partners freezes the UI for several seconds on a slow device (~300ms on a desktop, 5.6s with a 6x CPU throttle in the Chrome profiler). Each `PartnerLine`…
Opening the customer list with a few hundred partners freezes the UI for several seconds on a slow device (~300ms on a desktop, 5.6s with a 6x CPU throttle in the Chrome profiler). Each `PartnerLine` instantiates a full `Dropdown` for its "≡" menu. Setting up a `Dropdown` registers about ten lifecycle hooks (`useDropdownNesting`, `useNavigation`, `useDropdownGroup`, `usePopover`, `useEffect`, ...), and each hook registration eagerly allocates two `OwlError` objects to keep a stack trace. Multiplied by hundreds of rows, this dominates the render: ~60% of the click task is spent constructing errors for menus that will never be opened. Render a plain button per row instead and only mount the `Dropdown`, driven by a `useDropdownState`, once that button is clicked. The `Dropdown` opens its popover on mount when its state is already open and is unmounted again when it closes. The placeholder button keeps the classes the `Dropdown` would add to its toggler so the DOM, the styling and the tour selectors (`button.dropdown`) are unchanged. opw-6453848 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#287564 Forward-Port-Of: odoo/odoo#285974
The customer list in POS due settlement now avoids repeated due amount calculations for each row and uses the correct linked commercial partner to find balances. This makes opening large customer lists faster and reduces the risk of showing dues for the wrong customer with a similar name.
Original PR description
Opening the customer list with a few hundred partners is slow; part of the time is spent in `getPartnerCredit`. The `PartnerLine` template reads `this.partnerInfos` eight times per row, and each call runs `getPartnerCredit` again. For a contact, that method resolves the partner carrying the due by scanning every loaded partner for one named like `parent_name`, so the cost grows with the square of the number of partners. Cache the getter in a template variable so it is evaluated once per row, and resolve the due partner through the loaded `commercial_partner_id` instead of the name scan. This is also what the server sums the dues by, and it no longer picks a wrong homonym. `refreshTotalDueOfPartner` used the same lookup and now shares it. opw-6453848 Forward-Port-Of: odoo/enterprise#131079 Forward-Port-Of: odoo/enterprise#130015
Updated website builder test checks so they continue to pass with newer Chrome behavior. This helps keep quality checks stable without changing the website features users see.
Original PR description
In Chrome 152, single-value `background-size` properties may be serialized or expanded to include implicit dimensions (e.g., appending `auto` like `100px auto`), causing strict exact-string test assertions to fail. This commit updates `website` builder test expectations to use regex prefix matching or substring inclusion so tests remain reliable across different Chrome versions. Note: this is a followup of https://github.com/odoo/odoo/pull/285591 where I missed one occurence during forward-port. My bad. runbot-946570 Forward-Port-Of: odoo/odoo#288519
This fix restores the correct spacing around status bars when they appear inside dialog windows. It improves visual consistency and prevents cramped layouts in affected Odoo forms.
Original PR description
| Master | This PR | |--------|--------| | <img width="1016" height="393" alt="image" src="https://github.com/user-attachments/assets/5c60052d-c95b-4351-8ed5-ae122d4db5d6" /> | <img width="1006" height="312" alt="image" src="https://github.com/user-attachments/assets/6b88796f-5eb9-4fb8-b685-b59feed796e9" /> | This PR fixes an oversight from Commit[^1] which omitted the scenario of a statusbar rendered within a dialog. As we relied on the padding utility class for this scenario, we need to set the CSS variable in the dialog context as well. [^1]: https://github.com/odoo/odoo/commit/95e354c75386dc514e8fd0905ab8d348c7fb6ecd task-6578339 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix lets local electronic invoicing rules require a description field when needed. It helps prevent validation errors when generating or submitting electronic documents in countries or formats where that field is mandatory.
Original PR description
In a recent commit, odoo/odoo@c5037bb, we removed the product name from the description field, making the Description node optional for all localisations. This can lead to validation errors when generating or submitting electronic documents. Since individual localisations may enforce their own EDI requirements, this commit adds support for making the Description node mandatory in EDI overrides. See Also: - https://github.com/odoo/enterprise/pull/131081 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Message previews in Odoo Mail now prevent links and mentions from triggering their own actions when clicked. This helps users reliably open the intended conversation or related record from previews without accidental navigation or unwanted interactions.
Original PR description
Prior to this commit, clicking links or mentions in the message preview could trigger their actions instead of opening the related record. This could happen when clicking the preview from the messaging menu, while links and mentions in the chat bubble preview could also be clicked directly. This commit prevents clicking links and mentions from triggering their actions in message previews in both the messaging menu and chat bubbles. The links and mentions keep their visual appearance, but clicking the preview now only opens the related record in messaging menu and does nothing in bubble preview. Task-6569382 https://github.com/user-attachments/assets/8565bfa2-848f-424e-8378-bcacc43dfba8 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fixes an error that occurred when users opened sale or purchase receipts from Invoice Analysis. Businesses using receipts can now review related records from reports without interruption.
Original PR description
When enabling Sale/Purchase Receipts and trying to open the form view from Invoice Analysis, an error occurs. The issue is caused by the `move_type` used in `_where()`, which includes a type that is not defined in the `move_type` field selection. Steps to reproduce: - Enable Sale/Purchase Receipts. - Create a Sale/Purchase Receipt for partner A and confirm it - Go to Invoice Analysis and open the Pivot view - Click on a cell to open partner A's receipt, then try to open the form view - Error Ticket [link](https://www.odoo.com/odoo/project.task/6462153) opw-6462153 Forward-Port-Of: odoo/odoo#288425 Forward-Port-Of: odoo/odoo#282922
Fixed an issue that stopped the Timesheet Assistant from creating a summary when users grouped more than two AI suggestions. The assistant now uses the correct lookup method, so grouped suggestions can be summarized as expected.
Original PR description
Grouping more than two Timesheet Assistant suggestions called `/ai/get_direct_response` with agent_id. That route no longer accepts an agent id: it resolves a composer from interface_key and raises 404 when none is found, so the summary never ran. Register a timesheet_assistant_ai composer and pass that key from the client. task-6570754
This fixes electronic document generation for Colombian and Peruvian localizations by ensuring required description fields are included where local rules demand them. It helps prevent validation or submission errors after a recent change made descriptions optional more broadly.
Original PR description
In a recent commit, https://github.com/odoo/odoo/commit/c5037bb, we removed the product name from the description field, making the Description node optional for all localisations. This can lead to validation errors when generating or submitting electronic documents. Since individual localisations may enforce their own EDI requirements, this commit adds support for making the Description node mandatory in specific EDI overrides. See Also: - https://github.com/odoo/odoo/pull/287566
External attendees who cannot access an unpublished appointment type will now see a warning asking them to contact the organizer instead of hitting an error page when trying to reschedule. Cancellation remains available and now takes attendees to the cancelled event page, while backend-created appointments handle reschedule and cancel actions correctly.
Original PR description
**Purpose:** To reschedule, the attendee is sent to the appointment type page to pick a new slot. That page requires read access on the appointment type, which an external attendee does not have on an unpublished appointment and they get a 403, or a 404 with website_appointment installed. **Specifications:** - An attendee with no access to the appointment type should not be allowed to select slots, so the reschedule button now returns a warning message asking to contact the organizer. - Cancelling is still allowed as it does not need that page, and the cancel button now redirects to the cancelled event page instead. Task-6570481
The change prevents installation failures when using Dutch VAT reporting by making shared VAT payment warning settings available outside the Belgian localization. This helps affected companies install and use the reporting module without duplicate setup logic or broken views.
Original PR description
*: account_reports, l10n_be_reports --- Description of the issue this commit addresses: Since commit 8a2d22f9fdac, the Dutch VAT payment wizard view references a warning field and configuration action defined only by the Belgian localization. Installing l10n_nl_reports therefore fails during view validation. --- Desired behavior after this commit is merged: This commit moves the shared field and action to the generic QR payment wizard, making them available to both localizations without duplication --- task-6578161
The Point of Sale product configurator now only shows extra-price labels when those extras will actually be charged. This prevents cashiers and customers from seeing misleading price add-ons when a fixed pricelist keeps the final product price unchanged.
Original PR description
Steps to reproduce: - Create a pricelist "TAKEAWAY" with a fixed price of 10.00 on a product whose sales price is 20.00 - Create a preset "TAKEOUT" and set its pricelist to "TAKEAWAY" - Add to that…
Steps to reproduce: - Create a pricelist "TAKEAWAY" with a fixed price of 10.00 on a product whose sales price is 20.00 - Create a preset "TAKEOUT" and set its pricelist to "TAKEAWAY" - Add to that product an attribute with variant creation "Never", with a value carrying an extra price of 1.00 - In the POS, switch to the preset "TAKEOUT" and click the product Issue: The configurator advertises the attribute value with a "+ $ 1.00" badge, but that extra is charged nowhere: the title of the popup and the resulting order line both stay at the 10.00 of the pricelist. Cause: A fixed pricelist rule replaces the whole price of the product, the attribute extra prices included: _compute_price on product.pricelist.item returns fixed_price and never reaches _compute_base_price, the only place where _get_attributes_extra_price is taken into account. getPrice is a faithful port of that and overwrites `basePrice + price_extra` with rule.fixed_price. The configurator, however, rendered its badges out of value.price_extra alone, without ever asking what the pricelist of the order does with it. Fix: Only advertise an extra price when the price of the product actually reflects it, the way website_sale already does with the show_extra_price of _get_additionnal_combination_info. Asking getPrice covers more than a fixed rule: a rule based on another pricelist recurses with no extra either, and a full discount leaves nothing of it. Combo items keep their badges, since computeComboItems adds their extras on top of the combo price, like _get_combo_item_display_price does. opw-6528187 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287740 Forward-Port-Of: odoo/odoo#286475
This fixes an unwanted gap between the control panel and the settings form in Odoo Enterprise. The change makes the settings page layout appear more consistent and polished for users.
Original PR description
In Enterprise, the spacing between the control panel and the settings form view is not wanted. COM: https://github.com/odoo/odoo/pull/288107 task-6570898
This update cleans up several manufacturing and inventory screens so key status information is clearer and visual icons are more consistent. It also fixes a display issue where draft manufacturing orders were missing their status indicator, helping users scan work orders and stock operations more reliably.
Original PR description
1) the state of an MO now stays on one line in the work order kanban view 2) the icon of package is changed to package_2 in the smart button and kanban view 3) remaining time and date is hidden on a work order card in the kanban view 4) fixed a bug where draft manufacturing orders didn't have a state bubble in the MO kanban view --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Manufacturing and work order cards now display key status information more consistently and with cleaner visuals. This makes it easier for users to scan production status, identify draft orders, and recognize package-related actions without visual clutter.
Original PR description
1) the state of an MO now stays on one line in the work order kanban view 2) the icon of package is changed to package_2 in the smart button and kanban view 3) remaining time and date is hidden on a work order card in the kanban view 4) fixed a bug where draft manufacturing orders didn't have a state bubble in the MO kanban view
This fixes an issue where portal users could not open chat conversations if they did not have access to the related contact record. The chat view now handles avatars in this case, restoring access to discussions from the portal.
Original PR description
Before this commit, a user was not able to access a chat if not having access to the partner. Steps to reproduce: Log in as 'Admin' and create a chat with a portal user Send a message in the same chat Log in as the portal user > My Account > Discuss go to the 'chat' tab To fix this issue, we follow a similar approach as livechat and generate the avatar in that specific case. task-6455893 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fixes an issue where translated labels for partner additional identifiers were not displayed correctly in views. The change ensures labels are translated before rendering, reducing test failures and preventing confusing untranslated text for users.
Original PR description
Problem: Failing tests revealed that the lazy translated labels of the additional identifiers from the multi id latam refactor were not finding translations in the t-out of the views. Markupsafe is resolving the lazy translation first without an env which resulted in the error of not finding a translation. Solution: Resolve the lazy translation of the labels with an established helper method in Python before it hits Markupsafe. Related PR: https://github.com/odoo/enterprise/pull/117163 runbot-build-error-947021 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The Planning and Sales Planning apps now correctly create a new planning entry from the Plan dialog when users click an empty schedule cell. This prevents an error message and lets teams continue scheduling work without interruption.
Original PR description
Before this commit, when clicking on an empty cell and when the "Plan" dialog was opened, the "Create New" button produced a traceback. This was because the "assign_slot" rpc was called in "props.onSelected" instead of "props.onCreateEdit". Because "onCreateEdit" not overriden in the planning module, it was defaulting to "onSelected". We fix this by overriding "onCreateEdit" in the planning module and calling the "create" method of the model. task-6574764 version: 20.0
Validated time off that is shortened, such as when an employee leaves mid-leave, now keeps the employee calendar in sync instead of removing the related absence entry. This prevents payroll from incorrectly counting those time off days as worked attendance, improving accuracy for final payslips.
Original PR description
Problem ------- Fixes bug caused by PR odoo#249527. When a validated time off's dates are shortened while it stays validated (e.g. when the employee's departure date falls int he middle of the…
Problem ------- Fixes bug caused by PR odoo#249527. When a validated time off's dates are shortened while it stays validated (e.g. when the employee's departure date falls int he middle of the leave), the linked resource.calendar.leaves record was unconditionally unlinked. To reproduce: 1. Create and validate a time off request covering a whole month. 2. Register the employee's departure with a departure date in the middle of that time off. 3. Generate the employee's last payslip. The leave is correctly cut at the departure date, but since it never leaves the `validate` state, it never goes through `_validate_leave_request()` again, so its resource.calendar.leaves record is never recreated. The days that were covered by the deleted entry are no longer blocked in the employee's resource calendar, so the payslip's worked day lines (computed from resource.calendar.leaves) count them as attendance instead of time off. Cause ----- `hr.leave.write()` removed the resource.calendar.leaves record any time either the state changed away from `validate` or the leave's dates changed, regardless of whether the leave remained validated. Date-only changes on an already-validated leave never re-trigger validation, so the entry was not recreated. Solution -------- Only remove the resource.calendar.leaves record when the leave actually loses its validated state. When a validated leave's dates change but it stays validated, amend the existing resource.calendar.leaves record in place instead, falling back to creating one if none exists. Related PR: odoo#249527 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288070 Forward-Port-Of: odoo/odoo#286465
The Spanish VAT Books report was failing to open because it referenced an outdated page element. This update aligns the report with the current interface so users can view the VAT Books report normally again.
Original PR description
The chatter icon in account_reports's line_name.xml was changed from a <button t-if="line.chatter"> to an <i t-if="this.hasChatter()">. l10n_es_reports's VatBooksLineName template still targeted the old button element, so the inherited view could no longer find its xpath target and the VAT Books report failed to render. Update the xpath expression to match the current base template structure. task-6571232
The website sitemap generation now loads only the page data it needs instead of pulling large design history fields into memory. This helps sites with many pages generate sitemaps more reliably and reduces the risk of crashes during indexing-related tasks.
Original PR description
- Before this commit: All fields of ir.ui.view were prefetched, including arch_db and arch_prev. This caused an out-of-memory issue when dealing with many pages. - After this commit: Only required fields are fetched, avoiding unnecessary memory consumption from view architecture data. opw-6470593 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284945
Website tabs using button or tab styles now correctly apply selected theme backgrounds and automatically adjust link colors for readability. This makes designed page sections more consistent with the chosen brand theme and avoids manual color fixes.
Original PR description
*: html_builder, html_editor Steps to reproduce: - Drop a tabs snippet on the website builder - Switch the style to "Buttons" or "Tabs" - Select a theme (color combination) background => It isn't applied. After this commit, the color combination is properly applied. In addition: - When a theme is selected, the links custom color is applied on inactive tabs (the active one takes the color of the theme). - When a solid or gradient background color is selected, the links custom color is applied on all links (including active), but the active button takes a default background with an opacity (white/black depending on the link's color luminance) - When selecting a background, the links color is adapted to constrast with the background. - After designing the "button" or "tabs" styles, if you go back to "underline" or "pills" styles, the colors are reset (because they cannot be adapted from those styles). task-5951656
Fixed an issue where adding a property in worksheet templates could open the setup popover in read-only mode with a broken layout. The system now checks editing permissions at the right time, so users can configure properties as expected without needing to reload the record.
Original PR description
1. Open Planning > Configuration > Worksheet Templates and open a template 2. Click "Add Property" -> the definition popover opens read-only and its layout is broken During the migration to owl3, the `useLayoutEffect` updating `canChangeDefinition` was changed to a `useEffect`. This makes it run during setup rather than after mounting, so it runs while the edit mode is still off, returns early, and never performs the check that would set `canChangeDefinition`. Due to the fact that the condition inside the `useEffect` is only present inside the `untrack`, the effect never subscribes to it, so it is only re-run when the record is reloaded and `canChangeDefinition` remains false in the meantime. With this commit, the `untrack` is dropped so that the effect subscribes to its own condition and the check runs as soon as the field becomes editable. Task-6469849
Company-paid expenses now open the related accounting move correctly even when no supplier bill exists or is expected. The update also removes an obsolete internal option in expense handling, reducing maintenance overhead without changing day-to-day workflows.
Original PR description
## [FIX] hr_expense: Fix existing bill view
Fix an issue where company paid expenses
would refuse to display the move if there was no linked bill, even for cases where there would be no existing bill ever
## [IMP] {hr,sale}_expense: Remove deprecated parameter
Remove the check parameter as it is not used anywwhere anymore
Related: https://github.com/odoo/enterprise/pull/131561The website cookie policy now lists the timezone cookie alongside the existing language preference cookie. This helps ensure visitors receive a more complete explanation of essential cookies used before consent is requested.
Original PR description
The cookie policy page must disclose every cookie stored on a visitor's device, including the essential ones set before any consent is given. Add tz to the existing "Preferences (essential)" row next to frontend_lang, and extend that row's purpose to mention the timezone. task-6470891
This fixes Belgian payroll leave handling so public holidays during long economic unemployment are only employer-paid within the legally allowed first 14 calendar days. It prevents incorrect payment for holidays from day 15 onward and preserves the correct final leave day in payroll calculations.
Original PR description
The employer pays a public holiday during economic unemployment only when it falls in the first 14 calendar days. The window ended one day too late, and any segment crossing it took over every remaining day. A holiday on day 15 was paid, together with the days after it, and the last day of the leave was lost. task-6566666
The chat input request button now uses the standard filled secondary button style instead of a custom border. This keeps the Discuss interface visually consistent with the product theme and improves polish for users.
Original PR description
Drops the inline `border-black` override in favour of proper theme btn styling. task-6528352 | Before | After | |--------|--------| | <img width="392" height="639" alt="image" src="https://github.com/user-attachments/assets/99990e51-b9d9-4b1d-b76a-6ad89422cc18" /> | <img width="390" height="648" alt="image" src="https://github.com/user-attachments/assets/06b64fed-82ca-44cf-b3d8-af7a5a0d6556" /> |
This fixes an internal accounting test so it only checks identifiers that belong to the accounting area. It helps keep automated validation accurate and reduces false failures in development, with no expected change for end users.
Original PR description
The xmlids are defined in `account_reports` runbot-946593
Expense policy limits for job positions are now displayed in the company currency, matching how those limits are configured. This helps avoid confusion when reviewing or applying employee expense rules, especially in multi-currency contexts.
Original PR description
Display job position expense limits using the company currency to match how the limit amount is configure. Also clean up the limit model by moving it to its own file. task-[6445873](https://www.odoo.com/odoo/project/967/tasks/6445873) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The meeting-ready banner is no longer shown in one-to-one direct messages, where inviting others is not applicable. This prevents users from seeing an empty invitation link and keeps the chat experience clearer.
Original PR description
The "Your Meeting is Ready" banner offers to add more people to the call and shows a permanent invitation link to share with them. It is meant for conversations that can grow. In a DM there is nobody else to add, and DMs have no invitation link, so the link input rendered empty. Hide the banner for chat conversations. <img width="2742" height="689" alt="meeting-banner-dm-before-after" src="https://github.com/user-attachments/assets/7eaa527d-8260-4c8c-9425-343cca974d9b" />
Sales users can once again drag and drop request for quotation files onto the Quotations list or kanban views. This fixes a failed upload message and restores the intended quick upload workflow.
Original PR description
Versions -------- - saas-19.4+ Steps ----- 1. Install Sales (with `sale_management`); 2. open Sales > Orders > Quotations; 3. drag a request for quotation file onto the list. Issue ----- The drop…
Versions -------- - saas-19.4+ Steps ----- 1. Install Sales (with `sale_management`); 2. open Sales > Orders > Quotations; 3. drag a request for quotation file onto the list. Issue ----- The drop zone appears, but dropping the file only raises a "Could not upload files" notification. Same in the kanban view. Cause ----- `UploadDropZone.onDrop` hands the dropped files to the closest input matching `.document_file_uploader.o_input_file`. Commit d1742c42 moved the RFQ upload into the cog menu with a template overriding `account.DocumentFileUploader`'s, but without forwarding `fileUploadClass` to the `FileUploader`, so that input never carried the class. This went unnoticed because `saleFileUploadListView` inherited `account.FileuploadListView.Buttons`, which renders `account.DocumentViewUploadButton` and therefore a matching (hidden) input the drop zone could fall back on. Commit 00545bdd patched `saleFileUploadListView` and `saleFileUploadKanbanView` to use the `sale_management` button templates, which inherit `web.ListView.Buttons` instead, removing that input and leaving the page with no drop target at all. Solution -------- Set `fileUploadClass` on the cog menu's `FileUploader`. The drop zone now targets the RFQ uploader itself rather than the account one, so dropped files go through `QuotationRequestUploader.getResModel` as intended. opw-testing-day-v20 Forward-Port-Of: odoo/odoo#288293
The quotation document kanban card layout now has better spacing, making it easier to scan and read. This small visual fix improves the sales quotation builder experience without changing any business workflow.
Original PR description
The kanban card lacks spacing. task-6566640 | Before | After | |--------|--------| | <img width="1307" height="254" alt="image" src="https://github.com/user-attachments/assets/aff159f8-cddc-4b44-b98a-5609c0bf5748" /> | <img width="1363" height="274" alt="image" src="https://github.com/user-attachments/assets/23d135ae-79b0-4287-9d5c-81e44716324c" /> | --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Automation rules that run when a field reaches a target value now avoid running twice during record creation. This prevents duplicate emails, activities, or other automated actions when Odoo computes a default value while creating a record.
Original PR description
An automation rule triggered when a field reaches a specific value can run its actions twice when a record is created without that field provided explicitly. For example, a rule that sends one email…
An automation rule triggered when a field reaches a specific value can run its actions twice when a record is created without that field provided explicitly. For example, a rule that sends one email and schedules two activities can send two emails and schedule four activities for one record. ### Reproduction Steps Create an automation on `crm.lead` that triggers when the stage is set to "New", with one email action and one activity action. Create an opportunity through the website contact form, which leaves the stage unset. The lead is assigned to the "New" stage, but both actions run twice. ### Cause The lead stage is computed and read during creation. That computation can trigger the automation before the creation hook processes the same record, causing both paths to run the actions. The processing code tracks records already handled during nested computations. The feedback flag used for this tracking is attached to the records, but the code checked the automation context instead. When the computation was triggered during creation, the flag was therefore missed. ### Fix Read the feedback flag from the records so nested computations update the shared tracking state. The creation hook then skips records already processed by the computation. opw-6331134 Forward-Port-Of: odoo/odoo#288222
Gantt views now correctly hide unavailable time columns even when bookings or records have no value in the grouped field. This prevents the "Undefined" row from making off-hours appear available, giving users a cleaner and more accurate schedule view.
Original PR description
Folding a column requires it to be unavailable on every row, which means ignoring the "Undefined X" one: no record stands behind it, so the server sends no unavailability for it, which reads as "available at all times" and prevents any folding. That row was spotted as the one without any record, but it gathers the records with no value on the grouped by field: a single booking with no resource in the appointment gantt was enough to bring folding back to nothing. Record-less rows are not placeholders either, as group_expand returns real ones (every resource, every employee) whose unavailabilities do count. It is now spotted through the resId its unavailabilities are read from, which is false for it whether it holds records or not.
This fixes an unwanted extra line that could appear in list views when no footer totals were shown, especially with sample data or empty results. The change makes the interface look cleaner and avoids visual confusion for users reviewing lists such as Contacts.
Original PR description
Since 50b1ceabf1b2, the list view redesign gives the footer cells a top border, and at the same time drops their padding when they are empty, so that a footer without any aggregate takes no space. Both rules contradict each other: the row is squashed to a zero height, but its border keeps being painted. And since the table borders are collapsed, that border is painted by the table itself, so it also ignores the opacity the sample mode applies to the footer. Hence the line, especially visible with sample data where everything else is faded out. Only draw the separator when the footer actually holds something, the way the x2many list footers already do in form views. Steps to reproduce: - go to Contacts - add a favorite filter matching no record - go back to the list view: the sample data is displayed, and a stray line is drawn right under the last row
Instagram posts now allow more time to complete when images are involved. This reduces failed posts caused by timeouts because Instagram needs to download the image from Odoo before publishing.
Original PR description
Bug === On some database on the saas, timeout issue happen for Instagram. Because Instagram downloads the image on our server, it needs more timeout than other social media. Task-6547753 Forward-Port-Of: odoo/enterprise#131632 Forward-Port-Of: odoo/enterprise#131069
Leads created from WhatsApp conversations in Discuss now automatically include the related customer contact. This prevents sales teams from receiving incomplete leads and reduces manual correction after creating CRM opportunities from WhatsApp chats.
Original PR description
### Steps to reproduce: - Install 'whatsapp', and 'crm_livechat' - Configure a WhatsApp channel and receive a message to create a conversation in Discuss - Open the WhatsApp conversation in Discuss -…
### Steps to reproduce: - Install 'whatsapp', and 'crm_livechat' - Configure a WhatsApp channel and receive a message to create a conversation in Discuss - Open the WhatsApp conversation in Discuss - Click on the 'Create Lead' smart button - Check the created lead > The Customer/Contact field is empty ### Cause of Issue: When a lead is created from a Discuss conversation, `_convert_visitor_to_lead` in `crm_livechat` attempts to set the lead's customer. It does this by checking if the channel has `livechat_customer_partner_ids`. However, in whatsapp discuss conversations, the client is saved in `whatsapp_partner_id` and `livechat_customer_partner_ids` is empty. https://github.com/odoo/odoo/blob/b8f3a5c1d2a673fd896d3b8c2968d68e9fcefe19/addons/crm_livechat/models/discuss_channel.py#L56-L58 ### Fix: Kept the fix local to `crm_livechat` and checked whether `whatsapp` is installed before reading `whatsapp_partner_id`, which would otherwise raise an `AttributeError` since `crm_livechat` does not depend on `whatsapp` and vice versa. opw-6371274 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283008
This fixes a Point of Sale issue where removing a coupon reward could prevent the customer list from opening. Staff can continue managing customers and coupon-related loyalty cards without the checkout screen crashing.
Original PR description
Steps to reproduce: - A coupon program with a coupon assigned to a customer - In the PoS, set that customer on the order - Enter the coupon code, its reward line is added - Remove the reward line -…
Steps to reproduce: - A coupon program with a coupon assigned to a customer - In the PoS, set that customer on the order - Enter the coupon code, its reward line is added - Remove the reward line - Click the customer button Issue: The customer list does not open. The PoS crashes with "TypeError: Cannot read properties of undefined (reading 'id')" raised while rendering PartnerLine. Cause: `partnerId2CouponIds` maps a partner to the ids of its `loyalty.card` records. It is filled at boot and on every `loyalty.card` create, but nothing ever removes an id from it: the models only trigger a create event. Removing the reward line of a code activated coupon deletes that card from the local models (`_setValue` in the order summary), so its id stays in the map while the record is gone. `getLoyaltyCards` pushed `models["loyalty.card"].get(id)` unconditionally, hence an `undefined` entry in the list the PartnerLine template iterates over with `t-key="_loyaltyCard.id"`. Fix: Skip the ids whose record no longer exists. The partner keeps its remaining cards, and the path where no card was deleted is unchanged. The stale id is not pruned from the map: it is reached through the reactive store proxy, and mutating it there would notify subscribers in the middle of a render. opw-6517564 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287503 Forward-Port-Of: odoo/odoo#286971
This fix ensures that when a Point of Sale session cannot be closed cleanly because its accounting entry is unbalanced, the user is shown the proper force-close wizard instead of seeing a misleading successful close. This helps prevent missing accounting entries and unposted cash differences.
Original PR description
When the closing journal entry of a session is unbalanced, `_process_session_validation` in point_of_sale rolls back the transaction and returns the "Force Close Session" wizard action, which…
When the closing journal entry of a session is unbalanced, `_process_session_validation` in point_of_sale rolls back the transaction and returns the "Force Close Session" wizard action, which `_validate_session` returns to the user so the difference can be posted on a chosen account. The pos_stock override of `_process_session_validation` calls `super()` without returning its result. The action is dropped, `_validate_session` carries on, and the session is written as `closed` while the rolled-back entry no longer exists: `move_id` is empty, the orders stay in `paid`, `cash_real_transaction` stays at 0 and the cash difference is never posted. The user sees a successful close and nothing in accounting. Steps to reproduce: - Use a stock configuration whose costs yield a rounding difference on the closing entry (e.g. AVCO products with a sale and a return whose unrounded costs round differently when split between the expense and valuation lines). - Close the session from the PoS. - The session is closed, no journal entry exists, and no wizard was shown. Return the result of `super()` so the wizard is shown as in 19.0. opw-6543863 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287618
Point of Sale cash register closing messages now correctly include cash in/out entries made by POS administrators who do not have accounting permissions. This prevents false closing differences from appearing after a register is closed, improving trust in cash control reports.
Original PR description
Steps to reproduce: - Give a user PoS Administrator rights and no Accounting rights. - As that user, open a session with cash control, sell something paid in cash and do a Cash Out. - Close the…
Steps to reproduce: - Give a user PoS Administrator rights and no Accounting rights. - As that user, open a session with cash control, sell something paid in cash and do a Cash Out. - Close the register. The closing popup shows the expected cash (opening + cash payments - cash out); enter exactly that amount, the popup shows no difference. - Open the closed session: its chatter says "Closing difference: -X", X being the cash out amount, and "Closing expected" is the amount before the cash out. Issue: The closing control data agrees with the user, but the closing message records a cash difference equal to the cash out. Cause: `_compute_cash_balance` sums `statement_line_ids` with the rights of the current user. Cash moves are `account.bank.statement.line` records, which `_inherits` `account.move`, so the account.move record rules apply to them too. For a PoS user the only such rule is `rule_invoice_pos_user` (`pos_order_ids != False`), and a cash move has no PoS order: unless the user is in `account.group_account_invoice`, their own cash moves are filtered out and `cash_register_balance_end` ignores them, while `get_closing_control_data` sums the lines in sudo. Since da8be203ec32 PoS managers can create cash moves without any accounting group, which made this visible: in 18.0 cash in/out required `account.group_account_invoice`, whose `account_move_see_all` rule makes every move readable. The values stored at closing are right by accident: `_validate_session` reads the lines in sudo just before, and the one2many cache is shared between the sudo and non-sudo environments of the same transaction. The message posted by `update_closing_control_state_session` runs in its own transaction and gets the filtered sum. Fix: Read the cash lines in sudo in `_compute_cash_balance`, as `get_closing_control_data`, `get_cash_in_out_list` and `_validate_session` already do. Users with accounting rights see all lines already, so nothing changes for them. opw-6521591 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287741 Forward-Port-Of: odoo/odoo#286308
This fixes a Point of Sale issue where selling multiple physical gift cards with the same value could combine them into one order line, preventing staff from entering a unique code for each card. Orders with multiple gift cards should now validate and sync correctly, reducing checkout failures and duplicate-code problems.
Original PR description
Steps to reproduce: - Create a gift card program (several programs sharing the same gift card product show the same issue) - In the PoS, sell a physical gift card: click the gift card product and set…
Steps to reproduce:
- Create a gift card program (several programs sharing the same gift card product show the same issue)
- In the PoS, sell a physical gift card: click the gift card product and set a code through "Sell physical gift card?"
- Click the gift card product again to sell a second physical card of the same value
- Validate the order
Issue:
The second unit is merged into the already coded orderline (one line, qty 2, one code), so the "Sell physical gift card?" link is no longer displayed and the second code cannot be entered. Validating the order then fails with "The operation cannot be completed: A coupon/loyalty card must have a unique code." and the order stays unsynced: the qty 2 line is split into two point entries both carrying the same gift_code, so two loyalty.card records are created with the same code.
Cause:
_setupGiftCardOptions() (and setupEWalletOptions()) pass merge=false so that a gift card line is never merged, and until 17.0 add_product() honored it ("options.merge !== false"). The 18.0 store refactoring dropped it: addLineToOrder() decides merging from a local variable that is only set to false when a price_unit is given in vals, and never reads opts.merge. The option became dead code, in point_of_sale's addLineToOrder as well as for the pos_discount caller.
Fix:
Honor opts.merge === false in addLineToOrder(). Callers that do not pass the option are unaffected, so the default merging behaviour is byte-for-byte the same; only the callers explicitly forbidding a merge (gift card, ewallet and discount lines) get their pre-18.0 behaviour back.
opw-6466324
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#287486
Forward-Port-Of: odoo/odoo#286237Preparation tickets from self-order flows are now sent through the Odoo Box point-of-sale printing setup. This helps kitchen or preparation staff receive the right tickets when customers place orders themselves, reducing missed or delayed order preparation.
Original PR description
Following this commit : ==== - Preparation ticket will be printed through obox in self order task-6255005 Related PR : https://github.com/odoo/odoo/pull/269216
This fix ensures Odoo resets an internal performance counter for the registry itself, not just individual models. It matters for large combined test runs because it prevents Python from disabling a cache that can slow or destabilize extended testing.
Original PR description
reset_classes_tp_versions_used() was only ever fed model classes (registry.values() / self.values()), never the Registry class itself. Since Registry._lock (among others) gets patched on every HttpCase setUpClass/tearDownClass, the shared Registry class silently exhausts CPython 3.13's per-type version-tag budget (1000) over a long test run, permanently disabling its attribute cache, while the periodic reset kept "succeeding" on every other class. This causes issues particularly when testing all the modules in Odoo Community + Enterprise in a single go (not like in runbot)
This update reduces excess spacing around the form view control area and status bar for a cleaner, more balanced layout. It also adjusts the chatter area spacing so the page background and content alignment look more consistent across Odoo editions.
Original PR description
There is too much spacing under the control panel when we are in a form view and the community spacing doesn't work with the background. This commit reviews the spacing of the o_form_statusbar and control_panel, adapted in community and enterprise to a better visual balance. This change requires us to adapt the chatter spacing, removing the margins on the inner element to rely on a padding on the .o-mail-Chatter-topbar instead. task-6531145 Enterprise PR: https://github.com/odoo/enterprise/pull/130825 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The spreadsheet command palette now hides actions that users cannot access from the regular spreadsheet menu. This prevents users from seeing unavailable options and keeps the experience consistent across the interface.
Original PR description
Some items are displayed in the command palette while being hidden in the spreadsheet interface. With this revision, we synchronize the availability of a menu item between the topbar menu and the command palette. Task-6352180 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#273294
The help text for leave type pay rates now explicitly explains that unpaid leave should be configured at 0%. This reduces confusion for HR users when setting up leave policies and helps avoid incorrect payroll-related configuration.
Original PR description
- Update the help tooltip of the pay_rate field on leave types to explicitly mention the 0% configuration for unpaid leaves, preventing user confusion. task-6574198
This update fixes the setup for Latin American check tests so they no longer depend on unnecessary country-specific data. It makes the test suite simpler and more reliable, reducing the risk of false failures during development and quality checks.
Original PR description
Some tests were relying on latam documents or AR master data for no good reason. It only make sense to tests the third party checks with an Argentinean company. Most of the tests should run on a company of any localization. We create two separate setup to reflect that fact, and remove some useless complexity where we can. runbot-947000
This fix ensures Odoo correctly recognizes when a field has no real unsaved change after a user retypes the same value in a different format. It prevents the Save and Discard buttons from remaining stuck on screen when there is nothing left to save, reducing user confusion in forms.
Original PR description
**Description of the issue/feature this PR addresses:** Fixes #287978 In `useInputField` (`input_field_hook.js`), `onChange` and `commitChanges` only trigger the `FIELD_IS_DIRTY` bus event with…
**Description of the issue/feature this PR addresses:** Fixes #287978 In `useInputField` (`input_field_hook.js`), `onChange` and `commitChanges` only trigger the `FIELD_IS_DIRTY` bus event with `false` when the newly typed value actually differs from the value already stored on the record. When the typed text parses to the *same* value as what is already on the record (e.g. retyping `6,250` after the field already holds `6,25`), the code takes the `else` branch, which only resets the displayed text and never notifies the bus that the field is no longer dirty. `form_status_indicator.js` keeps whatever `fieldIsDirty` state it last received from that bus event, and shows the Save/Discard buttons whenever `root.dirty || fieldIsDirty`. Since nothing ever fires `FIELD_IS_DIRTY(false)` in that `else` branch, `fieldIsDirty` stays stuck at `true`. `Discard` correctly resets `root.dirty`, but has no way to reset `fieldIsDirty`, so the Save/Discard buttons keep showing even though the record is clean. **Current behavior before PR:** 1. Edit a field to a new value, click away (value committed, indicator shows Save/Discard as expected). 2. Edit the same field again, this time typing different text that parses to the *same* value (e.g. `1.20` when the field already holds `1.2`), click away. 3. Click **Discard**. The record is reverted, but the Save/Discard buttons remain visible. They stay stuck until the page is reloaded. **Desired behavior after PR is merged:** Retyping different text that parses to the same value clears the field's dirty state like any other case where the field ends up unchanged, so Discard (or any other action) correctly hides the Save/Discard buttons once there is nothing left to save. Forward-Port-Of: odoo/odoo#288030
Customers switching from store pickup to a country-specific delivery option now see the correct delivery charge instead of it remaining free. This prevents misleading checkout totals and ensures eligible delivery methods are priced using the customer's address.
Original PR description
Steps to reproduce: - Install eCommerce and Click & Collect. - Publish Pick up in store with a warehouse whose address is in country A (e.g. United States). - Publish a country-specific delivery…
Steps to reproduce: - Install eCommerce and Click & Collect. - Publish Pick up in store with a warehouse whose address is in country A (e.g. United States). - Publish a country-specific delivery method (Fixed Price, non-zero) limited to country B (e.g. Spain). - On the shop, checkout with a delivery address in country B. - Select Pick up in store, then select the country-specific method. Issue: Selecting the country-specific method keeps Delivery at 0 (Free). The same happens with country-restricted connectors: `/shop/set_delivery_method` never applies the method, so no rate is requested for that switch. Cause: Since saas-19.3, a pickup location replaces `partner_shipping_id` with a generated pickup address. `/shop/set_delivery_method` only calls `_set_delivery_method` if the method is in `_get_delivery_methods()`, which matches on the shipping address. The pickup country makes the country-specific method look unavailable, so it is never applied. The order still has the pickup method (0 delivery), and the summary reports the selected method as Free. Match availability on the customer when shipping is a pickup address, and restore that address before rating the method that is being set. Keep the Based on Rules check so a method with no applicable price rule is not listed after pickup. opw-6493956 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285068
This fix removes leftover component records that could be created when users manually adjust subcontracted production components. It helps prevent later inventory reservation and transfer validation errors caused by incomplete background records.
Original PR description
#### Issue When manually adding component lines in the subcontracting Record Production flow, extra stock move lines can remain in the database without a linked stock move. Those lines have no…
#### Issue When manually adding component lines in the subcontracting Record Production flow, extra stock move lines can remain in the database without a linked stock move. Those lines have no move_id, so their related state is empty. They can later be selected by stock reservation code and cause validation errors when processing the transfer. #### Steps to reproduce 1. Create a subcontracted product. 2. Confirm a subcontracting receipt/purchase flow for that product. 3. Open the subcontracting Record Production popup. 4. Remove an existing component line. 5. Add a new component line for another storable product available at the subcontractor location. 6. Save/record the production. 7. Check stock.move.line records , Inventory > History > Group By Status. An extra stock move line is left with no move_id. #### Root cause The inverse of `mrp.production.move_line_raw_ids` already collects and deletes move lines detached from existing raw moves. However, when the user adds a new component product, the inverse creates a new additional raw move. Creating that raw move can also create reserved move lines. The inverse then replaces `move.move_line_ids` with the user-entered lines, but it did not collect the newly created reserved lines before replacing them. Those reserved lines were detached from the move and left in the database as orphan stock move lines. #### Fix Apply the same cleanup logic to newly created additional raw moves: collect their auto-created move lines before replacing `move_line_ids`, then unlink those detached lines at the end of the inverse. opw-6304649 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281864 Forward-Port-Of: odoo/odoo#276235
Voice transcription requests are now sent in a format compatible with the updated service connection. The client also reads the new response format correctly, helping prevent transcription failures for call recordings and VoIP-related workflows.
Original PR description
Ok got it. 3 separate issues. 1st When we send audio for the transcription we used to send that over http, where we could have raw bytes, now we use json rpc, so we should base64 encode it first, addressed here 2nd When iap, requests the transcription request, and pipes it to the AI provider, it tries to unpack usage from the request. Which in the case of the VTT raw response is not there. Fixed here 3rd is that when odoo-client receives the transcription it tries to unpack the results. It looks for `result` key which got updated to `text` Thought about updating it on the iap side, but I see that realtime already uses it, so maybe let's keep it similar, so just updated it here This pr fixes 1s and 3rd, 2nd fixed on the iap side here https://github.com/odoo/iap-apps/pull/1909
Adjusted spacing around the form view control panel and chatter area to create a cleaner, more balanced layout. This fixes excessive empty space and keeps the interface visually consistent across Enterprise messaging and WhatsApp-related views.
Original PR description
*: mail_enterprise, whatsapp There is too much spacing under the control panel when we are in a form view and the community spacing doesn't work with the background. This commit reviews the spacing of the o_form_statusbar and control_panel, adapted in community and enterprise to a better visual balance. This change requires us to adapt the chatter spacing, removing the margins on the inner element to rely on a padding on the .o-mail-Chatter-topbar instead. task-6531145 Community PR: https://github.com/odoo/odoo/pull/287003
Corrects how fixed-rate taxes are calculated on Mexican electronic payment complements when an invoice is partially paid. This helps prevent payment CFDIs from being rejected by Mexican tax authorities or certification providers due to small tax amount mismatches.
Original PR description
When generating a payment complement, tax base and importe coming from the related invoice are prorated by the percentage actually paid, each rounded independently to the currency precision. The…
When generating a payment complement, tax base and importe coming from the related invoice are prorated by the percentage actually paid, each rounded independently to the currency precision. The post-fix step that restores the SAT invariant uses a Tasa-only formula (`base = total / (1 + rate)`), so Cuota (fixed amount per unit) taxes keep mismatched values, ending up with `ImporteDR != round(BaseDR * TasaOCuotaDR)`. This leads to CFDIs rejected by the PAC/SAT. Steps to reproduce: - Create a customer invoice with a Cuota IEPS tax (e.g. 26.2569). - Register a partial payment whose amount is not an exact divisor of the invoice total (e.g. one third). - Send the payment CFDI: the resulting Cuota TrasladoDR has an ImporteDR that does not match BaseDR * TasaOCuotaDR, leading to a rejected CFDI. This commit recomputes `importe` from the prorated `base` for Cuota taxes (bypassing the Tasa post-fix) opw-6087564 Forward-Port-Of: odoo/enterprise#131436 Forward-Port-Of: odoo/enterprise#113395
Australian payroll users can now register super payments successfully even when they open a super contribution from the Pay Runs kanban view. This prevents an incorrect pay run status from being applied to the payment process, avoiding an error and making the workflow consistent across entry points.
Original PR description
Current behavior: -- Registering a super payment from a pay run opened out of the Pay Runs kanban fails with "Wrong value for account.payment.state. The same super contribution registers without…
Current behavior: -- Registering a super payment from a pay run opened out of the Pay Runs kanban fails with "Wrong value for account.payment.state. The same super contribution registers without error when opened from Payroll > Reporting > Australia > Super Contributions. Expected behavior: -- Register Super Payment should work regardless of how the super contribution record was reached. Steps to reproduce: -- - Take an Australian pay run through to Paid - Open Payroll > Payslips > Pay Runs, kanban grouped by Status (default) - Open the pay run from the Paid column, then Super Submissions - Lock the super contribution and click Register Super Payment Cause of the issue: -- The Pay Runs kanban is grouped by state, so the web client adds default_state to the context of records opened from a column. That context follows the smart button into action_register_super_payment, where account.payment is created without an explicit state. The ORM applies default_state from the context which is a hr.payslip.run value that account.payment.state does not accept. The same leak reaches account.move through action_post, which creates the payment journal entry. Fix: -- apply with_context(clean_context(self.env.context)) to the recordset to remove the default_state key opw-6478084 Forward-Port-Of: odoo/enterprise#128724
The spreadsheet command palette is now consistently available from the top bar across all spreadsheet-related actions, not just document views. This fixes an access gap and makes spreadsheet tools easier to reach in different workflows.
Original PR description
The command palette was made available as a topbar menu item but it was only exposed in the document action. Task-6352180 Forward-Port-Of: odoo/enterprise#122423
Subscription invoices now use the earliest deferred start date across all invoice lines when setting subscription or upsell log effective dates. This prevents later invoice lines from overriding the correct start date, improving accuracy for subscription tracking and reporting.
Original PR description
When posting a subscription invoice, each invoice line overwrites the effective date of the subscription or upsell logs. The last line therefore determines the date, even when another line has an earlier deferred start. Use the earliest deferred start date on the invoice, falling back to the invoice date when no deferred start date is set. Forward-Port-Of: odoo/enterprise#131512
Portal users can now submit product reviews with attached files without hitting a misleading “Not Found” error. The fix ensures the website’s review settings are checked correctly during attachment uploads, so enabled review features work consistently.
Original PR description
A portal user cannot add an attachment when posting a review on a product page: submitting the review with a file attached fails with a generic "Not Found" error, while posting the exact same review…
A portal user cannot add an attachment when posting a review on a product page: submitting the review with a file attached fails with a generic "Not Found" error, while posting the exact same review without an attachment works fine.
Steps to reproduce:
-------------------
* Enable "Discussion and Rating" on a product page (Website > Customize)
* Log in as a portal user and open that product page
* Write a review, attach a file, then submit
> Observation:
"Not Found
The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again."
Why the fix:
------------
`product.template._get_mail_message_access()` demotes a portal user's create access to 'write' whenever
`env['website'].is_view_active('website_sale.product_comment')` is False, since 'write' access is a deliberate way to block posting when the feature is toggled off for the website. `is_view_active` only picks the correct, website-specific view when `website_id` is present in `self.env.context`; that key is injected by the website frontend only for routes declared `website=True`.
`/mail/attachment/upload` is not `website=True` (attachments used to go through the dedicated, `website=True` `/portal/attachment/add` route, removed when portal's attachment uploader was unified with mail's), so `website_id` is missing from context on that route, `is_view_active` falls back to the generic, shipped-`active="False"` view, and always reports the feature as disabled - even when it is actually enabled for the current website. Create access is then wrongly demoted to 'write', which a portal user never has on `product.template`, so the thread lookup fails and the controller raises `NotFound()`.
Resolve the current website from the request itself (`env['website'].get_current_website()`) and inject its id into context before checking `is_view_active`, instead of relying on `website_id` already being in context. This makes the check accurate regardless of which route triggered it.
opw-6539184
Forward-Port-Of: odoo/odoo#288116
Forward-Port-Of: odoo/odoo#287345Product carousels in single-scroll mode now slide correctly on right-to-left language websites such as Arabic. This prevents empty spaces and image distortion, giving visitors a smoother browsing experience.
Original PR description
**Description of the issue/feature this PR addresses:** When a dynamic product carousel is set to single-image scrolling mode in an RTL language (e.g., Arabic), sliding between items behaves…
**Description of the issue/feature this PR addresses:**
When a dynamic product carousel is set to single-image scrolling mode in an RTL language (e.g., Arabic), sliding between items behaves inconsistently, resulting in empty spots or temporarily distorted images.
This occurs because the DOM manipulation creating the infinite scroll was coded for LTR physical movements. In LTR, the first DOM element is visually on the left. In RTL, the visual layout is mirrored, with the first DOM element visually on the right. When we then trigger a "left" slide in RTL, the LTR-based JavaScript doesn't appropriately move the last element to the first index prior to the animation, resulting in an empty gap, among other issues.
This commit resolves the issue by checking the document's text direction. By swapping the target direction ("left" or "right") in onSlideSingleScroll and onSlidSingleScroll when RTL is active, the underlying array operations now correctly align with the animation.
**Steps to reproduce:**
- Website > Edit > add Product Carousel > Customize > change Scrolling Mode to Single
- Website > Edit > Theme > Website > Language > change to Arabic
- Click the arrows in the product carousel > observe inconsistent scrolling with visual glitches
**Current behavior before PR:**
- Visual glitches when sliding carousels in single-image scrolling mode for RTL languages
**Desired behavior after PR is merged:**
- No visual glitches when sliding carousels in single-image scrolling mode for RTL languages
opw-6490277
Forward-Port-Of: odoo/odoo#286042This fix keeps existing guided tours from breaking when they contain an older extra field. It helps ensure previously exported custom and industry tours continue loading normally without requiring manual updates.
Original PR description
Tours exported between 37b35f18 and d2ee747d carry a stray `url` key. `validate()` throws on any unknown key at `registry.addValidation()` time, which aborts asset loading for those pre-existing custom tours. Add a `"*"` wildcard to the schema: unknown keys are ignored, `steps` is still validated. 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#287807 Forward-Port-Of: odoo/odoo#287617
The Peruvian POS electronic invoicing tests now use the correct sales journal when checking receipts. This keeps automated validation aligned with recent accounting changes and helps prevent false test failures.
Original PR description
Since the POS accounting refactor in odoo/odoo#256889 pos.config.journal_id is the journal used to create invoices, so it has to be a sale journal. Before that it was the journal for session entries and invoice payments, which allowed general journals, and invoices went to invoice_journal_id. runbot-error-947223
This fix prevents an error when a check template filters accounts and the filter returns no results. Businesses can now use such templates without unexpected interruptions or tracebacks during accounting report processing.
Original PR description
When a check template has a domain on account.account that would give no result, it would throw a traceback because min cannot operate on an empty result. Forward-Port-Of: odoo/enterprise#131540
Customer invoice numbering now ignores vendor bill numbers, preventing the two document flows from accidentally sharing the same sequence. This helps businesses keep official Dominican electronic invoice numbers accurate and avoids confusion or compliance issues caused by skipped or incorrect numbering.
Original PR description
### Issue before this commit: When users enabled the "Use Documents" option on a Purchase journal and created a vendor bill with a specific document type (e.g., type 31) and number (e.g., E3100056),…
### Issue before this commit: When users enabled the "Use Documents" option on a Purchase journal and created a vendor bill with a specific document type (e.g., type 31) and number (e.g., E3100056), creating a new customer invoice with the same document type would incorrectly continue the sequence from the vendor bill's number (e.g., E3100057). ### Steps to reproduce the issue: 1. Download Accounting and l10n_do_edi 2. Go to invoices and vendor bills and delete all of them 3. Go to Journals and tick Use Documents options for Sales and Purchases journals 4. Create a vendor bill setting the document type as for example (31) Electronic Tax Credit Invoice and the document number as for example E3100056 5. Create an invoice and set the same document type 6. See that the sequence will increase by 1 but starting from the last number setted (so it will be E3100057) when you create a new invoice ### Cause of the issue: https://github.com/odoo/enterprise/blob/d4da04a036486818c12712a200a3482a66921abf/l10n_do_edi/models/account_move.py#L346-L354 The _get_last_sequence_domain override is fetching the last sequence across the company based on the document type, but it did not filter by move_type. Consequently, it was catching vendor bill numbers (in_invoice) to compute the next sequence for customer invoices (out_invoice). ### Reason to introduce the fix: Restrict the sequence lookup to sales documents will ensures that vendor bill references do not interfere with the official sequential numbering of customer invoices. opw-6430906 Forward-Port-Of: odoo/enterprise#128178
The appraisal process no longer requires a final assessment to be completed. This reduces unnecessary blockers for HR teams and managers when an appraisal can be closed or managed without that final text.
Original PR description
Forward-Port-Of: odoo/enterprise#131393
When the website generator cannot find an attachment, the error log now includes the missing file name. This makes troubleshooting faster because support teams no longer have to rely on an attachment ID that may be empty or unhelpful.
Original PR description
When an attachment is not found, we should log the filename as well as often the attachment id is None and tells us nothing. Forward-Port-Of: odoo/enterprise#131370
Large blog imports now process link updates in smaller controlled batches, preventing timeouts during website content generation. This helps ensure imports complete reliably instead of stopping partway through.
Original PR description
This commit fixes an issue where huge blog requests were timing out due to large replacements for links that weren't controlled with our batch creation wrapper. This resulted in incomplete imports. The fix is to put the text replacements in the batch create wrapper which will avoid timeouts and let it continue reliably. Forward-Port-Of: odoo/enterprise#131368
Salespeople now receive the expected upsell warning when multiple timesheet entries together exceed the prepaid service threshold. This helps teams catch upsell opportunities even when customer work is logged incrementally rather than all at once.
Original PR description
Steps to reproduce: -------------------------------------------- 1. Install `sale_timesheet` module 2. Create a product with: * Type: Service * Invoicing Policy: Prepaid/Fixed Price * Create on…
Steps to reproduce:
--------------------------------------------
1. Install `sale_timesheet` module
2. Create a product with:
* Type: Service
* Invoicing Policy: Prepaid/Fixed Price
* Create on Order: Project & Task
* Upsell Threshold: Set some value (e,g. 600%)
3. Create and confirm the sale order with that product
4. Create and confirm an invoice for the sale order
5. From Recorder Hours Smart button:
* Add two timesheets for hours 3 & 4 (Combined h > Threshold Percentage/100)
Observation:
--------------------------------------------
* When adding a single 7h timesheet, an upsell warning activity is correctly created for the salesperson in the chatter.
* When adding multiple incremental timesheets whose combined duration exceeds the threshold, no upsell warning activity is created.
Issue:
--------------------------------------------
* The `_compute_field_value` method filters on `invoice_status != 'upselling'` before recomputing the new invoice status.
* After the first incremental timesheet is added, the sales order already has the upselling status from the previous computation.
* As a result, subsequent computations skip the upsell activity logic, even though `has_displayed_warning_upsell` is still `False` and no warning activity has yet been created.
Solution:
--------------------------------------------
* Removes the `so.invoice_status != 'upselling'` condition from the filter.
* The order-level `invoice_status` was never the right place to control upsell activity creation. An order can have `invoice_status == 'upselling'` for various reasons (delivered > invoiced), but that doesn't mean all lines have already triggered their upsell warnings.
opw-6117754
Forward-Port-Of: odoo/odoo#287642
Forward-Port-Of: odoo/odoo#266275The spreadsheet insertion dialog no longer shows an unwanted horizontal scrollbar or duplicate scrolling on small screens. This makes the insert flow cleaner and easier to use, while keeping spreadsheet template styling separate from selector dialogs.
Original PR description
Opening "Insert in Spreadsheet" displayed an unwanted horizontal scrollbar in the spreadsheet selector. On small viewports, it could also produce a second scrollbar alongside the scrollable modal. The selector reused the `o-spreadsheet-templates-dialog` class, whose styles belong to `documents_spreadsheet`. This applied template-only max-height and overflow rules to the selector and made `spreadsheet_edition` rely on styles from a dependent module. Give selector dialogs their own class and keep the template-specific styles on the template dialog. Move the shared pager layout to `spreadsheet_edition` under a dedicated class, and update both pager consumers and the dashboard document selector. Task: 6526781 Forward-Port-Of: odoo/enterprise#131450 Forward-Port-Of: odoo/enterprise#130114
This update makes marketing campaign webhooks more reliable by better validating incoming data, avoiding unintended record creation, and improving error logging. It also restores and cleans up marketing automation views, helping teams diagnose campaign activity issues more easily after recent refactoring.
Original PR description
Provide various fixes after recent marketing automation refactoring. Notably * improve defensive webhook behavior, sanitize input, improve logging; * various views fixup, notably introducing back a generic activity view for debugging; * fix various small code issues; See commits for more details. Task-6559148 Followup of Task-3866422 (PR odoo/enterprise#113376)
Direct debit mandates now receive a sensible default type when the system cannot find an exact match. This prevents missing selections in cases where only one mandate option exists and the field may be hidden from users.
Original PR description
When no good match is found for the mandate_type, we still want to default on something. In particular when only one concrete implementation of the mandate exists, the field will be hidden since there is only one possible value. task-none
A website test was adjusted so it consistently uses a mock service instead of accidentally calling the real external image API. This reduces false test failures and helps keep the release process stable without changing customer-facing functionality.
Original PR description
The `test_media_dialog_undraw` test monkeypatches `HTML_Editor.media_library_search` to avoid calling the real undraw API during the tour. However, the "routing" ormcache may already hold the controller endpoint built with the original (unpatched) method, if it was resolved by an earlier request served by the same worker. Invalidate the "routing" ormcache after patching so the requests are routed to the patched methods. Same change as done in #271536. https://runbot.odoo.com/odoo/error/939933 Forward-Port-Of: odoo/odoo#288246